Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 67eeb568 authored by Dmitry Monakhov's avatar Dmitry Monakhov Committed by Theodore Ts'o
Browse files

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: default avatarDmitry Monakhov <dmonakhov@openvz.org>
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
parent 273df556
Loading
Loading
Loading
Loading
+34 −35
Original line number Diff line number Diff line
@@ -4010,9 +4010,7 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
	ext4_lblk_t blk = off >> EXT4_BLOCK_SIZE_BITS(sb);
	int err = 0;
	int offset = off & (sb->s_blocksize - 1);
	int tocopy;
	int journal_quota = EXT4_SB(sb)->s_qf_names[type] != NULL;
	size_t towrite = len;
	struct buffer_head *bh;
	handle_t *handle = journal_current_handle();

@@ -4022,10 +4020,18 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
			(unsigned long long)off, (unsigned long long)len);
		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) {
		ext4_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);
	while (towrite > 0) {
		tocopy = sb->s_blocksize - offset < towrite ?
				sb->s_blocksize - offset : towrite;
	bh = ext4_bread(handle, inode, blk, 1, &err);
	if (!bh)
		goto out;
@@ -4037,7 +4043,7 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
		}
	}
	lock_buffer(bh);
		memcpy(bh->b_data+offset, data, tocopy);
	memcpy(bh->b_data+offset, data, len);
	flush_dcache_page(bh->b_page);
	unlock_buffer(bh);
	if (journal_quota)
@@ -4048,26 +4054,19 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
		mark_buffer_dirty(bh);
	}
	brelse(bh);
		if (err)
			goto out;
		offset = 0;
		towrite -= tocopy;
		data += tocopy;
		blk++;
	}
out:
	if (len == towrite) {
	if (err) {
		mutex_unlock(&inode->i_mutex);
		return err;
	}
	if (inode->i_size < off+len-towrite) {
		i_size_write(inode, off+len-towrite);
	if (inode->i_size < off + len) {
		i_size_write(inode, off + len);
		EXT4_I(inode)->i_disksize = inode->i_size;
	}
	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
	ext4_mark_inode_dirty(handle, inode);
	mutex_unlock(&inode->i_mutex);
	return len - towrite;
	return len;
}

#endif