Re: [PATCH 1/3] ntfs: set the attribute list size before reserving space for it

From: liubaolin

Date: Sun Sep 27 2026 - 14:11:49 EST




在 2026/9/27 18:57, Matthias Goergens 写道:
ntfs_attrlist_update_locked() resizes the attribute list and then, for
$MFT, tries to reserve the maximum list size. When that allocation
fails with -ENOSPC it falls back to ntfs_attrlist_repack(), which reads
the list through ntfs_inode_attr_pread(). The read stops at i_size,
but i_size is only updated after the reserve, so when the list has just
grown the repack gets a short read and returns -EIO. Only -ENOSPC from
the reserve is ignored, so the -EIO fails the $MFT extension and with
it the file creation that needed a new mft record.

On a 32 MiB volume with 512-byte clusters whose $MFT has a non-resident
attribute list, 591 of 1500 creations of empty files succeed and the
rest fail with EIO while 742 KiB is still free:

ntfs: (device vda): ntfs_attrlist_update_locked(): Failed to reserve attribute list space
ntfs: Failed add attr entry to attrlist
ntfs: MP update failed
ntfs: (device vda): ntfs_mft_record_alloc(): Failed to extend mft data allocation.

Update i_size as soon as the list has been resized. File creation then
goes on until the volume is full and fails with ENOSPC.

Fixes: b1d732e62a5b ("ntfs: repack $MFT/$ATTRIBUTE LIST")
Signed-off-by: Matthias Goergens <matthias.goergens@xxxxxxxxx>
---
The volume is frag.img from
https://github.com/matthiasgoergens/linux/tree/reproducer/2026-09-26-ntfs-mft-runlist
(mft-bootstrap/frag.img.xz); creating empty files on it until one fails
is enough. Tested with KASAN and lockdep: after the patch the files
written before the volume filled read back intact after a remount, and
ntfsfix and ntfsresize --info find nothing wrong with the image.
---
fs/ntfs/attrlist.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/ntfs/attrlist.c b/fs/ntfs/attrlist.c
index 1bbd2bc62c582..62dfb96e31a3c 100644
--- a/fs/ntfs/attrlist.c
+++ b/fs/ntfs/attrlist.c
@@ -252,6 +252,9 @@ int ntfs_attrlist_update_locked(struct ntfs_inode *base_ni,
return err;
}
+ /* ntfs_attrlist_repack() below reads the list up to i_size. */
+ i_size_write(attr_vi, base_ni->attr_list_size);
+
/*
* Reserve the maximum legal list size while the MFT metadata area is
* still easy to allocate contiguously. This prevents a later list entry
@@ -279,8 +282,6 @@ int ntfs_attrlist_update_locked(struct ntfs_inode *base_ni,
}
}
- i_size_write(attr_vi, base_ni->attr_list_size);
-
if (NInoNonResident(attr_ni) && !NInoAttrListNonResident(base_ni))
NInoSetAttrListNonResident(base_ni);

Looks good to me.

Reviewed-by: Baolin Liu <liubaolin@xxxxxxxxxx>