mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
hfs: rework hfsplus_readdir() logic
The xfstests' test-case generic/637 fails with error: FSTYP -- hfs PLATFORM -- Linux/x86_64 kvm-xfstests 6.15.0-rc4-xfstests-g00b827f0cffa #1 SMP PREEMPT_DYNAMIC Fri May 25 MKFS_OPTIONS -- /dev/vdc MOUNT_OPTIONS -- /dev/vdc /vdc QA output created by 637 entries 7 and 8 have duplicate d_off 8 Found unlinked files in open dir (see xfstests-dev/results//generic/637.full for details) Likewise HFS+, currently, HFS has very complicated and fragile logic of rd->file->f_pos correction in hfs_delete_cat(). This patch removes this logic and it stores the current pos into hfs_readdir_data. Finally, if rd->pos == ctx->pos then hfs_readdir() tries to find the position in b-tree's node by means of hfs_cat_key. This position is used to re-start the folder's content traversal. sudo ./check generic/637 FSTYP -- hfs PLATFORM -- Linux/x86_64 hfsplus-testing-0001 7.1.0-rc1+ #55 SMP PREEMPT_DYNAMIC Tue May 19 15:18:02 PDT 2026 MKFS_OPTIONS -- /dev/loop51 MOUNT_OPTIONS -- /dev/loop51 /mnt/scratch generic/637 32s ... 31s Ran: generic/637 Passed all 1 tests Closes: https://github.com/hfs-linux-kernel/hfs-linux-kernel/issues/65 cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> cc: Yangtao Li <frank.li@vivo.com> cc: linux-fsdevel@vger.kernel.org Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com> Link: https://lore.kernel.org/r/20260519222811.1311071-2-slava@dubeyko.com Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
This commit is contained in:
@@ -340,7 +340,6 @@ int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str)
|
||||
{
|
||||
struct super_block *sb;
|
||||
struct hfs_find_data fd;
|
||||
struct hfs_readdir_data *rd;
|
||||
int res, type;
|
||||
|
||||
hfs_dbg("name %s, cnid %u\n", str ? str->name : NULL, cnid);
|
||||
@@ -366,14 +365,6 @@ int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str)
|
||||
}
|
||||
}
|
||||
|
||||
/* we only need to take spinlock for exclusion with ->release() */
|
||||
spin_lock(&HFS_I(dir)->open_dir_lock);
|
||||
list_for_each_entry(rd, &HFS_I(dir)->open_dir_list, list) {
|
||||
if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
|
||||
rd->file->f_pos--;
|
||||
}
|
||||
spin_unlock(&HFS_I(dir)->open_dir_lock);
|
||||
|
||||
res = hfs_brec_remove(&fd);
|
||||
if (res)
|
||||
goto out;
|
||||
|
||||
28
fs/hfs/dir.c
28
fs/hfs/dir.c
@@ -97,7 +97,15 @@ static int hfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
}
|
||||
if (ctx->pos >= inode->i_size)
|
||||
goto out;
|
||||
err = hfs_brec_goto(&fd, ctx->pos - 1);
|
||||
rd = file->private_data;
|
||||
if (rd && rd->pos == ctx->pos) {
|
||||
memcpy(fd.search_key, &rd->key, sizeof(struct hfs_cat_key));
|
||||
err = hfs_brec_find(&fd);
|
||||
if (err == -ENOENT)
|
||||
err = hfs_brec_goto(&fd, 1);
|
||||
} else {
|
||||
err = hfs_brec_goto(&fd, ctx->pos - 1);
|
||||
}
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@@ -146,7 +154,6 @@ static int hfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
rd = file->private_data;
|
||||
if (!rd) {
|
||||
rd = kmalloc_obj(struct hfs_readdir_data);
|
||||
if (!rd) {
|
||||
@@ -154,15 +161,8 @@ static int hfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
goto out;
|
||||
}
|
||||
file->private_data = rd;
|
||||
rd->file = file;
|
||||
spin_lock(&HFS_I(inode)->open_dir_lock);
|
||||
list_add(&rd->list, &HFS_I(inode)->open_dir_list);
|
||||
spin_unlock(&HFS_I(inode)->open_dir_lock);
|
||||
}
|
||||
/*
|
||||
* Can be done after the list insertion; exclusion with
|
||||
* hfs_delete_cat() is provided by directory lock.
|
||||
*/
|
||||
rd->pos = ctx->pos;
|
||||
memcpy(&rd->key, &fd.key->cat, sizeof(struct hfs_cat_key));
|
||||
out:
|
||||
hfs_find_exit(&fd);
|
||||
@@ -171,13 +171,7 @@ static int hfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
|
||||
static int hfs_dir_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct hfs_readdir_data *rd = file->private_data;
|
||||
if (rd) {
|
||||
spin_lock(&HFS_I(inode)->open_dir_lock);
|
||||
list_del(&rd->list);
|
||||
spin_unlock(&HFS_I(inode)->open_dir_lock);
|
||||
kfree(rd);
|
||||
}
|
||||
kfree(file->private_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
/*======== Data structures kept in memory ========*/
|
||||
|
||||
struct hfs_readdir_data {
|
||||
struct list_head list;
|
||||
struct file *file;
|
||||
loff_t pos;
|
||||
struct hfs_cat_key key;
|
||||
};
|
||||
|
||||
|
||||
@@ -36,8 +36,6 @@ struct hfs_inode_info {
|
||||
|
||||
struct hfs_cat_key cat_key;
|
||||
|
||||
struct list_head open_dir_list;
|
||||
spinlock_t open_dir_lock;
|
||||
struct inode *rsrc_inode;
|
||||
|
||||
struct mutex extents_lock;
|
||||
|
||||
@@ -195,8 +195,6 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t
|
||||
err = -ERANGE;
|
||||
|
||||
mutex_init(&HFS_I(inode)->extents_lock);
|
||||
INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list);
|
||||
spin_lock_init(&HFS_I(inode)->open_dir_lock);
|
||||
hfs_cat_build_key(sb, (btree_key *)&HFS_I(inode)->cat_key, dir->i_ino, name);
|
||||
next_id = atomic64_inc_return(&HFS_SB(sb)->next_id);
|
||||
if (next_id > U32_MAX) {
|
||||
@@ -353,8 +351,6 @@ static int hfs_read_inode(struct inode *inode, void *data)
|
||||
HFS_I(inode)->flags = 0;
|
||||
HFS_I(inode)->rsrc_inode = NULL;
|
||||
mutex_init(&HFS_I(inode)->extents_lock);
|
||||
INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list);
|
||||
spin_lock_init(&HFS_I(inode)->open_dir_lock);
|
||||
|
||||
/* Initialize the inode */
|
||||
inode->i_uid = hsb->s_uid;
|
||||
|
||||
Reference in New Issue
Block a user