[PATCH 2/4] ntfs: centalize $INDEX_ROOT header validation

From: Hyunchul Lee

Date: Thu May 21 2026 - 20:49:03 EST


Add a dedicated helper to perform stricter validation of $INDEX_ROOT and
use it for both directory inodes and named index inodes. This keeps the
root size and header geometry checks consistent across both read paths.

Signed-off-by: Hyunchul Lee <hyc.lee@xxxxxxxxx>
---
fs/ntfs/index.c | 18 ++++++++++++++++++
fs/ntfs/index.h | 3 +++
fs/ntfs/inode.c | 11 ++---------
3 files changed, 23 insertions(+), 9 deletions(-)

diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c
index c203a5ad47b6..91fcaa79f8ac 100644
--- a/fs/ntfs/index.c
+++ b/fs/ntfs/index.c
@@ -539,6 +539,24 @@ int ntfs_index_block_inconsistent(struct ntfs_volume *vol,
return 0;
}

+int ntfs_index_root_inconsistent(struct ntfs_volume *vol,
+ const struct attr_record *a,
+ const struct index_root *ir, u64 inum)
+{
+ u32 value_length = le32_to_cpu(a->data.resident.value_length);
+
+ if (value_length < offsetof(struct index_root, index)) {
+ ntfs_error(vol->sb, "$INDEX_ROOT in inode %llu is too small.",
+ (unsigned long long)inum);
+ return -EIO;
+ }
+
+ return ntfs_index_header_inconsistent(vol, &ir->index,
+ value_length -
+ offsetof(struct index_root, index),
+ inum);
+}
+
static struct index_root *ntfs_ir_lookup(struct ntfs_inode *ni, __le16 *name,
u32 name_len, struct ntfs_attr_search_ctx **ctx)
{
diff --git a/fs/ntfs/index.h b/fs/ntfs/index.h
index 3451ec8a1c4e..cad78568d8b3 100644
--- a/fs/ntfs/index.h
+++ b/fs/ntfs/index.h
@@ -89,6 +89,9 @@ struct ntfs_index_context {
bool sync_write;
};

+int ntfs_index_root_inconsistent(struct ntfs_volume *vol,
+ const struct attr_record *a,
+ const struct index_root *ir, u64 inum);
int ntfs_index_block_inconsistent(struct ntfs_volume *vol,
const struct index_block *ib,
u32 block_size, s64 vcn, u64 inum);
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index 360bebd1ee3f..63ee7acff4fc 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -890,7 +890,6 @@ static int ntfs_read_locked_inode(struct inode *vi)
*/
if (S_ISDIR(vi->i_mode)) {
struct index_root *ir;
- u8 *ir_end, *index_end;

view_index_meta:
/* It is a directory, find index root attribute. */
@@ -940,10 +939,7 @@ static int ntfs_read_locked_inode(struct inode *vi)
}
ir = (struct index_root *)((u8 *)a +
le16_to_cpu(a->data.resident.value_offset));
- ir_end = (u8 *)ir + le32_to_cpu(a->data.resident.value_length);
- index_end = (u8 *)&ir->index +
- le32_to_cpu(ir->index.index_length);
- if (index_end > ir_end) {
+ if (ntfs_index_root_inconsistent(ni->vol, a, ir, ni->mft_no)) {
ntfs_error(vi->i_sb, "Directory index is corrupt.");
goto unm_err_out;
}
@@ -1483,7 +1479,6 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
struct attr_record *a;
struct ntfs_attr_search_ctx *ctx;
struct index_root *ir;
- u8 *ir_end, *index_end;
int err = 0;

ntfs_debug("Entering for i_ino 0x%llx.", ni->mft_no);
@@ -1534,9 +1529,7 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
}

ir = (struct index_root *)((u8 *)a + le16_to_cpu(a->data.resident.value_offset));
- ir_end = (u8 *)ir + le32_to_cpu(a->data.resident.value_length);
- index_end = (u8 *)&ir->index + le32_to_cpu(ir->index.index_length);
- if (index_end > ir_end) {
+ if (ntfs_index_root_inconsistent(vol, a, ir, ni->mft_no)) {
ntfs_error(vi->i_sb, "Index is corrupt.");
goto unm_err_out;
}

--
2.43.0