]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
ext4: fix inline data updates with checksums enabled
authorTheodore Ts'o <tytso@mit.edu>
Tue, 10 Jul 2018 05:07:43 +0000 (01:07 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Tue, 10 Jul 2018 05:07:43 +0000 (01:07 -0400)
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
fs/ext4/inline.c
fs/ext4/inode.c

index e55a8bc870bd1ef0743cf479e0f1aa565eb364c6..3543fe80a3c442364d752fcbb74a7edd4df97dc9 100644 (file)
@@ -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 @@ 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;
@@ -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;
@@ -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;
 }
index 7d6c10017bdf5910a655d9f005a65c5b190f727a..4efe77286ecd55a6a1c79e45e61a93eb3554b901 100644 (file)
@@ -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) {
@@ -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))
@@ -1493,6 +1494,7 @@ 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);
@@ -1500,7 +1502,7 @@ static int ext4_journalled_write_end(struct file *file,
 
        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) {
@@ -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;
@@ -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);