fs/ntfs3/file.c | 9 +++++++++ fs/ntfs3/inode.c | 12 +++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-)
Commit 099ef9ab9203 ("fs/ntfs3: implement iomap-based file operations")
made ntfs_iomap_begin() report a read extent that starts below the valid
size as mapped for its whole length.
However, ntfs_get_block_vbo() used to end such an extent at the block
that holds the valid size, and ntfs_file_read_iter() zeroes only up to
the count it returns. As of now, a direct read that reaches the end of
the file returns the right count, but the rest of the user buffer is
filled from the clusters beyond the valid size. These hold old contents
of the disk: the rest of a large cluster, clusters preallocated for a
file that is being written, or what a truncate has cut off.
For example, on a volume with 64K clusters a 5000-byte file is read with
one O_DIRECT read of 1M.
Before commit 099ef9ab9203 ("fs/ntfs3: implement iomap-based file
operations"), the read returns 5000 and the buffer is not changed past
offset 8192.
After that commit, the read returns 5000 and bytes 8192..65535 of the
buffer hold the contents of a file that was deleted earlier.
This patch will end a read extent at the block that holds the valid
size, so that the rest is reported as unwritten and read as zeros, and
zero the tail of that block in the buffer also past the returned bytes.
Fixes: 099ef9ab9203 ("fs/ntfs3: implement iomap-based file operations")
Cc: stable@vger.kernel.org
Signed-off-by: Hengyu Liang <hengyul@cs.unc.edu>
---
A test program is available on request.
fs/ntfs3/file.c | 9 +++++++++
fs/ntfs3/inode.c | 12 +++++++++++-
2 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
index 95dfe6878860..1a3f1d1e6a28 100644
--- a/fs/ntfs3/file.c
+++ b/fs/ntfs3/file.c
@@ -905,6 +905,15 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
iov_iter_revert(iter, to_zero);
iov_iter_zero(to_zero, iter);
}
+
+ /* The block with 'valid' is read as a whole, zero its tail. */
+ valid = min_t(loff_t, ntfs_up_block(inode->i_sb, valid),
+ vbo + bytes);
+ if (end < valid) {
+ size_t done = iov_iter_zero(valid - end, iter);
+
+ iov_iter_revert(iter, done);
+ }
} else if (vbo < i_size) {
if (end > i_size)
bytes = i_size - vbo;
diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
index e6c96ef88b01..b9a5bf8352e2 100644
--- a/fs/ntfs3/inode.c
+++ b/fs/ntfs3/inode.c
@@ -1056,8 +1056,18 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
iomap->type = IOMAP_MAPPED;
} else if (offset < ni->i_valid) {
iomap->type = IOMAP_MAPPED;
- if (flags & IOMAP_REPORT)
+ if (flags & IOMAP_REPORT) {
iomap->length = ni->i_valid - offset;
+ } else if (!rw && !(flags & IOMAP_ZERO)) {
+ /*
+ * Read. Stop at the end of the block with
+ * 'valid', the rest is reported as unwritten.
+ */
+ loff_t vend = ntfs_up_block(sbi->sb, ni->i_valid);
+
+ if (endbyte > vend)
+ iomap->length = vend - offset;
+ }
} else if (rw || (flags & IOMAP_ZERO)) {
iomap->type = IOMAP_MAPPED;
} else {
--
2.53.0
© 2016 - 2026 Red Hat, Inc.