ext4: update quota information while swapping boot loader inode

While do swap between two inode, they swap i_data without update
quota information. Also, swap_inode_boot_loader can do "revert"
somtimes, so update the quota while all operations has been finished.

Signed-off-by: yangerkun <yangerkun@huawei.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: stable@kernel.org
This commit is contained in:
yangerkun 2019-02-11 00:14:02 -05:00 committed by Theodore Ts'o
parent a46c68a318
commit aa507b5faf

View file

@ -68,8 +68,6 @@ static void swap_inode_data(struct inode *inode1, struct inode *inode2)
ei2 = EXT4_I(inode2);
swap(inode1->i_version, inode2->i_version);
swap(inode1->i_blocks, inode2->i_blocks);
swap(inode1->i_bytes, inode2->i_bytes);
swap(inode1->i_atime, inode2->i_atime);
swap(inode1->i_mtime, inode2->i_mtime);
@ -115,6 +113,9 @@ static long swap_inode_boot_loader(struct super_block *sb,
int err;
struct inode *inode_bl;
struct ext4_inode_info *ei_bl;
qsize_t size, size_bl, diff;
blkcnt_t blocks;
unsigned short bytes;
inode_bl = ext4_iget(sb, EXT4_BOOT_LOADER_INO, EXT4_IGET_SPECIAL);
if (IS_ERR(inode_bl))
@ -180,6 +181,13 @@ static long swap_inode_boot_loader(struct super_block *sb,
memset(ei_bl->i_data, 0, sizeof(ei_bl->i_data));
}
err = dquot_initialize(inode);
if (err)
goto err_out1;
size = (qsize_t)(inode->i_blocks) * (1 << 9) + inode->i_bytes;
size_bl = (qsize_t)(inode_bl->i_blocks) * (1 << 9) + inode_bl->i_bytes;
diff = size - size_bl;
swap_inode_data(inode, inode_bl);
inode->i_ctime = inode_bl->i_ctime = current_time(inode);
@ -193,24 +201,46 @@ static long swap_inode_boot_loader(struct super_block *sb,
err = ext4_mark_inode_dirty(handle, inode);
if (err < 0) {
/* No need to update quota information. */
ext4_warning(inode->i_sb,
"couldn't mark inode #%lu dirty (err %d)",
inode->i_ino, err);
/* Revert all changes: */
swap_inode_data(inode, inode_bl);
ext4_mark_inode_dirty(handle, inode);
} else {
err = ext4_mark_inode_dirty(handle, inode_bl);
if (err < 0) {
ext4_warning(inode_bl->i_sb,
"couldn't mark inode #%lu dirty (err %d)",
inode_bl->i_ino, err);
/* Revert all changes: */
swap_inode_data(inode, inode_bl);
ext4_mark_inode_dirty(handle, inode);
ext4_mark_inode_dirty(handle, inode_bl);
}
goto err_out1;
}
blocks = inode_bl->i_blocks;
bytes = inode_bl->i_bytes;
inode_bl->i_blocks = inode->i_blocks;
inode_bl->i_bytes = inode->i_bytes;
err = ext4_mark_inode_dirty(handle, inode_bl);
if (err < 0) {
/* No need to update quota information. */
ext4_warning(inode_bl->i_sb,
"couldn't mark inode #%lu dirty (err %d)",
inode_bl->i_ino, err);
goto revert;
}
/* Bootloader inode should not be counted into quota information. */
if (diff > 0)
dquot_free_space(inode, diff);
else
err = dquot_alloc_space(inode, -1 * diff);
if (err < 0) {
revert:
/* Revert all changes: */
inode_bl->i_blocks = blocks;
inode_bl->i_bytes = bytes;
swap_inode_data(inode, inode_bl);
ext4_mark_inode_dirty(handle, inode);
ext4_mark_inode_dirty(handle, inode_bl);
}
err_out1:
ext4_journal_stop(handle);
ext4_double_up_write_data_sem(inode, inode_bl);