aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
authorKonstantin Komarov <almaz.alexandrovich@paragon-software.com>2026-05-22 15:14:39 +0200
committerKonstantin Komarov <almaz.alexandrovich@paragon-software.com>2026-05-28 15:42:16 +0200
commit9433301146e233650d37f14231033335e7c001c1 (patch)
treed4b53a076d19a4e4c85df63c56de349ca6fe7146 /fs
parent6de1a76f241ed49ca0cd3dad8f0ca046938b7427 (diff)
downloadlinux-next-history-9433301146e233650d37f14231033335e7c001c1.tar.gz
fs/ntfs3: reject SEEK_DATA and SEEK_HOLE past EOF early
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>
Diffstat (limited to 'fs')
-rw-r--r--fs/ntfs3/file.c14
-rw-r--r--fs/ntfs3/frecord.c17
2 files changed, 21 insertions, 10 deletions
diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
index 06a5d9b44ac1b..1b52447bd2289 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 78eb065c7e431..4b5dd3de327ba 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);
}
/*