The xfstests' test-case generic/533 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/533 _check_generic_filesystem: filesystem on /dev/loop50 is inconsistent (see xfstests-dev/results//generic/533.full for details) The key reason of the issue is returning -ENOENT error code from hfsplus_find_attr(), __hfsplus_delete_attr(), hfsplus_delete_attr_nolock(), hfsplus_delete_all_attrs(). The file exists but we don't have any xattr for this file. Finally, -ENODATA error code is expected by application logic. This patch reworks xattr logic of HFS+ by means exchanging the -ENOENT error code on -ENODATA error code if xattr has not been found for existing file or folder. sudo ./check generic/533 FSTYP -- hfsplus PLATFORM -- Linux/x86_64 hfsplus-testing-0001 7.0.0-rc1+ #16 SMP PREEMPT_DYNAMIC Wed Mar 11 15:04:58 PDT 2026 MKFS_OPTIONS -- /dev/loop51 MOUNT_OPTIONS -- /dev/loop51 /mnt/scratch generic/533 33s ... 32s Ran: generic/533 Passed all 1 tests Closes: https://github.com/hfs-linux-kernel/hfs-linux-kernel/issues/184 Signed-off-by: Viacheslav Dubeyko cc: John Paul Adrian Glaubitz cc: Yangtao Li cc: linux-fsdevel@vger.kernel.org --- fs/hfsplus/attributes.c | 38 +++++++++++++++++++++++++++++--------- fs/hfsplus/hfsplus_fs.h | 7 ++++++- fs/hfsplus/xattr.c | 6 +++--- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/fs/hfsplus/attributes.c b/fs/hfsplus/attributes.c index 6585bcea731c..704635c65e9a 100644 --- a/fs/hfsplus/attributes.c +++ b/fs/hfsplus/attributes.c @@ -153,14 +153,22 @@ int hfsplus_find_attr(struct super_block *sb, u32 cnid, if (err) goto failed_find_attr; err = hfs_brec_find(fd, hfs_find_rec_by_key); - if (err) + if (err == -ENOENT) { + /* file exists but xattr is absent */ + err = -ENODATA; + goto failed_find_attr; + } else if (err) goto failed_find_attr; } else { err = hfsplus_attr_build_key(sb, fd->search_key, cnid, NULL); if (err) goto failed_find_attr; err = hfs_brec_find(fd, hfs_find_1st_rec_by_cnid); - if (err) + if (err == -ENOENT) { + /* file exists but xattr is absent */ + err = -ENODATA; + goto failed_find_attr; + } else if (err) goto failed_find_attr; } @@ -174,6 +182,9 @@ int hfsplus_attr_exists(struct inode *inode, const char *name) struct super_block *sb = inode->i_sb; struct hfs_find_data fd; + hfs_dbg("name %s, ino %ld\n", + name ? name : NULL, inode->i_ino); + if (!HFSPLUS_SB(sb)->attr_tree) return 0; @@ -293,15 +304,16 @@ int hfsplus_create_attr(struct inode *inode, static int __hfsplus_delete_attr(struct inode *inode, u32 cnid, struct hfs_find_data *fd) { - int err = 0; + int err; __be32 found_cnid, record_type; + found_cnid = U32_MAX; hfs_bnode_read(fd->bnode, &found_cnid, fd->keyoffset + offsetof(struct hfsplus_attr_key, cnid), sizeof(__be32)); if (cnid != be32_to_cpu(found_cnid)) - return -ENOENT; + return -ENODATA; hfs_bnode_read(fd->bnode, &record_type, fd->entryoffset, sizeof(record_type)); @@ -330,7 +342,7 @@ static int __hfsplus_delete_attr(struct inode *inode, u32 cnid, hfsplus_mark_inode_dirty(HFSPLUS_ATTR_TREE_I(inode->i_sb), HFSPLUS_I_ATTR_DIRTY); hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY); - return err; + return 0; } static @@ -354,7 +366,10 @@ int hfsplus_delete_attr_nolock(struct inode *inode, const char *name, } err = hfs_brec_find(fd, hfs_find_rec_by_key); - if (err) + if (err == -ENOENT) { + /* file exists but xattr is absent */ + return -ENODATA; + } else if (err) return err; err = __hfsplus_delete_attr(inode, inode->i_ino, fd); @@ -414,9 +429,14 @@ int hfsplus_delete_all_attrs(struct inode *dir, u32 cnid) for (;;) { err = hfsplus_find_attr(dir->i_sb, cnid, NULL, &fd); - if (err) { - if (err != -ENOENT) - pr_err("xattr search failed\n"); + if (err == -ENOENT || err == -ENODATA) { + /* + * xattr has not been found + */ + err = -ENODATA; + goto end_delete_all; + } else if (err) { + pr_err("xattr search failed\n"); goto end_delete_all; } diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 420dc920e097..caba698814fe 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -571,7 +571,12 @@ hfsplus_btree_lock_class(struct hfs_btree *tree) static inline bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off) { - bool is_valid = off < node->tree->node_size; + bool is_valid; + + if (!node || !node->tree) + return false; + + is_valid = off < node->tree->node_size; if (!is_valid) { pr_err("requested invalid offset: " diff --git a/fs/hfsplus/xattr.c b/fs/hfsplus/xattr.c index a824bcaac172..89e2e7e46e96 100644 --- a/fs/hfsplus/xattr.c +++ b/fs/hfsplus/xattr.c @@ -562,10 +562,10 @@ ssize_t __hfsplus_getxattr(struct inode *inode, const char *name, res = hfsplus_find_attr(inode->i_sb, inode->i_ino, name, &fd); if (res) { - if (res == -ENOENT) + if (res == -ENOENT || res == -ENODATA) res = -ENODATA; else - pr_err("xattr searching failed\n"); + pr_err("xattr search failed\n"); goto out; } @@ -757,7 +757,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 (err == -ENOENT || err == -ENODATA) { res = 0; goto end_listxattr; } else { -- 2.43.0