[PATCH 09/11] fs/ntfs3: take ni_lock() once in ntfs_readdir()

From: Konstantin Komarov

Date: Fri Oct 09 2026 - 13:24:44 EST


ntfs_readdir() took ni_lock() to load subrecords, dropped it, and then
took it again for the index walk. The double-checked pattern around
ni_load_all_mi() and the separate out_unlock label exist only because
of that split.

Take the lock once for the whole function and drop the second
acquisition, the inner re-check and the duplicate error label.

Signed-off-by: Konstantin Komarov <almaz.alexandrovich@xxxxxxxxxxxxxxxxxxxx>
---
fs/ntfs3/dir.c | 38 ++++++++++++++++----------------------
1 file changed, 16 insertions(+), 22 deletions(-)

diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c
index 328c9a29df89..0998c98eb49c 100644
--- a/fs/ntfs3/dir.c
+++ b/fs/ntfs3/dir.c
@@ -519,34 +519,29 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx)
if (!name)
return -ENOMEM;

+ /*
+ * Keep directory metadata stable for the whole walk. Loading subrecords
+ * once is not enough if concurrent writeback can still compact ATTR_LIST
+ * entries and free the record that ntfs_read_hdr() is currently walking.
+ */
+ ni_lock(ni);
+
if (!ni->mi_loaded && ni->attr_list.size) {
/*
* Directory inode is locked for read.
* Load all subrecords to avoid 'write' access to 'ni' during
* directory reading.
*/
- ni_lock(ni);
- if (!ni->mi_loaded && ni->attr_list.size) {
- err = ni_load_all_mi(ni);
- if (!err)
- ni->mi_loaded = true;
- }
- ni_unlock(ni);
+ err = ni_load_all_mi(ni);
if (err)
goto out;
+ ni->mi_loaded = true;
}

- /*
- * Keep directory metadata stable for the whole walk. Loading subrecords
- * once is not enough if concurrent writeback can still compact ATTR_LIST
- * entries and free the record that ntfs_read_hdr() is currently walking.
- */
- ni_lock(ni);
-
root = indx_get_root(&ni->dir, ni, NULL, NULL);
if (!root) {
err = -EINVAL;
- goto out_unlock;
+ goto out;
}

if (pos >= sbi->record_size) {
@@ -557,7 +552,7 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx)
*/
err = ntfs_read_hdr(sbi, ni, &root->ihdr, 0, pos, name, ctx);
if (err)
- goto out_unlock;
+ goto out;
bit = 0;
}

@@ -568,7 +563,7 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx)
/* Get the next used index. */
err = indx_used_bit(&ni->dir, ni, &bit);
if (err)
- goto out_unlock;
+ goto out;

if (bit == MINUS_ONE_T) {
/* no more used indexes. end of dir. */
@@ -578,13 +573,13 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx)
if (bit >= max_bit) {
/* Corrupted directory. */
err = -EINVAL;
- goto out_unlock;
+ goto out;
}

err = indx_read_ra(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
&node, &file->f_ra);
if (err)
- goto out_unlock;
+ goto out;

/*
* Add each name from index in 'ctx'.
@@ -593,13 +588,12 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx)
((u64)bit << index_bits) + sbi->record_size,
pos, name, ctx);
if (err)
- goto out_unlock;
+ goto out;
}

-out_unlock:
+out:
ni_unlock(ni);

-out:
kfree(name);
put_indx_node(node);

--
2.43.0