Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'for-6.18-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux

Pull btrfs fixes from David Sterba:

- in send, fix duplicated rmdir operations when using extrefs
(hardlinks), receive can fail with ENOENT

- fixup of error check when reading extent root in ref-verify and
damaged roots are allowed by mount option (found by smatch)

- fix freeing partially initialized fs info (found by syzkaller)

- fix use-after-free when printing ref_tracking status of delayed
inodes

* tag 'for-6.18-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
btrfs: ref-verify: fix IS_ERR() vs NULL check in btrfs_build_ref_tree()
btrfs: fix delayed_node ref_tracker use after free
btrfs: send: fix duplicated rmdir operations when using extrefs
btrfs: directly free partially initialized fs_info in btrfs_check_leaked_roots()

+64 -11
+1 -1
fs/btrfs/delayed-inode.c
··· 2110 2110 2111 2111 for (int i = 0; i < count; i++) { 2112 2112 __btrfs_kill_delayed_node(delayed_nodes[i]); 2113 + btrfs_delayed_node_ref_tracker_dir_print(delayed_nodes[i]); 2113 2114 btrfs_release_delayed_node(delayed_nodes[i], 2114 2115 &delayed_node_trackers[i]); 2115 - btrfs_delayed_node_ref_tracker_dir_print(delayed_nodes[i]); 2116 2116 } 2117 2117 } 2118 2118 }
+7
fs/btrfs/delayed-inode.h
··· 219 219 if (!btrfs_test_opt(node->root->fs_info, REF_TRACKER)) 220 220 return; 221 221 222 + /* 223 + * Only print if there are leaked references. The caller is 224 + * holding one reference, so if refs == 1 there is no leak. 225 + */ 226 + if (refcount_read(&node->refs) == 1) 227 + return; 228 + 222 229 ref_tracker_dir_print(&node->ref_dir.dir, 223 230 BTRFS_DELAYED_NODE_REF_TRACKER_DISPLAY_LIMIT); 224 231 }
+1 -1
fs/btrfs/ref-verify.c
··· 982 982 983 983 extent_root = btrfs_extent_root(fs_info, 0); 984 984 /* If the extent tree is damaged we cannot ignore it (IGNOREBADROOTS). */ 985 - if (IS_ERR(extent_root)) { 985 + if (!extent_root) { 986 986 btrfs_warn(fs_info, "ref-verify: extent tree not available, disabling"); 987 987 btrfs_clear_opt(fs_info->mount_opt, REF_VERIFY); 988 988 return 0;
+48 -8
fs/btrfs/send.c
··· 4102 4102 return ret; 4103 4103 } 4104 4104 4105 + static int rbtree_check_dir_ref_comp(const void *k, const struct rb_node *node) 4106 + { 4107 + const struct recorded_ref *data = k; 4108 + const struct recorded_ref *ref = rb_entry(node, struct recorded_ref, node); 4109 + 4110 + if (data->dir > ref->dir) 4111 + return 1; 4112 + if (data->dir < ref->dir) 4113 + return -1; 4114 + if (data->dir_gen > ref->dir_gen) 4115 + return 1; 4116 + if (data->dir_gen < ref->dir_gen) 4117 + return -1; 4118 + return 0; 4119 + } 4120 + 4121 + static bool rbtree_check_dir_ref_less(struct rb_node *node, const struct rb_node *parent) 4122 + { 4123 + const struct recorded_ref *entry = rb_entry(node, struct recorded_ref, node); 4124 + 4125 + return rbtree_check_dir_ref_comp(entry, parent) < 0; 4126 + } 4127 + 4128 + static int record_check_dir_ref_in_tree(struct rb_root *root, 4129 + struct recorded_ref *ref, struct list_head *list) 4130 + { 4131 + struct recorded_ref *tmp_ref; 4132 + int ret; 4133 + 4134 + if (rb_find(ref, root, rbtree_check_dir_ref_comp)) 4135 + return 0; 4136 + 4137 + ret = dup_ref(ref, list); 4138 + if (ret < 0) 4139 + return ret; 4140 + 4141 + tmp_ref = list_last_entry(list, struct recorded_ref, list); 4142 + rb_add(&tmp_ref->node, root, rbtree_check_dir_ref_less); 4143 + tmp_ref->root = root; 4144 + return 0; 4145 + } 4146 + 4105 4147 static int rename_current_inode(struct send_ctx *sctx, 4106 4148 struct fs_path *current_path, 4107 4149 struct fs_path *new_path) ··· 4171 4129 struct recorded_ref *cur; 4172 4130 struct recorded_ref *cur2; 4173 4131 LIST_HEAD(check_dirs); 4132 + struct rb_root rbtree_check_dirs = RB_ROOT; 4174 4133 struct fs_path *valid_path = NULL; 4175 4134 u64 ow_inode = 0; 4176 4135 u64 ow_gen; 4177 4136 u64 ow_mode; 4178 - u64 last_dir_ino_rm = 0; 4179 4137 bool did_overwrite = false; 4180 4138 bool is_orphan = false; 4181 4139 bool can_rename = true; ··· 4479 4437 goto out; 4480 4438 } 4481 4439 } 4482 - ret = dup_ref(cur, &check_dirs); 4440 + ret = record_check_dir_ref_in_tree(&rbtree_check_dirs, cur, &check_dirs); 4483 4441 if (ret < 0) 4484 4442 goto out; 4485 4443 } ··· 4507 4465 } 4508 4466 4509 4467 list_for_each_entry(cur, &sctx->deleted_refs, list) { 4510 - ret = dup_ref(cur, &check_dirs); 4468 + ret = record_check_dir_ref_in_tree(&rbtree_check_dirs, cur, &check_dirs); 4511 4469 if (ret < 0) 4512 4470 goto out; 4513 4471 } ··· 4517 4475 * We have a moved dir. Add the old parent to check_dirs 4518 4476 */ 4519 4477 cur = list_first_entry(&sctx->deleted_refs, struct recorded_ref, list); 4520 - ret = dup_ref(cur, &check_dirs); 4478 + ret = record_check_dir_ref_in_tree(&rbtree_check_dirs, cur, &check_dirs); 4521 4479 if (ret < 0) 4522 4480 goto out; 4523 4481 } else if (!S_ISDIR(sctx->cur_inode_mode)) { ··· 4551 4509 if (is_current_inode_path(sctx, cur->full_path)) 4552 4510 fs_path_reset(&sctx->cur_inode_path); 4553 4511 } 4554 - ret = dup_ref(cur, &check_dirs); 4512 + ret = record_check_dir_ref_in_tree(&rbtree_check_dirs, cur, &check_dirs); 4555 4513 if (ret < 0) 4556 4514 goto out; 4557 4515 } ··· 4594 4552 ret = cache_dir_utimes(sctx, cur->dir, cur->dir_gen); 4595 4553 if (ret < 0) 4596 4554 goto out; 4597 - } else if (ret == inode_state_did_delete && 4598 - cur->dir != last_dir_ino_rm) { 4555 + } else if (ret == inode_state_did_delete) { 4599 4556 ret = can_rmdir(sctx, cur->dir, cur->dir_gen); 4600 4557 if (ret < 0) 4601 4558 goto out; ··· 4606 4565 ret = send_rmdir(sctx, valid_path); 4607 4566 if (ret < 0) 4608 4567 goto out; 4609 - last_dir_ino_rm = cur->dir; 4610 4568 } 4611 4569 } 4612 4570 }
+7 -1
fs/btrfs/super.c
··· 2068 2068 fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL); 2069 2069 fs_info->super_for_commit = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL); 2070 2070 if (!fs_info->super_copy || !fs_info->super_for_commit) { 2071 - btrfs_free_fs_info(fs_info); 2071 + /* 2072 + * Dont call btrfs_free_fs_info() to free it as it's still 2073 + * initialized partially. 2074 + */ 2075 + kfree(fs_info->super_copy); 2076 + kfree(fs_info->super_for_commit); 2077 + kvfree(fs_info); 2072 2078 return -ENOMEM; 2073 2079 } 2074 2080 btrfs_init_fs_info(fs_info);