@@ -92,6 +92,13 @@ struct extent_backref {
unsigned int broken:1;
};
+static int add_root_item_to_list(struct list_head *head,
+ u64 objectid, u64 bytenr, u64 last_snapshot,
+ u8 level, u8 drop_level,
+ int level_size, struct btrfs_key *drop_key);
+
+static void free_root_item_list(struct list_head *list);
+
static inline struct extent_backref* to_extent_backref(struct list_head *entry)
{
return list_entry(entry, struct extent_backref, list);
@@ -3761,6 +3768,10 @@ static int check_fs_roots(struct btrfs_root *root,
struct btrfs_root *tree_root = root->fs_info->tree_root;
int ret;
int err = 0;
+ struct list_head dropping_trees;
+ struct btrfs_disk_key *drop_progress;
+
+ INIT_LIST_HEAD(&dropping_trees);
if (ctx.progress_enabled) {
ctx.tp = TASK_FS_ROOTS;
@@ -3818,6 +3829,32 @@ again:
err = 1;
goto next;
}
+
+ drop_progress = &tmp_root->root_item.drop_progress;
+ if (btrfs_disk_key_objectid(drop_progress) != 0) {
+ struct btrfs_key drop_key;
+
+ btrfs_disk_key_to_cpu(&drop_key, drop_progress);
+ ret = add_root_item_to_list(&dropping_trees,
+ tmp_root->root_key.objectid,
+ 0, 0, 0, tmp_root->root_item.drop_level,
+ 0, &drop_key);
+ if (ret < 0) {
+ err = 1;
+ goto out;
+ }
+ } else if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) {
+ struct root_item_record *ri_rec;
+
+ list_for_each_entry(ri_rec, &dropping_trees, list) {
+ if (ri_rec->objectid == key.offset) {
+ btrfs_cpu_key_to_disk(drop_progress, &ri_rec->drop_key);
+ tmp_root->root_item.drop_level = ri_rec->drop_level;
+ break;
+ }
+ }
+ }
+
ret = check_fs_root(tmp_root, root_cache, &wc);
if (ret == -EAGAIN) {
free_root_recs_tree(root_cache);
@@ -3837,6 +3874,7 @@ next:
path.slots[0]++;
}
out:
+ free_root_item_list(&dropping_trees);
btrfs_release_path(&path);
if (err)
free_extent_cache_tree(&wc.shared);
@@ -8536,6 +8574,9 @@ again:
if (found_key.type == BTRFS_ROOT_ITEM_KEY) {
unsigned long offset;
u64 last_snapshot;
+ struct root_item_record *ri_rec;
+ struct btrfs_key drop_key = {0};
+ u8 drop_level = 0;
offset = btrfs_item_ptr_offset(leaf, path.slots[0]);
read_extent_buffer(leaf, &ri, offset, sizeof(ri));
@@ -8543,11 +8584,20 @@ again:
if (btrfs_disk_key_objectid(&ri.drop_progress) == 0) {
level = btrfs_root_level(&ri);
level_size = root->nodesize;
+ if (found_key.objectid == BTRFS_TREE_RELOC_OBJECTID) {
+ list_for_each_entry(ri_rec, &dropping_trees, list) {
+ if (ri_rec->objectid == found_key.offset) {
+ drop_key = ri_rec->drop_key;
+ drop_level = ri_rec->drop_level;
+ break;
+ }
+ }
+ }
ret = add_root_item_to_list(&normal_trees,
found_key.objectid,
btrfs_root_bytenr(&ri),
last_snapshot, level,
- 0, level_size, NULL);
+ drop_level, level_size, &drop_key);
if (ret < 0)
goto out;
} else {
If subvolume is deleted, we add drop_key into the corresponding root item, so we know where to start processing the deleted subvolume. However, we don't skip the keys prior to the drop_key in corresponding relocation tree of the deleted subvolume. As a result, we might run into block that is freed and being used again. This cause btrfs check to report false alarm. Fix this by adding drop_key for deleted subvolume to its corresponding relocation tree. Signed-off-by: ethanwu <ethanwu@synology.com> --- cmds-check.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-)