]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
hfsplus: fix generic/062 xfstests failure
authorViacheslav Dubeyko <slava@dubeyko.com>
Tue, 20 Jan 2026 04:19:38 +0000 (20:19 -0800)
committerViacheslav Dubeyko <slava@dubeyko.com>
Wed, 28 Jan 2026 22:53:15 +0000 (14:53 -0800)
The xfstests' test-case generic/062 fails to execute
correctly:

FSTYP -- hfsplus
PLATFORM -- Linux/x86_64 hfsplus-testing-0001 6.15.0-rc4+ #8 SMP PREEMPT_DYNAMIC Thu May 1 16:43:22 PDT 2025
MKFS_OPTIONS -- /dev/loop51
MOUNT_OPTIONS -- /dev/loop51 /mnt/scratch

generic/062 - output mismatch (see xfstests-dev/results//generic/062.out.bad)

The generic/062 test tries to set and get xattrs for various types
of objects (regular file, folder, block device, character
device, pipe, etc) with the goal to check that xattr operations
works correctly for all possible types of file system objects.
But current HFS+ implementation somehow hasn't support of
xattr operatioons for the case of block device, character
device, and pipe objects. Also, it has not completely correct
set of operations for the case symlinks.

This patch implements proper declaration of xattrs operations
hfsplus_special_inode_operations and hfsplus_symlink_inode_operations.
Also, it slightly corrects the logic of hfsplus_listxattr()
method.

sudo ./check generic/062
FSTYP         -- hfsplus
PLATFORM      -- Linux/x86_64 hfsplus-testing-0001 6.19.0-rc1+ #59 SMP PREEMPT_DYNAMIC Mon Jan 19 16:26:21 PST 2026
MKFS_OPTIONS  -- /dev/loop51
MOUNT_OPTIONS -- /dev/loop51 /mnt/scratch

generic/062 20s ...  20s
Ran: generic/062
Passed all 1 tests

[1] https://github.com/hfs-linux-kernel/hfs-linux-kernel/issues/93

Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
cc: Yangtao Li <frank.li@vivo.com>
cc: linux-fsdevel@vger.kernel.org
Link: https://lore.kernel.org/r/20260120041937.3450928-1-slava@dubeyko.com
Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
fs/hfsplus/inode.c
fs/hfsplus/xattr.c

index 6153e5cc6eb65e99019f791a37503f2533150289..533c43cc376804d98e55abd5f3d7d1599572ab75 100644 (file)
@@ -396,6 +396,19 @@ static const struct inode_operations hfsplus_file_inode_operations = {
        .fileattr_set   = hfsplus_fileattr_set,
 };
 
+const struct inode_operations hfsplus_symlink_inode_operations = {
+       .get_link       = page_get_link,
+       .setattr        = hfsplus_setattr,
+       .getattr        = hfsplus_getattr,
+       .listxattr      = hfsplus_listxattr,
+};
+
+const struct inode_operations hfsplus_special_inode_operations = {
+       .setattr        = hfsplus_setattr,
+       .getattr        = hfsplus_getattr,
+       .listxattr      = hfsplus_listxattr,
+};
+
 static const struct file_operations hfsplus_file_operations = {
        .llseek         = generic_file_llseek,
        .read_iter      = generic_file_read_iter,
@@ -455,12 +468,17 @@ struct inode *hfsplus_new_inode(struct super_block *sb, struct inode *dir,
                hip->clump_blocks = sbi->data_clump_blocks;
        } else if (S_ISLNK(inode->i_mode)) {
                sbi->file_count++;
-               inode->i_op = &page_symlink_inode_operations;
+               inode->i_op = &hfsplus_symlink_inode_operations;
                inode_nohighmem(inode);
                inode->i_mapping->a_ops = &hfsplus_aops;
                hip->clump_blocks = 1;
+       } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
+                  S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
+               sbi->file_count++;
+               inode->i_op = &hfsplus_special_inode_operations;
        } else
                sbi->file_count++;
+
        insert_inode_hash(inode);
        mark_inode_dirty(inode);
        hfsplus_mark_mdb_dirty(sb);
@@ -591,10 +609,11 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
                        inode->i_fop = &hfsplus_file_operations;
                        inode->i_mapping->a_ops = &hfsplus_aops;
                } else if (S_ISLNK(inode->i_mode)) {
-                       inode->i_op = &page_symlink_inode_operations;
+                       inode->i_op = &hfsplus_symlink_inode_operations;
                        inode_nohighmem(inode);
                        inode->i_mapping->a_ops = &hfsplus_aops;
                } else {
+                       inode->i_op = &hfsplus_special_inode_operations;
                        init_special_inode(inode, inode->i_mode,
                                           be32_to_cpu(file->permissions.dev));
                }
index c3dcbe30f16adbd8f4eb0cd957a8fc015555b695..9904944cbd54e3d326591fa65a5ed678f38ca583 100644 (file)
@@ -258,6 +258,15 @@ end_attr_file_creation:
        return err;
 }
 
+static inline
+bool is_xattr_operation_supported(struct inode *inode)
+{
+       if (HFSPLUS_IS_RSRC(inode))
+               return false;
+
+       return true;
+}
+
 int __hfsplus_setxattr(struct inode *inode, const char *name,
                        const void *value, size_t size, int flags)
 {
@@ -268,9 +277,11 @@ int __hfsplus_setxattr(struct inode *inode, const char *name,
        u16 folder_finderinfo_len = sizeof(DInfo) + sizeof(DXInfo);
        u16 file_finderinfo_len = sizeof(FInfo) + sizeof(FXInfo);
 
-       if ((!S_ISREG(inode->i_mode) &&
-                       !S_ISDIR(inode->i_mode)) ||
-                               HFSPLUS_IS_RSRC(inode))
+       hfs_dbg("ino %lu, name %s, value %p, size %zu\n",
+               inode->i_ino, name ? name : NULL,
+               value, size);
+
+       if (!is_xattr_operation_supported(inode))
                return -EOPNOTSUPP;
 
        if (value == NULL)
@@ -390,6 +401,7 @@ int __hfsplus_setxattr(struct inode *inode, const char *name,
 
 end_setxattr:
        hfs_find_exit(&cat_fd);
+       hfs_dbg("finished: res %d\n", err);
        return err;
 }
 
@@ -514,9 +526,7 @@ ssize_t __hfsplus_getxattr(struct inode *inode, const char *name,
        u16 record_length = 0;
        ssize_t res;
 
-       if ((!S_ISREG(inode->i_mode) &&
-                       !S_ISDIR(inode->i_mode)) ||
-                               HFSPLUS_IS_RSRC(inode))
+       if (!is_xattr_operation_supported(inode))
                return -EOPNOTSUPP;
 
        if (!strcmp_xattr_finder_info(name))
@@ -709,9 +719,7 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
 
        hfs_dbg("ino %lu\n", inode->i_ino);
 
-       if ((!S_ISREG(inode->i_mode) &&
-                       !S_ISDIR(inode->i_mode)) ||
-                               HFSPLUS_IS_RSRC(inode))
+       if (!is_xattr_operation_supported(inode))
                return -EOPNOTSUPP;
 
        res = hfsplus_listxattr_finder_info(dentry, buffer, size);
@@ -737,8 +745,7 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
        err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd);
        if (err) {
                if (err == -ENOENT) {
-                       if (res == 0)
-                               res = -ENODATA;
+                       res = 0;
                        goto end_listxattr;
                } else {
                        res = err;