bcachefs: Fix short buffered writes
authorKent Overstreet <kent.overstreet@gmail.com>
Thu, 9 Jul 2020 17:54:58 +0000 (13:54 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:08:42 +0000 (17:08 -0400)
In the buffered write path, we have to check for short writes that write
to the full page, where the page wasn't UpToDate; when this happens, the
page is partly garbage, so we have to zero it out and revert that part
of the write.

This check was wrong - we reverted total from copied, but didn't revert
the iov_iter, probably also leading to corrupted writes.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/fs-io.c

index d379581c2517702ef63bf39643d8d4ade8172ae9..c0995723ddd24832e6494d132020279a7f44b313 100644 (file)
@@ -1454,23 +1454,23 @@ retry_reservation:
                if (!pg_copied)
                        break;
 
+               if (!PageUptodate(page) &&
+                   pg_copied != PAGE_SIZE &&
+                   pos + copied + pg_copied < inode->v.i_size) {
+                       zero_user(page, 0, PAGE_SIZE);
+                       break;
+               }
+
                flush_dcache_page(page);
                copied += pg_copied;
+
+               if (pg_copied != pg_len)
+                       break;
        }
 
        if (!copied)
                goto out;
 
-       if (copied < len &&
-           ((offset + copied) & (PAGE_SIZE - 1))) {
-               struct page *page = pages[(offset + copied) >> PAGE_SHIFT];
-
-               if (!PageUptodate(page)) {
-                       zero_user(page, 0, PAGE_SIZE);
-                       copied -= (offset + copied) & (PAGE_SIZE - 1);
-               }
-       }
-
        spin_lock(&inode->v.i_lock);
        if (pos + copied > inode->v.i_size)
                i_size_write(&inode->v, pos + copied);
@@ -1567,6 +1567,7 @@ again:
                }
                pos += ret;
                written += ret;
+               ret = 0;
 
                balance_dirty_pages_ratelimited(mapping);
        } while (iov_iter_count(iter));