]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
fs/ntfs3: reject SEEK_DATA and SEEK_HOLE past EOF early
authorKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
Fri, 22 May 2026 13:14:39 +0000 (15:14 +0200)
committerKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
Tue, 2 Jun 2026 15:02:36 +0000 (17:02 +0200)
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
fs/ntfs3/frecord.c

index 06a5d9b44ac1bfc7d01ffcd8acef4651ff67fe5f..1b52447bd2289cfa8fca410735217c9fc431cbf5 100644 (file)
@@ -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;
 }
index 78eb065c7e431b44713f77fc8d74a05ae750b36f..4b5dd3de327ba99a0ae1d93f8670935cfac0e4f9 100644 (file)
@@ -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);
 }
 
 /*