ntfs: only require compressed size on first extent

The attribute validator required the compressed_size field for
every sparse or compressed extent. Continuation extents omit that field,
so their valid mapping_pairs_offset of 64 was rejected and compressed
reads failed.

Fixes: ffc755828d ("ntfs: Fix min_len for compressed/sparse attributes in ntfs_non_resident_attr_value_is_valid()")
Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Hyunchul Lee 2026-10-06 11:24:12 +09:00 committed by Namjae Jeon
parent a5ec710849
commit 8deea34496
2 changed files with 15 additions and 11 deletions

View File

@ -733,8 +733,12 @@ static bool ntfs_non_resident_attr_value_is_valid(const struct attr_record *a)
min_len = offsetof(struct attr_record, data.non_resident.initialized_size) +
sizeof(a->data.non_resident.initialized_size);
/* Sparse and compressed attributes have the extra compressed_size field */
if (a->flags & (ATTR_IS_SPARSE | ATTR_COMPRESSION_MASK))
/*
* Only the first extent of sparse and compressed attributes has the
* extra compressed_size field.
*/
if ((a->flags & (ATTR_IS_SPARSE | ATTR_COMPRESSION_MASK)) &&
!a->data.non_resident.lowest_vcn)
min_len += sizeof(a->data.non_resident.compressed_size);
if (attr_len < min_len)

View File

@ -1157,6 +1157,11 @@ static int ntfs_read_locked_inode(struct inode *vi)
}
if (a->non_resident) {
NInoSetNonResident(ni);
if (a->data.non_resident.lowest_vcn) {
ntfs_error(vi->i_sb,
"First extent of $DATA attribute has non zero lowest_vcn.");
goto unm_err_out;
}
if (NInoCompressed(ni) || (NInoSparse(ni) && !NInoWofCompressed(ni))) {
if (NInoCompressed(ni) &&
a->data.non_resident.compression_unit != 4) {
@ -1205,11 +1210,6 @@ static int ntfs_read_locked_inode(struct inode *vi)
ni->itype.compressed.size = le64_to_cpu(
a->data.non_resident.compressed_size);
}
if (a->data.non_resident.lowest_vcn) {
ntfs_error(vi->i_sb,
"First extent of $DATA attribute has non zero lowest_vcn.");
goto unm_err_out;
}
if (ntfs_non_resident_sizes_inconsistent(vi, a))
goto unm_err_out;
vi->i_size = ni->data_size = le64_to_cpu(a->data.non_resident.data_size);
@ -1435,6 +1435,10 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
}
} else {
NInoSetNonResident(ni);
if (a->data.non_resident.lowest_vcn) {
ntfs_error(vi->i_sb, "First extent of attribute has non-zero lowest_vcn.");
goto unm_err_out;
}
/*
* Ensure the attribute name is placed before the mapping pairs
* array.
@ -1470,10 +1474,6 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
ni->itype.compressed.size = le64_to_cpu(
a->data.non_resident.compressed_size);
}
if (a->data.non_resident.lowest_vcn) {
ntfs_error(vi->i_sb, "First extent of attribute has non-zero lowest_vcn.");
goto unm_err_out;
}
if (ntfs_non_resident_sizes_inconsistent(vi, a))
goto unm_err_out;
vi->i_size = ni->data_size = le64_to_cpu(a->data.non_resident.data_size);