aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/super.c
diff options
context:
space:
mode:
authorDmitry Monakhov <dmonakhov@openvz.org>2010-03-02 08:08:51 -0500
committerTheodore Ts'o <tytso@mit.edu>2010-03-02 08:08:51 -0500
commit67eeb5685d2a211c0252ac7884142e503c759500 (patch)
tree48f01e99b3cd52e20d5ac253ddd87e45a0a61af0 /fs/ext4/super.c
parent273df556b6ee2065bfe96edab5888d3dc9b108d8 (diff)
ext4: Fix ext4_quota_write cross block boundary behaviour
We always assume what dquot update result in changes in one data block But ext4_quota_write() function may handle cross block boundary writes In fact if this ever happen it will result in incorrect journal credits reservation, and later a BUG_ON. As soon this never happen the boundary cross loop is NOOP. In order to make things straight let's remove this loop and assert cross boundary condition. Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4/super.c')
-rw-r--r--fs/ext4/super.c69
1 files changed, 34 insertions, 35 deletions
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 5a18e9ec7cf9..ad1ee5f21bab 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -4010,9 +4010,7 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
4010 ext4_lblk_t blk = off >> EXT4_BLOCK_SIZE_BITS(sb); 4010 ext4_lblk_t blk = off >> EXT4_BLOCK_SIZE_BITS(sb);
4011 int err = 0; 4011 int err = 0;
4012 int offset = off & (sb->s_blocksize - 1); 4012 int offset = off & (sb->s_blocksize - 1);
4013 int tocopy;
4014 int journal_quota = EXT4_SB(sb)->s_qf_names[type] != NULL; 4013 int journal_quota = EXT4_SB(sb)->s_qf_names[type] != NULL;
4015 size_t towrite = len;
4016 struct buffer_head *bh; 4014 struct buffer_head *bh;
4017 handle_t *handle = journal_current_handle(); 4015 handle_t *handle = journal_current_handle();
4018 4016
@@ -4022,52 +4020,53 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
4022 (unsigned long long)off, (unsigned long long)len); 4020 (unsigned long long)off, (unsigned long long)len);
4023 return -EIO; 4021 return -EIO;
4024 } 4022 }
4023 /*
4024 * Since we account only one data block in transaction credits,
4025 * then it is impossible to cross a block boundary.
4026 */
4027 if (sb->s_blocksize - offset < len) {
4028 ext4_msg(sb, KERN_WARNING, "Quota write (off=%llu, len=%llu)"
4029 " cancelled because not block aligned",
4030 (unsigned long long)off, (unsigned long long)len);
4031 return -EIO;
4032 }
4033
4025 mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA); 4034 mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
4026 while (towrite > 0) { 4035 bh = ext4_bread(handle, inode, blk, 1, &err);
4027 tocopy = sb->s_blocksize - offset < towrite ? 4036 if (!bh)
4028 sb->s_blocksize - offset : towrite; 4037 goto out;
4029 bh = ext4_bread(handle, inode, blk, 1, &err); 4038 if (journal_quota) {
4030 if (!bh) 4039 err = ext4_journal_get_write_access(handle, bh);
4040 if (err) {
4041 brelse(bh);
4031 goto out; 4042 goto out;
4032 if (journal_quota) {
4033 err = ext4_journal_get_write_access(handle, bh);
4034 if (err) {
4035 brelse(bh);
4036 goto out;
4037 }
4038 } 4043 }
4039 lock_buffer(bh);
4040 memcpy(bh->b_data+offset, data, tocopy);
4041 flush_dcache_page(bh->b_page);
4042 unlock_buffer(bh);
4043 if (journal_quota)
4044 err = ext4_handle_dirty_metadata(handle, NULL, bh);
4045 else {
4046 /* Always do at least ordered writes for quotas */
4047 err = ext4_jbd2_file_inode(handle, inode);
4048 mark_buffer_dirty(bh);
4049 }
4050 brelse(bh);
4051 if (err)
4052 goto out;
4053 offset = 0;
4054 towrite -= tocopy;
4055 data += tocopy;
4056 blk++;
4057 } 4044 }
4045 lock_buffer(bh);
4046 memcpy(bh->b_data+offset, data, len);
4047 flush_dcache_page(bh->b_page);
4048 unlock_buffer(bh);
4049 if (journal_quota)
4050 err = ext4_handle_dirty_metadata(handle, NULL, bh);
4051 else {
4052 /* Always do at least ordered writes for quotas */
4053 err = ext4_jbd2_file_inode(handle, inode);
4054 mark_buffer_dirty(bh);
4055 }
4056 brelse(bh);
4058out: 4057out:
4059 if (len == towrite) { 4058 if (err) {
4060 mutex_unlock(&inode->i_mutex); 4059 mutex_unlock(&inode->i_mutex);
4061 return err; 4060 return err;
4062 } 4061 }
4063 if (inode->i_size < off+len-towrite) { 4062 if (inode->i_size < off + len) {
4064 i_size_write(inode, off+len-towrite); 4063 i_size_write(inode, off + len);
4065 EXT4_I(inode)->i_disksize = inode->i_size; 4064 EXT4_I(inode)->i_disksize = inode->i_size;
4066 } 4065 }
4067 inode->i_mtime = inode->i_ctime = CURRENT_TIME; 4066 inode->i_mtime = inode->i_ctime = CURRENT_TIME;
4068 ext4_mark_inode_dirty(handle, inode); 4067 ext4_mark_inode_dirty(handle, inode);
4069 mutex_unlock(&inode->i_mutex); 4068 mutex_unlock(&inode->i_mutex);
4070 return len - towrite; 4069 return len;
4071} 4070}
4072 4071
4073#endif 4072#endif