[PATCH] fs/ntfs3: reject SEEK_DATA and SEEK_HOLE past EOF early

Konstantin Komarov posted 1 patch 1 week, 5 days ago
fs/ntfs3/file.c    | 14 ++++++++------
fs/ntfs3/frecord.c | 17 +++++++++++++----
2 files changed, 21 insertions(+), 10 deletions(-)
[PATCH] fs/ntfs3: reject SEEK_DATA and SEEK_HOLE past EOF early
Posted by Konstantin Komarov 1 week, 5 days ago
Handle non-data/hole seeks through generic_file_llseek_size() and return
-ENXIO immediately when SEEK_DATA or SEEK_HOLE is requested at or past
EOF. Handle compressed files in such cases properly as well.

Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
---
 fs/ntfs3/file.c    | 14 ++++++++------
 fs/ntfs3/frecord.c | 17 +++++++++++++----
 2 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
index 06a5d9b44ac1..1b52447bd228 100644
--- a/fs/ntfs3/file.c
+++ b/fs/ntfs3/file.c
@@ -1008,7 +1008,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
 		CLST lcn, clen;
 
 		frame = valid >> frame_bits;
-		frame_vbo = valid & ~(frame_size - 1);
+		frame_vbo = valid & ~(u64)(frame_size - 1);
 		off = valid & (frame_size - 1);
 
 		err = attr_data_get_block(ni, frame << NTFS_LZNT_CUNIT, 1, &lcn,
@@ -1077,7 +1077,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
 		if (bytes > count)
 			bytes = count;
 
-		frame_vbo = pos & ~(frame_size - 1);
+		frame_vbo = pos & ~(u64)(frame_size - 1);
 		index = frame_vbo >> PAGE_SHIFT;
 
 		if (unlikely(fault_in_iov_iter_readable(from, bytes))) {
@@ -1530,7 +1530,12 @@ static loff_t ntfs_llseek(struct file *file, loff_t offset, int whence)
 	loff_t maxbytes = ntfs_get_maxbytes(ni);
 	loff_t ret;
 
-	if (whence == SEEK_DATA || whence == SEEK_HOLE) {
+	if (whence != SEEK_DATA && whence != SEEK_HOLE) {
+		ret = generic_file_llseek_size(file, offset, whence, maxbytes,
+					       i_size_read(inode));
+	} else if ((unsigned long long)offset >= i_size_read(inode)) {
+		ret = -ENXIO;
+	} else {
 		inode_lock_shared(inode);
 		/* Scan file for hole or data. */
 		ret = ni_seek_data_or_hole(ni, offset, whence == SEEK_DATA);
@@ -1538,9 +1543,6 @@ static loff_t ntfs_llseek(struct file *file, loff_t offset, int whence)
 
 		if (ret >= 0)
 			ret = vfs_setpos(file, ret, maxbytes);
-	} else {
-		ret = generic_file_llseek_size(file, offset, whence, maxbytes,
-					       i_size_read(inode));
 	}
 	return ret;
 }
diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
index 78eb065c7e43..4b5dd3de327b 100644
--- a/fs/ntfs3/frecord.c
+++ b/fs/ntfs3/frecord.c
@@ -2889,8 +2889,14 @@ loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data)
 			 * the file offset is set to offset.
 			 */
 			if (lcn != SPARSE_LCN) {
-				vbo = (u64)vcn << cluster_bits;
-				return max(vbo, offset);
+				/* Normal cluster. */
+				break;
+			}
+
+			if ((ni->std_fa & FILE_ATTRIBUTE_COMPRESSED) &&
+			    (vcn & (NTFS_LZNT_CLUSTERS - 1))) {
+				/* Compressed cluster in compressed frame. */
+				break;
 			}
 		} else {
 			/*
@@ -2904,8 +2910,8 @@ loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data)
 			    /* native compression hole begins at aligned vcn. */
 			    (!(ni->std_fa & FILE_ATTRIBUTE_COMPRESSED) ||
 			     !(vcn & (NTFS_LZNT_CLUSTERS - 1)))) {
-				vbo = (u64)vcn << cluster_bits;
-				return max(vbo, offset);
+				/* Hole in sparsed or compressed file frame. */
+				break;
 			}
 		}
 
@@ -2914,6 +2920,9 @@ loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data)
 			return -EINVAL;
 		}
 	}
+
+	vbo = (u64)vcn << cluster_bits;
+	return max(vbo, offset);
 }
 
 /*
-- 
2.43.0