[PATCH 02/11] ntfs: support large pages in compressed writes

Namjae Jeon <[email protected]> Tue, 21 Jul 2026 18:54:22 +0900
Newsgroups dev.linux.lists.ntfs
Message-ID <[email protected]>
ntfs_compress_write() derives its page count by shifting the compression
block size and assumes that every compression block begins at a page
boundary. This produces a zero page count for small compression blocks on
large-page systems and ignores an in-page compression block offset.

Map every page covering the compression block, pass the in-page offset to
ntfs_write_cb(), and stage uncompressed output in page-aligned pages.

Signed-off-by: Namjae Jeon <[email protected]>
---
 fs/ntfs/compress.c | 33 ++++++++++++++++++++++-----------
 1 file changed, 22 insertions(+), 11 deletions(-)

diff --git a/fs/ntfs/compress.c b/fs/ntfs/compress.c
index 6b78a8efe3ac..006b8831836c 100644
--- a/fs/ntfs/compress.c
+++ b/fs/ntfs/compress.c
@@ -1258,11 +1258,11 @@ static int ntfs_compress_block(const char *inbuf, const int bufsize,
 }
 
 static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
-		int pages_per_cb)
+		int pages_per_cb, unsigned int page_offset)
 {
 	struct ntfs_volume *vol = ni->vol;
-	char *outbuf = NULL, *pbuf, *inbuf;
-	u32 compsz, p, insz = pages_per_cb << PAGE_SHIFT;
+	char *outbuf = NULL, *pbuf, *inbuf, *in_mapping;
+	u32 compsz, p, insz = ni->itype.compressed.block_size;
 	s32 rounded, bio_size;
 	int sz;
 	unsigned int bsz;
@@ -1284,14 +1284,15 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
 	loff_t new_length;
 	s64 new_vcn;
 
-	inbuf = vmap(pages, pages_per_cb, VM_MAP, PAGE_KERNEL_RO);
-	if (!inbuf)
+	in_mapping = vmap(pages, pages_per_cb, VM_MAP, PAGE_KERNEL_RO);
+	if (!in_mapping)
 		return -ENOMEM;
+	inbuf = in_mapping + page_offset;
 
 	/* may need 2 extra bytes per block and 2 more bytes */
 	pages_disk = kcalloc(pages_count, sizeof(struct page *), GFP_NOFS);
 	if (!pages_disk) {
-		vunmap(inbuf);
+		vunmap(in_mapping);
 		return -ENOMEM;
 	}
 
@@ -1361,7 +1362,9 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
 		err = 0;
 		goto out;
 	} else {
+		memcpy(outbuf, inbuf, insz);
 		bio_size = insz;
+		pages = pages_disk;
 	}
 
 	new_vcn = ntfs_bytes_to_cluster(vol,
@@ -1420,7 +1423,8 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
 					GFP_NOIO);
 			bio->bi_iter.bi_sector =
 				ntfs_bytes_to_sector(vol,
-						ntfs_cluster_to_bytes(vol, bio_lcn + i));
+						ntfs_cluster_to_bytes(vol, bio_lcn) +
+						((s64)i << PAGE_SHIFT));
 		}
 
 		if (!bio_add_page(bio, pages[i], page_size, 0)) {
@@ -1437,7 +1441,8 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
 	err = submit_bio_wait(bio);
 	bio_put(bio);
 out:
-	vunmap(outbuf);
+	if (outbuf)
+		vunmap(outbuf);
 	for (i = 0; i < pages_count; i++) {
 		pg = pages_disk[i];
 		if (pg) {
@@ -1446,7 +1451,7 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
 		}
 	}
 	kfree(pages_disk);
-	vunmap(inbuf);
+	vunmap(in_mapping);
 	NInoSetFileNameDirty(ni);
 	mark_mft_record_dirty(ni);
 
@@ -1458,12 +1463,15 @@ int ntfs_compress_write(struct ntfs_inode *ni, loff_t pos, size_t count,
 {
 	struct folio *folio;
 	struct page **pages = NULL, *page;
-	int pages_per_cb = ni->itype.compressed.block_size >> PAGE_SHIFT;
+	int pages_per_cb;
 	int cb_size = ni->itype.compressed.block_size, cb_off, err = 0;
 	int i, ip;
 	size_t written = 0;
 	struct address_space *mapping = VFS_I(ni)->i_mapping;
 
+	pages_per_cb = DIV_ROUND_UP(offset_in_page(pos & ~(cb_size - 1)) +
+			cb_size, PAGE_SIZE);
+
 	pages = kmalloc_array(pages_per_cb, sizeof(struct page *), GFP_NOFS);
 	if (!pages)
 		return -ENOMEM;
@@ -1471,6 +1479,7 @@ int ntfs_compress_write(struct ntfs_inode *ni, loff_t pos, size_t count,
 	while (count) {
 		pgoff_t index;
 		size_t copied, bytes;
+		unsigned int page_offset;
 		int off;
 
 		off = pos & (cb_size - 1);
@@ -1479,6 +1488,8 @@ int ntfs_compress_write(struct ntfs_inode *ni, loff_t pos, size_t count,
 			bytes = count;
 
 		cb_off = pos & ~(cb_size - 1);
+		page_offset = offset_in_page(cb_off);
+		pages_per_cb = DIV_ROUND_UP(page_offset + cb_size, PAGE_SIZE);
 		index = cb_off >> PAGE_SHIFT;
 
 		if (unlikely(fault_in_iov_iter_readable(from, bytes))) {
@@ -1527,7 +1538,7 @@ int ntfs_compress_write(struct ntfs_inode *ni, loff_t pos, size_t count,
 			}
 		}
 
-		err = ntfs_write_cb(ni, pos, pages, pages_per_cb);
+		err = ntfs_write_cb(ni, pos, pages, pages_per_cb, page_offset);
 
 		for (i = 0; i < pages_per_cb; i++) {
 			folio = page_folio(pages[i]);
-- 
2.34.1