ext3: quota_write cross block boundary behaviour

We always assume what dquot update result in changes in one data block
But ext3_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 bug_on triggering. 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: Jan Kara <jack@suse.cz>
This commit is contained in:
Dmitry Monakhov 2010-02-16 19:33:42 +03:00 committed by Jan Kara
parent ac0e773718
commit e5472147e1
1 changed files with 37 additions and 38 deletions

View File

@ -2948,9 +2948,7 @@ static ssize_t ext3_quota_write(struct super_block *sb, int type,
sector_t blk = off >> EXT3_BLOCK_SIZE_BITS(sb); sector_t blk = off >> EXT3_BLOCK_SIZE_BITS(sb);
int err = 0; int err = 0;
int offset = off & (sb->s_blocksize - 1); int offset = off & (sb->s_blocksize - 1);
int tocopy;
int journal_quota = EXT3_SB(sb)->s_qf_names[type] != NULL; int journal_quota = EXT3_SB(sb)->s_qf_names[type] != NULL;
size_t towrite = len;
struct buffer_head *bh; struct buffer_head *bh;
handle_t *handle = journal_current_handle(); handle_t *handle = journal_current_handle();
@ -2961,10 +2959,18 @@ static ssize_t ext3_quota_write(struct super_block *sb, int type,
(unsigned long long)off, (unsigned long long)len); (unsigned long long)off, (unsigned long long)len);
return -EIO; return -EIO;
} }
/*
* Since we account only one data block in transaction credits,
* then it is impossible to cross a block boundary.
*/
if (sb->s_blocksize - offset < len) {
ext3_msg(sb, KERN_WARNING, "Quota write (off=%llu, len=%llu)"
" cancelled because not block aligned",
(unsigned long long)off, (unsigned long long)len);
return -EIO;
}
mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA); mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
while (towrite > 0) {
tocopy = sb->s_blocksize - offset < towrite ?
sb->s_blocksize - offset : towrite;
bh = ext3_bread(handle, inode, blk, 1, &err); bh = ext3_bread(handle, inode, blk, 1, &err);
if (!bh) if (!bh)
goto out; goto out;
@ -2976,7 +2982,7 @@ static ssize_t ext3_quota_write(struct super_block *sb, int type,
} }
} }
lock_buffer(bh); lock_buffer(bh);
memcpy(bh->b_data+offset, data, tocopy); memcpy(bh->b_data+offset, data, len);
flush_dcache_page(bh->b_page); flush_dcache_page(bh->b_page);
unlock_buffer(bh); unlock_buffer(bh);
if (journal_quota) if (journal_quota)
@ -2987,27 +2993,20 @@ static ssize_t ext3_quota_write(struct super_block *sb, int type,
mark_buffer_dirty(bh); mark_buffer_dirty(bh);
} }
brelse(bh); brelse(bh);
if (err)
goto out;
offset = 0;
towrite -= tocopy;
data += tocopy;
blk++;
}
out: out:
if (len == towrite) { if (err) {
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
return err; return err;
} }
if (inode->i_size < off+len-towrite) { if (inode->i_size < off + len) {
i_size_write(inode, off+len-towrite); i_size_write(inode, off + len);
EXT3_I(inode)->i_disksize = inode->i_size; EXT3_I(inode)->i_disksize = inode->i_size;
} }
inode->i_version++; inode->i_version++;
inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_mtime = inode->i_ctime = CURRENT_TIME;
ext3_mark_inode_dirty(handle, inode); ext3_mark_inode_dirty(handle, inode);
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
return len - towrite; return len;
} }
#endif #endif