ntfs: fix incorrect VCN units for index blocks in large clusters

NTFS stores VCNs for index blocks smaller than their cluster in fixed
512-byte units, independent of logical sector size. Use correct block
units when initializing and traversing these indexes so directory indexes
are located correctly.

Verified on a Windows-created 4Kn/32KB-cluster NTFS volume that when the
index block is smaller than the cluster, the VCN unit is 512 bytes.

Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
Reviewed-by: Baolin Liu <liubaolin@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Hyunchul Lee 2026-09-18 09:18:53 +09:00 committed by Namjae Jeon
parent 3581ef4558
commit 15b7bc4067
5 changed files with 12 additions and 13 deletions

View File

@ -852,7 +852,7 @@ int ntfs_index_lookup(const void *key, const u32 key_len, struct ntfs_index_cont
if (ni->vol->cluster_size <= icx->block_size)
icx->vcn_size_bits = ni->vol->cluster_size_bits;
else
icx->vcn_size_bits = ni->vol->sector_size_bits;
icx->vcn_size_bits = NTFS_BLOCK_SIZE_BITS;
icx->cr = ir->collation_rule;
if (!ntfs_is_collation_rule_supported(icx->cr)) {

View File

@ -37,7 +37,7 @@
* (maximum is MAX_PARENT_VCN)
* @ib_dirty: true if the current index block (@ia/@ib) was modified
* @block_size: size of index blocks in bytes (from $INDEX_ROOT or $Boot)
* @vcn_size_bits: log2(cluster size)
* @vcn_size_bits: log2 of the VCN unit size
* @sync_write: true if synchronous writeback is requested for this context
*
* @idx_ni is the index inode this context belongs to.

View File

@ -1021,8 +1021,8 @@ static int ntfs_read_locked_inode(struct inode *vi)
ni->itype.index.vcn_size = vol->cluster_size;
ni->itype.index.vcn_size_bits = vol->cluster_size_bits;
} else {
ni->itype.index.vcn_size = vol->sector_size;
ni->itype.index.vcn_size_bits = vol->sector_size_bits;
ni->itype.index.vcn_size = NTFS_BLOCK_SIZE;
ni->itype.index.vcn_size_bits = NTFS_BLOCK_SIZE_BITS;
}
/* Setup the index allocation attribute, even if not present. */
@ -1612,8 +1612,8 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
ni->itype.index.vcn_size = vol->cluster_size;
ni->itype.index.vcn_size_bits = vol->cluster_size_bits;
} else {
ni->itype.index.vcn_size = vol->sector_size;
ni->itype.index.vcn_size_bits = vol->sector_size_bits;
ni->itype.index.vcn_size = NTFS_BLOCK_SIZE;
ni->itype.index.vcn_size_bits = NTFS_BLOCK_SIZE_BITS;
}
/* Find index allocation attribute. */

View File

@ -1880,8 +1880,8 @@ struct index_header {
* @index_block_size: Size of each index block in bytes
* (in $INDEX_ALLOCATION).
* @clusters_per_index_block:
* Clusters per index block (or log2(bytes)
* if < cluster).
* Clusters per index block, or 512-byte units when
* the index block is smaller than a cluster.
* Power of 2; used for encoding block size.
* @reserved: 3 bytes reserved/alignment (zero).
* @index: Index header for root entries (entries follow
@ -1921,7 +1921,7 @@ struct index_root {
* @lsn: Log sequence number of last modification.
* @index_block_vcn: VCN of this index block.
* Units: clusters if cluster_size <= index_block_size;
* sectors otherwise.
* 512-byte blocks otherwise.
* @index: Index header describing entries in this block.
*
* When creating the index block, we place the update sequence array at this

View File

@ -433,9 +433,8 @@ static struct ntfs_inode *__ntfs_create(const struct mnt_idmap *idmap, struct in
ni->itype.index.vcn_size_bits =
vol->cluster_size_bits;
} else {
ni->itype.index.vcn_size = vol->sector_size;
ni->itype.index.vcn_size_bits =
vol->sector_size_bits;
ni->itype.index.vcn_size = NTFS_BLOCK_SIZE;
ni->itype.index.vcn_size_bits = NTFS_BLOCK_SIZE_BITS;
}
}
@ -569,7 +568,7 @@ static struct ntfs_inode *__ntfs_create(const struct mnt_idmap *idmap, struct in
NTFS_B_TO_CLU(vol, ni->vol->index_record_size);
else
ir->clusters_per_index_block =
ni->vol->index_record_size >> ni->vol->sector_size_bits;
ni->vol->index_record_size >> NTFS_BLOCK_SIZE_BITS;
ir->index.entries_offset = cpu_to_le32(sizeof(struct index_header));
ir->index.index_length = cpu_to_le32(index_len);
ir->index.allocated_size = cpu_to_le32(index_len);