]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
hfs: rework hfsplus_readdir() logic
authorViacheslav Dubeyko <slava@dubeyko.com>
Tue, 19 May 2026 22:28:12 +0000 (15:28 -0700)
committerViacheslav Dubeyko <slava@dubeyko.com>
Wed, 27 May 2026 18:24:05 +0000 (11:24 -0700)
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>
fs/hfs/catalog.c
fs/hfs/dir.c
fs/hfs/hfs.h
fs/hfs/hfs_fs.h
fs/hfs/inode.c

index 7f5339ee57c15aae2d5d00474133a985be3af6ca..1bfa36d71e247a7d5aabb45d037046d27139575e 100644 (file)
@@ -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;
index 702c16ea954bf76423e1f45645efd3d255aea109..509c77a99625a42e3fd1fee11aff837dbd0077ff 100644 (file)
@@ -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 @@ out:
 
 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;
 }
 
index 3f2293ff6fdd264b4076732fe723c40d6d2f400e..2e958c46bc0affcb8ade5ca2b271c87763c09480 100644 (file)
@@ -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;
 };
 
index ac0e83f77a0f196bafd00a436ec92c042d4e9560..97e8d1f96d6d6c6f33afaf335685cca82dd4ea55 100644 (file)
@@ -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;
index b2329de151ecaeaa8a1845272cfbe0b717268749..ab52ad2d2ad8a5a26b654c7421f532bec62fa5c6 100644 (file)
@@ -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;