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 <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>
This commit is contained in:
Viacheslav Dubeyko 2026-01-19 20:19:38 -08:00
parent b18c5b84fa
commit aef5078471
2 changed files with 39 additions and 13 deletions

View 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));
}

View 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;