]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ntfs: Do not overwrite uptodate pages
authorMatthew Wilcox (Oracle) <willy@infradead.org>
Fri, 18 Jul 2025 19:53:58 +0000 (20:53 +0100)
committerKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
Fri, 17 Oct 2025 14:45:36 +0000 (16:45 +0200)
When reading a compressed file, we may read several pages in addition to
the one requested.  The current code will overwrite pages in the page
cache with the data from disc which can definitely result in changes
that have been made being lost.

For example if we have four consecutie pages ABCD in the file compressed
into a single extent, on first access, we'll bring in ABCD.  Then we
write to page B.  Memory pressure results in the eviction of ACD.
When we attempt to write to page C, we will overwrite the data in page
B with the data currently on disk.

I haven't investigated the decompression code to check whether it's
OK to overwrite a clean page or whether it might be possible to see
corrupt data.  Out of an abundance of caution, decline to overwrite
uptodate pages, not just dirty pages.

Fixes: 4342306f0f0d (fs/ntfs3: Add file operations and implementation)
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: stable@vger.kernel.org
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
fs/ntfs3/frecord.c

index e1832b66718ff9bff8af60454efcc8fc77d3f27c..e441811855265956577f9eeed00739d76393a0f1 100644 (file)
@@ -2020,6 +2020,29 @@ out:
        return err;
 }
 
+static struct page *ntfs_lock_new_page(struct address_space *mapping,
+               pgoff_t index, gfp_t gfp)
+{
+       struct folio *folio = __filemap_get_folio(mapping, index,
+                       FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
+       struct page *page;
+
+       if (IS_ERR(folio))
+               return ERR_CAST(folio);
+
+       if (!folio_test_uptodate(folio))
+               return folio_file_page(folio, index);
+
+       /* Use a temporary page to avoid data corruption */
+       folio_unlock(folio);
+       folio_put(folio);
+       page = alloc_page(gfp);
+       if (!page)
+               return ERR_PTR(-ENOMEM);
+       __SetPageLocked(page);
+       return page;
+}
+
 /*
  * ni_readpage_cmpr
  *
@@ -2074,9 +2097,9 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio)
                if (i == idx)
                        continue;
 
-               pg = find_or_create_page(mapping, index, gfp_mask);
-               if (!pg) {
-                       err = -ENOMEM;
+               pg = ntfs_lock_new_page(mapping, index, gfp_mask);
+               if (IS_ERR(pg)) {
+                       err = PTR_ERR(pg);
                        goto out1;
                }
                pages[i] = pg;
@@ -2175,13 +2198,13 @@ int ni_decompress_file(struct ntfs_inode *ni)
                for (i = 0; i < pages_per_frame; i++, index++) {
                        struct page *pg;
 
-                       pg = find_or_create_page(mapping, index, gfp_mask);
-                       if (!pg) {
+                       pg = ntfs_lock_new_page(mapping, index, gfp_mask);
+                       if (IS_ERR(pg)) {
                                while (i--) {
                                        unlock_page(pages[i]);
                                        put_page(pages[i]);
                                }
-                               err = -ENOMEM;
+                               err = PTR_ERR(pg);
                                goto out;
                        }
                        pages[i] = pg;