Skip to content

Commit

Permalink
ext4: fix inline data updates with checksums enabled
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Theodore Ts'o committed Jul 10, 2018
1 parent 2dca60d commit 362eca7
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 17 deletions.
19 changes: 11 additions & 8 deletions fs/ext4/inline.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -898,7 +903,6 @@ int ext4_da_write_inline_data_begin(struct address_space *mapping,
goto out;
}


page = grab_cache_page_write_begin(mapping, 0, flags);
if (!page) {
ret = -ENOMEM;
Expand All @@ -916,6 +920,9 @@ int ext4_da_write_inline_data_begin(struct address_space *mapping,
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;
Expand All @@ -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);
Expand All @@ -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);

Expand All @@ -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;
}
Expand Down
16 changes: 7 additions & 9 deletions fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1389,9 +1389,10 @@ static int ext4_write_end(struct file *file,
loff_t old_size = inode->i_size;
int ret = 0, ret2;
int i_size_changed = 0;
int inline_data = ext4_has_inline_data(inode);

trace_ext4_write_end(inode, pos, len, copied);
if (ext4_has_inline_data(inode)) {
if (inline_data) {
ret = ext4_write_inline_data_end(inode, pos, len,
copied, page);
if (ret < 0) {
Expand Down Expand Up @@ -1419,7 +1420,7 @@ static int ext4_write_end(struct file *file,
* ordering of page lock and transaction start for journaling
* filesystems.
*/
if (i_size_changed)
if (i_size_changed || inline_data)
ext4_mark_inode_dirty(handle, inode);

if (pos + len > inode->i_size && ext4_can_truncate(inode))
Expand Down Expand Up @@ -1493,14 +1494,15 @@ static int ext4_journalled_write_end(struct file *file,
int partial = 0;
unsigned from, to;
int size_changed = 0;
int inline_data = ext4_has_inline_data(inode);

trace_ext4_journalled_write_end(inode, pos, len, copied);
from = pos & (PAGE_SIZE - 1);
to = from + len;

BUG_ON(!ext4_handle_valid(handle));

if (ext4_has_inline_data(inode)) {
if (inline_data) {
ret = ext4_write_inline_data_end(inode, pos, len,
copied, page);
if (ret < 0) {
Expand Down Expand Up @@ -1531,7 +1533,7 @@ static int ext4_journalled_write_end(struct file *file,
if (old_size < pos)
pagecache_isize_extended(inode, old_size, pos);

if (size_changed) {
if (size_changed || inline_data) {
ret2 = ext4_mark_inode_dirty(handle, inode);
if (!ret)
ret = ret2;
Expand Down Expand Up @@ -2028,11 +2030,7 @@ static int __ext4_journalled_writepage(struct page *page,
}

if (inline_data) {
BUFFER_TRACE(inode_bh, "get write access");
ret = ext4_journal_get_write_access(handle, inode_bh);

err = ext4_handle_dirty_metadata(handle, inode, inode_bh);

ret = ext4_mark_inode_dirty(handle, inode);
} else {
ret = ext4_walk_page_buffers(handle, page_bufs, 0, len, NULL,
do_journal_get_write_access);
Expand Down

0 comments on commit 362eca7

Please sign in to comment.