From: DaeMyung Kang <charsyam@gmail.com>
To: Namjae Jeon <linkinjeon@kernel.org>, Hyunchul Lee <hyc.lee@gmail.com>
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
DaeMyung Kang <charsyam@gmail.com>
Subject: [PATCH v5 2/4] ntfs: grow index root value before reparent header update
Date: Sun, 7 Jun 2026 14:17:21 +0900 [thread overview]
Message-ID: <20260607051723.1499833-3-charsyam@gmail.com> (raw)
In-Reply-To: <20260607051723.1499833-1-charsyam@gmail.com>
ntfs_ir_reparent() moves the resident index root entries into an index
block and leaves a small root stub containing the child VCN. That root
stub can be larger than the existing resident value. For example, an
empty root with value_length 48 has an index area of 32 bytes, while the
large-index root stub needs index_length and allocated_size of 40 bytes.
The current code publishes the larger index.index_length and
index.allocated_size before resizing the resident value. If the resize
returns -ENOSPC, the recovery path can call ntfs_inode_add_attrlist(),
which looks attributes up again while the root header says
allocated_size 40 but the resident value still only provides 32 bytes of
index area. Lookup-time $INDEX_ROOT validation then correctly rejects
that transient layout as corrupt.
This reproduces as a generic/013 failure under qemu. In the failing run,
the transient root had value_len=48, index_size=32, index_length=40, and
allocated_size=40, and ntfsprogs-plus ntfsck reported "Corrupt index
root in MFT record 1177".
When the root stub grows, resize the resident value before publishing the
larger root header. If the resize fails, the old root remains valid for
recovery lookups. Keep the existing header-before-resize ordering for
shrink or same-size cases so the resident value never temporarily
exposes an allocated_size beyond its bounds.
Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
---
fs/ntfs/index.c | 78 ++++++++++++++++++++++++++++++-------------------
1 file changed, 48 insertions(+), 30 deletions(-)
diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c
index 146e011c1a41..ab9a4bc36f0b 100644
--- a/fs/ntfs/index.c
+++ b/fs/ntfs/index.c
@@ -1173,6 +1173,8 @@ static int ntfs_ir_reparent(struct ntfs_index_context *icx)
struct index_entry *ie;
struct index_block *ib = NULL;
s64 new_ib_vcn;
+ u32 index_length;
+ u32 old_value_length;
int ix_root_size;
int ret = 0;
@@ -1220,6 +1222,21 @@ static int ntfs_ir_reparent(struct ntfs_index_context *icx)
goto clear_bmp;
}
+ old_value_length = le32_to_cpu(ctx->attr->data.resident.value_length);
+ index_length = le32_to_cpu(ir->index.entries_offset) +
+ sizeof(struct index_entry_header) + sizeof(s64);
+ ix_root_size = offsetof(struct index_root, index) + index_length;
+ /* Grow the resident value before publishing the larger root header. */
+ if (ix_root_size > old_value_length) {
+ ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, ix_root_size);
+ if (ret)
+ goto resize_failed;
+
+ icx->idx_ni->data_size = ix_root_size;
+ icx->idx_ni->initialized_size = ix_root_size;
+ icx->idx_ni->allocated_size = (ix_root_size + 7) & ~7;
+ }
+
ntfs_ir_nill(ir);
ie = ntfs_ie_get_first(&ir->index);
@@ -1228,48 +1245,49 @@ static int ntfs_ir_reparent(struct ntfs_index_context *icx)
ir->index.flags = LARGE_INDEX;
NInoSetIndexAllocPresent(icx->idx_ni);
- ir->index.index_length = cpu_to_le32(le32_to_cpu(ir->index.entries_offset) +
- le16_to_cpu(ie->length));
+ ir->index.index_length = cpu_to_le32(index_length);
ir->index.allocated_size = ir->index.index_length;
- ix_root_size = sizeof(struct index_root) - sizeof(struct index_header) +
- le32_to_cpu(ir->index.allocated_size);
- ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, ix_root_size);
- if (ret) {
- /*
- * When there is no space to build a non-resident
- * index, we may have to move the root to an extent
- */
- if ((ret == -ENOSPC) && (ctx->al_entry || !ntfs_inode_add_attrlist(icx->idx_ni))) {
+ if (ix_root_size <= old_value_length) {
+ ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, ix_root_size);
+ if (ret)
+ goto resize_failed;
+
+ icx->idx_ni->data_size = ix_root_size;
+ icx->idx_ni->initialized_size = ix_root_size;
+ icx->idx_ni->allocated_size = (ix_root_size + 7) & ~7;
+ }
+ ntfs_ie_set_vcn(ie, new_ib_vcn);
+ goto err_out;
+
+resize_failed:
+ /*
+ * When there is no space to build a non-resident
+ * index, we may have to move the root to an extent
+ */
+ if ((ret == -ENOSPC) && (ctx->al_entry || !ntfs_inode_add_attrlist(icx->idx_ni))) {
+ ntfs_attr_put_search_ctx(ctx);
+ ctx = NULL;
+ ir = ntfs_ir_lookup(icx->idx_ni, icx->name, icx->name_len, &ctx);
+ if (ir && !ntfs_attr_record_move_away(ctx, ix_root_size -
+ le32_to_cpu(ctx->attr->data.resident.value_length))) {
+ if (ntfs_attrlist_update(ctx->base_ntfs_ino ?
+ ctx->base_ntfs_ino : ctx->ntfs_ino))
+ goto clear_bmp;
ntfs_attr_put_search_ctx(ctx);
ctx = NULL;
- ir = ntfs_ir_lookup(icx->idx_ni, icx->name, icx->name_len, &ctx);
- if (ir && !ntfs_attr_record_move_away(ctx, ix_root_size -
- le32_to_cpu(ctx->attr->data.resident.value_length))) {
- if (ntfs_attrlist_update(ctx->base_ntfs_ino ?
- ctx->base_ntfs_ino : ctx->ntfs_ino))
- goto clear_bmp;
- ntfs_attr_put_search_ctx(ctx);
- ctx = NULL;
- goto retry;
- }
+ goto retry;
}
- goto clear_bmp;
- } else {
- icx->idx_ni->data_size = icx->idx_ni->initialized_size = ix_root_size;
- icx->idx_ni->allocated_size = (ix_root_size + 7) & ~7;
}
- ntfs_ie_set_vcn(ie, new_ib_vcn);
-
+clear_bmp:
+ ntfs_ibm_clear(icx, new_ib_vcn);
+ goto err_out;
err_out:
kvfree(ib);
if (ctx)
ntfs_attr_put_search_ctx(ctx);
out:
return ret;
-clear_bmp:
- ntfs_ibm_clear(icx, new_ib_vcn);
- goto err_out;
}
/*
--
2.43.0
next prev parent reply other threads:[~2026-06-07 5:17 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-07 5:17 [PATCH v5 0/4] ntfs: finish index root lookup validation DaeMyung Kang
2026-06-07 5:17 ` [PATCH v5 1/4] ntfs: reject non-resident records for resident-only attributes DaeMyung Kang
2026-06-07 5:17 ` DaeMyung Kang [this message]
2026-06-07 5:17 ` [PATCH v5 3/4] ntfs: update index root allocated size before shrink DaeMyung Kang
2026-06-07 5:17 ` [PATCH v5 4/4] ntfs: validate index root allocated_size on lookup DaeMyung Kang
2026-06-08 5:08 ` Hyunchul Lee
2026-06-08 5:38 ` CharSyam
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260607051723.1499833-3-charsyam@gmail.com \
--to=charsyam@gmail.com \
--cc=hyc.lee@gmail.com \
--cc=linkinjeon@kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®