diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c index d99ab086ef6f..a052ba9250e7 100644 --- a/fs/ntfs3/dir.c +++ b/fs/ntfs3/dir.c @@ -489,10 +489,17 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx) goto out; } + /* + * 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; + goto out_unlock; } if (pos >= sbi->record_size) { @@ -503,7 +510,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; + goto out_unlock; bit = 0; } @@ -514,7 +521,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; + goto out_unlock; if (bit == MINUS_ONE_T) { /* no more used indexes. end of dir. */ @@ -524,13 +531,13 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx) if (bit >= max_bit) { /* Corrupted directory. */ err = -EINVAL; - goto out; + goto out_unlock; } err = indx_read_ra(&ni->dir, ni, bit << ni->dir.idx2vbn_bits, &node, &file->f_ra); if (err) - goto out; + goto out_unlock; /* * Add each name from index in 'ctx'. @@ -539,9 +546,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; + goto out_unlock; } +out_unlock: + ni_unlock(ni); + out: kfree(name); put_indx_node(node);