Commit aef50784 authored by Viacheslav Dubeyko's avatar Viacheslav Dubeyko
Browse files

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: default avatarViacheslav 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: default avatarViacheslav Dubeyko <slava@dubeyko.com>
parent b18c5b84
Loading
Loading
Loading
Loading
+21 −2
Original line number Diff line number Diff line
@@ -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));
		}
+18 −11
Original line number Diff line number Diff line
@@ -258,6 +258,15 @@ static int hfsplus_create_attributes_file(struct super_block *sb)
	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;