Skip to content

Commit 942048d

Browse files
committed
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()
2 parents 43e9ad0 + ada7d45 commit 942048d

5 files changed

Lines changed: 64 additions & 11 deletions

File tree

fs/btrfs/delayed-inode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2110,9 +2110,9 @@ void btrfs_kill_all_delayed_nodes(struct btrfs_root *root)
21102110

21112111
for (int i = 0; i < count; i++) {
21122112
__btrfs_kill_delayed_node(delayed_nodes[i]);
2113+
btrfs_delayed_node_ref_tracker_dir_print(delayed_nodes[i]);
21132114
btrfs_release_delayed_node(delayed_nodes[i],
21142115
&delayed_node_trackers[i]);
2115-
btrfs_delayed_node_ref_tracker_dir_print(delayed_nodes[i]);
21162116
}
21172117
}
21182118
}

fs/btrfs/delayed-inode.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,13 @@ static inline void btrfs_delayed_node_ref_tracker_dir_print(struct btrfs_delayed
219219
if (!btrfs_test_opt(node->root->fs_info, REF_TRACKER))
220220
return;
221221

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+
222229
ref_tracker_dir_print(&node->ref_dir.dir,
223230
BTRFS_DELAYED_NODE_REF_TRACKER_DISPLAY_LIMIT);
224231
}

fs/btrfs/ref-verify.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,7 @@ int btrfs_build_ref_tree(struct btrfs_fs_info *fs_info)
982982

983983
extent_root = btrfs_extent_root(fs_info, 0);
984984
/* If the extent tree is damaged we cannot ignore it (IGNOREBADROOTS). */
985-
if (IS_ERR(extent_root)) {
985+
if (!extent_root) {
986986
btrfs_warn(fs_info, "ref-verify: extent tree not available, disabling");
987987
btrfs_clear_opt(fs_info->mount_opt, REF_VERIFY);
988988
return 0;

fs/btrfs/send.c

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4102,6 +4102,48 @@ static int refresh_ref_path(struct send_ctx *sctx, struct recorded_ref *ref)
41024102
return ret;
41034103
}
41044104

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+
41054147
static int rename_current_inode(struct send_ctx *sctx,
41064148
struct fs_path *current_path,
41074149
struct fs_path *new_path)
@@ -4129,11 +4171,11 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
41294171
struct recorded_ref *cur;
41304172
struct recorded_ref *cur2;
41314173
LIST_HEAD(check_dirs);
4174+
struct rb_root rbtree_check_dirs = RB_ROOT;
41324175
struct fs_path *valid_path = NULL;
41334176
u64 ow_inode = 0;
41344177
u64 ow_gen;
41354178
u64 ow_mode;
4136-
u64 last_dir_ino_rm = 0;
41374179
bool did_overwrite = false;
41384180
bool is_orphan = false;
41394181
bool can_rename = true;
@@ -4437,7 +4479,7 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
44374479
goto out;
44384480
}
44394481
}
4440-
ret = dup_ref(cur, &check_dirs);
4482+
ret = record_check_dir_ref_in_tree(&rbtree_check_dirs, cur, &check_dirs);
44414483
if (ret < 0)
44424484
goto out;
44434485
}
@@ -4465,7 +4507,7 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
44654507
}
44664508

44674509
list_for_each_entry(cur, &sctx->deleted_refs, list) {
4468-
ret = dup_ref(cur, &check_dirs);
4510+
ret = record_check_dir_ref_in_tree(&rbtree_check_dirs, cur, &check_dirs);
44694511
if (ret < 0)
44704512
goto out;
44714513
}
@@ -4475,7 +4517,7 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
44754517
* We have a moved dir. Add the old parent to check_dirs
44764518
*/
44774519
cur = list_first_entry(&sctx->deleted_refs, struct recorded_ref, list);
4478-
ret = dup_ref(cur, &check_dirs);
4520+
ret = record_check_dir_ref_in_tree(&rbtree_check_dirs, cur, &check_dirs);
44794521
if (ret < 0)
44804522
goto out;
44814523
} else if (!S_ISDIR(sctx->cur_inode_mode)) {
@@ -4509,7 +4551,7 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
45094551
if (is_current_inode_path(sctx, cur->full_path))
45104552
fs_path_reset(&sctx->cur_inode_path);
45114553
}
4512-
ret = dup_ref(cur, &check_dirs);
4554+
ret = record_check_dir_ref_in_tree(&rbtree_check_dirs, cur, &check_dirs);
45134555
if (ret < 0)
45144556
goto out;
45154557
}
@@ -4552,8 +4594,7 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
45524594
ret = cache_dir_utimes(sctx, cur->dir, cur->dir_gen);
45534595
if (ret < 0)
45544596
goto out;
4555-
} else if (ret == inode_state_did_delete &&
4556-
cur->dir != last_dir_ino_rm) {
4597+
} else if (ret == inode_state_did_delete) {
45574598
ret = can_rmdir(sctx, cur->dir, cur->dir_gen);
45584599
if (ret < 0)
45594600
goto out;
@@ -4565,7 +4606,6 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
45654606
ret = send_rmdir(sctx, valid_path);
45664607
if (ret < 0)
45674608
goto out;
4568-
last_dir_ino_rm = cur->dir;
45694609
}
45704610
}
45714611
}

fs/btrfs/super.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2068,7 +2068,13 @@ static int btrfs_get_tree_subvol(struct fs_context *fc)
20682068
fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL);
20692069
fs_info->super_for_commit = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL);
20702070
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);
20722078
return -ENOMEM;
20732079
}
20742080
btrfs_init_fs_info(fs_info);

0 commit comments

Comments
 (0)