summaryrefslogtreecommitdiff
path: root/fs/hfsplus/super.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-04-13 16:50:38 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-04-13 16:50:38 -0700
commit4d9981429aa61c31e67371ac09e7dbba6b59de14 (patch)
treed2ef472a3272c96c2a35c5dfb02925f26280b935 /fs/hfsplus/super.c
parentf3756afb6f6cdcbbc246f1edd0580b8c7485124a (diff)
parentc1307d18caa819ddc28459d858eb38fdd6c3f8a0 (diff)
downloadlinux-next-4d9981429aa61c31e67371ac09e7dbba6b59de14.tar.gz
linux-next-4d9981429aa61c31e67371ac09e7dbba6b59de14.zip
Merge tag 'hfs-v7.1-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs
Pull hfsplus updates from Viacheslav Dubeyko: "This contains several fixes of syzbot reported issues and HFS+ fixes of xfstests failures. - Fix a syzbot reported issue of a KMSAN uninit-value in hfsplus_strcasecmp(). The root cause was that hfs_brec_read() doesn't validate that the on-disk record size matches the expected size for the record type being read. The fix introduced hfsplus_brec_read_cat() wrapper that validates the record size based on the type field and returns -EIO if size doesn't match (Deepanshu Kartikey) - Fix a syzbot reported issue of processing corrupted HFS+ images where the b-tree allocation bitmap indicates that the header node (Node 0) is free. Node 0 must always be allocated. Violating this invariant leads to allocator corruption, which cascades into kernel panics or undefined behavior. Prevent trusting a corrupted allocator state by adding a validation check during hfs_btree_open(). If corruption is detected, print a warning identifying the specific corrupted tree and force the filesystem to mount read-only (SB_RDONLY). This prevents kernel panics from corrupted images while enabling data recovery (Shardul Bankar) - Fix a potential deadlock in hfsplus_fill_super(). hfsplus_fill_super() calls hfs_find_init() to initialize a search structure, which acquires tree->tree_lock. If the subsequent call to hfsplus_cat_build_key() fails, the function jumps to the out_put_root error label without releasing the lock. Fix this by adding the missing hfs_find_exit(&fd) call before jumping to the out_put_root error label. This ensures that tree->tree_lock is properly released on the error path (Zilin Guan) - Update a files ctime after rename in hfsplus_rename() (Yangtao Li) The rest of the patches introduce the HFS+ fixes for the case of generic/348, generic/728, generic/533, generic/523, and generic/642 test-cases of xfstests suite" * tag 'hfs-v7.1-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs: hfsplus: fix generic/642 failure hfsplus: rework logic of map nodes creation in xattr b-tree hfsplus: fix logic of alloc/free b-tree node hfsplus: fix error processing issue in hfs_bmap_free() hfsplus: fix potential race conditions in b-tree functionality hfsplus: extract hidden directory search into a helper function hfsplus: fix held lock freed on hfsplus_fill_super() hfsplus: fix generic/523 test-case failure hfsplus: validate b-tree node 0 bitmap at mount time hfsplus: refactor b-tree map page access and add node-type validation hfsplus: fix to update ctime after rename hfsplus: fix generic/533 test-case failure hfsplus: set ctime after setxattr and removexattr hfsplus: fix uninit-value by validating catalog record size hfsplus: fix potential Allocation File corruption after fsync
Diffstat (limited to 'fs/hfsplus/super.c')
-rw-r--r--fs/hfsplus/super.c47
1 files changed, 37 insertions, 10 deletions
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index b3917249c206..40a0feda716b 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -153,7 +153,10 @@ static int hfsplus_system_write_inode(struct inode *inode)
}
hfsplus_inode_write_fork(inode, fork);
if (tree) {
+ mutex_lock_nested(&tree->tree_lock,
+ hfsplus_btree_lock_class(tree));
int err = hfs_btree_write(tree);
+ mutex_unlock(&tree->tree_lock);
if (err) {
pr_err("b-tree write err: %d, ino %llu\n",
@@ -424,12 +427,35 @@ void hfsplus_prepare_volume_header_for_commit(struct hfsplus_vh *vhdr)
vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
}
+static inline int hfsplus_get_hidden_dir_entry(struct super_block *sb,
+ const struct qstr *str,
+ hfsplus_cat_entry *entry)
+{
+ struct hfs_find_data fd;
+ int err;
+
+ err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
+ if (unlikely(err))
+ return err;
+
+ err = hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, str);
+ if (unlikely(err))
+ goto free_fd;
+
+ err = hfsplus_brec_read_cat(&fd, entry);
+ if (err)
+ err = -ENOENT;
+
+free_fd:
+ hfs_find_exit(&fd);
+ return err;
+}
+
static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)
{
struct hfsplus_vh *vhdr;
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
hfsplus_cat_entry entry;
- struct hfs_find_data fd;
struct inode *root, *inode;
struct qstr str;
struct nls_table *nls;
@@ -565,14 +591,14 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)
str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
str.name = HFSP_HIDDENDIR_NAME;
- err = hfs_find_init(sbi->cat_tree, &fd);
- if (err)
- goto out_put_root;
- err = hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str);
- if (unlikely(err < 0))
+ err = hfsplus_get_hidden_dir_entry(sb, &str, &entry);
+ if (err == -ENOENT) {
+ /*
+ * Hidden directory is absent or it cannot be read.
+ */
+ } else if (unlikely(err)) {
goto out_put_root;
- if (!hfs_brec_read(&fd, &entry, sizeof(entry))) {
- hfs_find_exit(&fd);
+ } else {
if (entry.type != cpu_to_be16(HFSPLUS_FOLDER)) {
err = -EIO;
goto out_put_root;
@@ -583,8 +609,7 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)
goto out_put_root;
}
sbi->hidden_dir = inode;
- } else
- hfs_find_exit(&fd);
+ }
if (!sb_rdonly(sb)) {
/*
@@ -625,6 +650,8 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)
}
mutex_unlock(&sbi->vh_mutex);
+ hfsplus_mark_inode_dirty(HFSPLUS_CAT_TREE_I(sb),
+ HFSPLUS_I_CAT_DIRTY);
hfsplus_mark_inode_dirty(sbi->hidden_dir,
HFSPLUS_I_CAT_DIRTY);
}