ext4: fix inline data error paths
authorTheodore Ts'o <tytso@mit.edu>
Sun, 5 Feb 2017 04:04:00 +0000 (23:04 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 12 Mar 2017 05:41:46 +0000 (06:41 +0100)
commit eb5efbcb762aee4b454b04f7115f73ccbcf8f0ef upstream.

The write_end() function must always unlock the page and drop its ref
count, even on an error.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/ext4/inline.c
fs/ext4/inode.c

index 0283297..37b521e 100644 (file)
@@ -933,8 +933,15 @@ int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
                                  struct page *page)
 {
        int i_size_changed = 0;
+       int ret;
 
-       copied = ext4_write_inline_data_end(inode, pos, len, copied, page);
+       ret = ext4_write_inline_data_end(inode, pos, len, copied, page);
+       if (ret < 0) {
+               unlock_page(page);
+               put_page(page);
+               return ret;
+       }
+       copied = ret;
 
        /*
         * No need to use i_size_read() here, the i_size
index a48b17c..1d4f5fa 100644 (file)
@@ -1324,8 +1324,11 @@ static int ext4_write_end(struct file *file,
        if (ext4_has_inline_data(inode)) {
                ret = ext4_write_inline_data_end(inode, pos, len,
                                                 copied, page);
-               if (ret < 0)
+               if (ret < 0) {
+                       unlock_page(page);
+                       put_page(page);
                        goto errout;
+               }
                copied = ret;
        } else
                copied = block_write_end(file, mapping, pos,
@@ -1427,10 +1430,16 @@ static int ext4_journalled_write_end(struct file *file,
 
        BUG_ON(!ext4_handle_valid(handle));
 
-       if (ext4_has_inline_data(inode))
-               copied = ext4_write_inline_data_end(inode, pos, len,
-                                                   copied, page);
-       else if (unlikely(copied < len) && !PageUptodate(page)) {
+       if (ext4_has_inline_data(inode)) {
+               ret = ext4_write_inline_data_end(inode, pos, len,
+                                                copied, page);
+               if (ret < 0) {
+                       unlock_page(page);
+                       put_page(page);
+                       goto errout;
+               }
+               copied = ret;
+       } else if (unlikely(copied < len) && !PageUptodate(page)) {
                copied = 0;
                ext4_journalled_zero_new_buffers(handle, page, from, to);
        } else {
@@ -1465,6 +1474,7 @@ static int ext4_journalled_write_end(struct file *file,
                 */
                ext4_orphan_add(handle, inode);
 
+errout:
        ret2 = ext4_journal_stop(handle);
        if (!ret)
                ret = ret2;