ext4: fix inline data updates with checksums enabled
The inline data code was updating the raw inode directly; this is problematic since if metadata checksums are enabled, ext4_mark_inode_dirty() must be called to update the inode's checksum. In addition, the jbd2 layer requires that get_write_access() be called before the metadata buffer is modified. Fix both of these problems. https://bugzilla.kernel.org/show_bug.cgi?id=200443 Signed-off-by: Theodore Ts'o <tytso@mit.edu> Cc: stable@vger.kernel.org
This commit is contained in:
@@ -682,6 +682,10 @@ int ext4_try_to_write_inline_data(struct address_space *mapping,
|
||||
goto convert;
|
||||
}
|
||||
|
||||
ret = ext4_journal_get_write_access(handle, iloc.bh);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
flags |= AOP_FLAG_NOFS;
|
||||
|
||||
page = grab_cache_page_write_begin(mapping, 0, flags);
|
||||
@@ -710,7 +714,7 @@ int ext4_try_to_write_inline_data(struct address_space *mapping,
|
||||
out_up_read:
|
||||
up_read(&EXT4_I(inode)->xattr_sem);
|
||||
out:
|
||||
if (handle)
|
||||
if (handle && (ret != 1))
|
||||
ext4_journal_stop(handle);
|
||||
brelse(iloc.bh);
|
||||
return ret;
|
||||
@@ -752,6 +756,7 @@ int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
|
||||
|
||||
ext4_write_unlock_xattr(inode, &no_expand);
|
||||
brelse(iloc.bh);
|
||||
mark_inode_dirty(inode);
|
||||
out:
|
||||
return copied;
|
||||
}
|
||||
@@ -898,7 +903,6 @@ retry_journal:
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
page = grab_cache_page_write_begin(mapping, 0, flags);
|
||||
if (!page) {
|
||||
ret = -ENOMEM;
|
||||
@@ -916,6 +920,9 @@ retry_journal:
|
||||
if (ret < 0)
|
||||
goto out_release_page;
|
||||
}
|
||||
ret = ext4_journal_get_write_access(handle, iloc.bh);
|
||||
if (ret)
|
||||
goto out_release_page;
|
||||
|
||||
up_read(&EXT4_I(inode)->xattr_sem);
|
||||
*pagep = page;
|
||||
@@ -936,7 +943,6 @@ int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
|
||||
unsigned len, unsigned copied,
|
||||
struct page *page)
|
||||
{
|
||||
int i_size_changed = 0;
|
||||
int ret;
|
||||
|
||||
ret = ext4_write_inline_data_end(inode, pos, len, copied, page);
|
||||
@@ -954,10 +960,8 @@ int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
|
||||
* But it's important to update i_size while still holding page lock:
|
||||
* page writeout could otherwise come in and zero beyond i_size.
|
||||
*/
|
||||
if (pos+copied > inode->i_size) {
|
||||
if (pos+copied > inode->i_size)
|
||||
i_size_write(inode, pos+copied);
|
||||
i_size_changed = 1;
|
||||
}
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
|
||||
@@ -967,8 +971,7 @@ int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
|
||||
* ordering of page lock and transaction start for journaling
|
||||
* filesystems.
|
||||
*/
|
||||
if (i_size_changed)
|
||||
mark_inode_dirty(inode);
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
Reference in New Issue
Block a user