diff options
| author | Viacheslav Dubeyko <slava@dubeyko.com> | 2026-04-03 16:05:52 -0700 |
|---|---|---|
| committer | Viacheslav Dubeyko <slava@dubeyko.com> | 2026-04-08 14:23:28 -0700 |
| commit | 6dca66d7ba1767d1e8688ee63162eca8d2248e8c (patch) | |
| tree | c737580059b99ae2bbf9aa7bd86209df9ee6f23e /fs/hfsplus | |
| parent | d47059dcc472ae823c7eebe87fb7cec9148b9f06 (diff) | |
hfsplus: fix potential race conditions in b-tree functionality
The HFS_BNODE_DELETED flag is checked in hfs_bnode_put()
under locked tree->hash_lock. This patch adds locking
for the case of setting the HFS_BNODE_DELETED flag in
hfs_bnode_unlink() with the goal to avoid potential
race conditions.
The hfs_btree_write() method should be called under
tree->tree_lock. This patch reworks logic by adding
locking the tree->tree_lock for the calls of
hfs_btree_write() in hfsplus_cat_write_inode() and
hfsplus_system_write_inode().
This patch adds also the lockdep_assert_held() in
hfs_bmap_reserve(), hfs_bmap_alloc(), and hfs_bmap_free().
cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
cc: Yangtao Li <frank.li@vivo.com>
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
Link: https://lore.kernel.org/r/20260403230556.614171-2-slava@dubeyko.com
Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
Diffstat (limited to 'fs/hfsplus')
| -rw-r--r-- | fs/hfsplus/bnode.c | 3 | ||||
| -rw-r--r-- | fs/hfsplus/btree.c | 5 | ||||
| -rw-r--r-- | fs/hfsplus/inode.c | 15 | ||||
| -rw-r--r-- | fs/hfsplus/super.c | 3 |
4 files changed, 18 insertions, 8 deletions
diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c index 250a226336ea..f8b5a8ae58ff 100644 --- a/fs/hfsplus/bnode.c +++ b/fs/hfsplus/bnode.c @@ -420,7 +420,10 @@ void hfs_bnode_unlink(struct hfs_bnode *node) tree->root = 0; tree->depth = 0; } + + spin_lock(&tree->hash_lock); set_bit(HFS_BNODE_DELETED, &node->flags); + spin_unlock(&tree->hash_lock); } static inline int hfs_bnode_hash(u32 num) diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index 04304f952f6b..f1ed7c336593 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -500,6 +500,8 @@ int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes) u32 count; int res; + lockdep_assert_held(&tree->tree_lock); + if (rsvd_nodes <= 0) return 0; @@ -529,6 +531,8 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree) u8 *data, byte, m; int i, res; + lockdep_assert_held(&tree->tree_lock); + res = hfs_bmap_reserve(tree, 1); if (res) return ERR_PTR(res); @@ -607,6 +611,7 @@ void hfs_bmap_free(struct hfs_bnode *node) hfs_dbg("node %u\n", node->this); BUG_ON(!node->this); tree = node->tree; + lockdep_assert_held(&tree->tree_lock); nidx = node->this; node = hfs_bnode_find(tree, 0); if (IS_ERR(node)) diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index cdf08393de44..e8f359d69328 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -720,20 +720,19 @@ int hfsplus_cat_write_inode(struct inode *inode) sizeof(struct hfsplus_cat_file)); } + res = hfs_btree_write(tree); + if (res) { + pr_err("b-tree write err: %d, ino %lu\n", + res, inode->i_ino); + goto out; + } + set_bit(HFSPLUS_I_CAT_DIRTY, &HFSPLUS_I(HFSPLUS_CAT_TREE_I(inode->i_sb))->flags); set_bit(HFSPLUS_I_CAT_DIRTY, &HFSPLUS_I(inode)->flags); out: hfs_find_exit(&fd); - if (!res) { - res = hfs_btree_write(tree); - if (res) { - pr_err("b-tree write err: %d, ino %lu\n", - res, inode->i_ino); - } - } - return res; } diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 8bd1627ffc9c..44635f92ada9 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 %lu\n", |
