Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fs/ntfs3: implement llseek SEEK_DATA/SEEK_HOLE by scanning data runs

The generic llseek implementation does not understand ntfs data runs,
sparse regions, or compression semantics, and therefore cannot correctly
locate data or holes in files.

Add a filesystem-specific llseek handler that scans attribute data runs
to find the next data or hole starting at the given offset. Handle
resident attributes, sparse runs, compressed holes, and the implicit hole
at end-of-file.

Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>

+113 -3
+2 -2
fs/ntfs3/attrib.c
··· 940 940 941 941 if (!attr_b->non_res) { 942 942 *lcn = RESIDENT_LCN; 943 - *len = 1; 943 + *len = le32_to_cpu(attr_b->res.data_size); 944 944 goto out; 945 945 } 946 946 ··· 950 950 err = -EINVAL; 951 951 } else { 952 952 *len = 1; 953 - *lcn = SPARSE_LCN; 953 + *lcn = EOF_LCN; 954 954 } 955 955 goto out; 956 956 }
+26 -1
fs/ntfs3/file.c
··· 1474 1474 return ret; 1475 1475 } 1476 1476 1477 + /* 1478 + * ntfs_llseek - file_operations::llseek 1479 + */ 1480 + static loff_t ntfs_llseek(struct file *file, loff_t offset, int whence) 1481 + { 1482 + struct inode *inode = file->f_mapping->host; 1483 + struct ntfs_inode *ni = ntfs_i(inode); 1484 + loff_t maxbytes = ntfs_get_maxbytes(ni); 1485 + loff_t ret; 1486 + 1487 + if (whence == SEEK_DATA || whence == SEEK_HOLE) { 1488 + inode_lock_shared(inode); 1489 + /* Scan fragments for hole or data. */ 1490 + ret = ni_seek_data_or_hole(ni, offset, whence == SEEK_DATA); 1491 + inode_unlock_shared(inode); 1492 + 1493 + if (ret >= 0) 1494 + ret = vfs_setpos(file, ret, maxbytes); 1495 + } else { 1496 + ret = generic_file_llseek_size(file, offset, whence, maxbytes, 1497 + i_size_read(inode)); 1498 + } 1499 + return ret; 1500 + } 1501 + 1477 1502 // clang-format off 1478 1503 const struct inode_operations ntfs_file_inode_operations = { 1479 1504 .getattr = ntfs_getattr, ··· 1510 1485 }; 1511 1486 1512 1487 const struct file_operations ntfs_file_operations = { 1513 - .llseek = generic_file_llseek, 1488 + .llseek = ntfs_llseek, 1514 1489 .read_iter = ntfs_file_read_iter, 1515 1490 .write_iter = ntfs_file_write_iter, 1516 1491 .unlocked_ioctl = ntfs_ioctl,
+76
fs/ntfs3/frecord.c
··· 3002 3002 } 3003 3003 3004 3004 /* 3005 + * ni_seek_data_or_hole 3006 + * 3007 + * Helper function for ntfs_llseek( SEEK_DATA/SEEK_HOLE ) 3008 + */ 3009 + loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data) 3010 + { 3011 + int err; 3012 + u8 cluster_bits = ni->mi.sbi->cluster_bits; 3013 + CLST vcn, lcn, clen; 3014 + loff_t vbo; 3015 + 3016 + /* Enumerate all fragments. */ 3017 + for (vcn = offset >> cluster_bits;; vcn += clen) { 3018 + err = attr_data_get_block(ni, vcn, 1, &lcn, &clen, NULL, false); 3019 + if (err) { 3020 + return err; 3021 + } 3022 + 3023 + if (lcn == RESIDENT_LCN) { 3024 + /* clen - resident size in bytes. clen == ni->vfs_inode.i_size */ 3025 + if (offset >= clen) { 3026 + /* check eof. */ 3027 + return -ENXIO; 3028 + } 3029 + 3030 + if (data) { 3031 + return offset; 3032 + } 3033 + 3034 + return clen; 3035 + } 3036 + 3037 + if (lcn == EOF_LCN) { 3038 + if (data) { 3039 + return -ENXIO; 3040 + } 3041 + 3042 + /* implicit hole at the end of file. */ 3043 + return ni->vfs_inode.i_size; 3044 + } 3045 + 3046 + if (data) { 3047 + /* 3048 + * Adjust the file offset to the next location in the file greater than 3049 + * or equal to offset containing data. If offset points to data, then 3050 + * the file offset is set to offset. 3051 + */ 3052 + if (lcn != SPARSE_LCN) { 3053 + vbo = (u64)vcn << cluster_bits; 3054 + return max(vbo, offset); 3055 + } 3056 + } else { 3057 + /* 3058 + * Adjust the file offset to the next hole in the file greater than or 3059 + * equal to offset. If offset points into the middle of a hole, then the 3060 + * file offset is set to offset. If there is no hole past offset, then the 3061 + * file offset is adjusted to the end of the file 3062 + * (i.e., there is an implicit hole at the end of any file). 3063 + */ 3064 + if (lcn == SPARSE_LCN && 3065 + /* native compression hole begins at aligned vcn. */ 3066 + (!(ni->std_fa & FILE_ATTRIBUTE_COMPRESSED) || 3067 + !(vcn & (NTFS_LZNT_CLUSTERS - 1)))) { 3068 + vbo = (u64)vcn << cluster_bits; 3069 + return max(vbo, offset); 3070 + } 3071 + } 3072 + 3073 + if (!clen) { 3074 + /* Corrupted file. */ 3075 + return -EINVAL; 3076 + } 3077 + } 3078 + } 3079 + 3080 + /* 3005 3081 * ni_write_parents 3006 3082 * 3007 3083 * Helper function for ntfs_file_fsync.
+1
fs/ntfs3/ntfs.h
··· 81 81 #define SPARSE_LCN ((CLST)-1) 82 82 #define RESIDENT_LCN ((CLST)-2) 83 83 #define COMPRESSED_LCN ((CLST)-3) 84 + #define EOF_LCN ((CLST)-4) 84 85 85 86 enum RECORD_NUM { 86 87 MFT_REC_MFT = 0,
+8
fs/ntfs3/ntfs_fs.h
··· 591 591 struct NTFS_DE *new_de); 592 592 593 593 bool ni_is_dirty(struct inode *inode); 594 + loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data); 594 595 int ni_write_parents(struct ntfs_inode *ni, int sync); 595 596 596 597 /* Globals from fslog.c */ ··· 1106 1105 static inline int is_resident(struct ntfs_inode *ni) 1107 1106 { 1108 1107 return ni->ni_flags & NI_FLAG_RESIDENT; 1108 + } 1109 + 1110 + static inline loff_t ntfs_get_maxbytes(struct ntfs_inode *ni) 1111 + { 1112 + struct ntfs_sb_info *sbi = ni->mi.sbi; 1113 + return is_sparsed(ni) || is_compressed(ni) ? sbi->maxbytes_sparse : 1114 + sbi->maxbytes; 1109 1115 } 1110 1116 1111 1117 static inline void le16_sub_cpu(__le16 *var, u16 val)