From: Viacheslav Dubeyko Date: Tue, 20 Jan 2026 04:19:38 +0000 (-0800) Subject: hfsplus: fix generic/062 xfstests failure X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aef5078471294e7a7a2d6b9da4988a6a3b7aff54;p=thirdparty%2Flinux.git hfsplus: fix generic/062 xfstests failure 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 cc: John Paul Adrian Glaubitz cc: Yangtao Li cc: linux-fsdevel@vger.kernel.org Link: https://lore.kernel.org/r/20260120041937.3450928-1-slava@dubeyko.com Signed-off-by: Viacheslav Dubeyko --- diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 6153e5cc6eb65..533c43cc37680 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -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)); } diff --git a/fs/hfsplus/xattr.c b/fs/hfsplus/xattr.c index c3dcbe30f16ad..9904944cbd54e 100644 --- a/fs/hfsplus/xattr.c +++ b/fs/hfsplus/xattr.c @@ -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;