diff --git a/cmds-check.c b/cmds-check.c index a65670e3..dbf48c8c 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -119,6 +119,12 @@ struct inode_backref { char name[0]; }; +struct dropping_root_item_record { + struct list_head list; + struct btrfs_root_item ri; + struct btrfs_key found_key; +}; + #define REF_ERR_NO_DIR_ITEM (1 << 0) #define REF_ERR_NO_DIR_INDEX (1 << 1) #define REF_ERR_NO_INODE_REF (1 << 2) @@ -3600,6 +3606,21 @@ static int check_csums(struct btrfs_root *root) return errors; } +static int is_dropped_key(struct btrfs_key *key, + struct btrfs_key *drop_key) { + if (key->objectid < drop_key->objectid) + return 1; + else if (key->objectid == drop_key->objectid) { + if (key->type < drop_key->type) + return 1; + else if (key->type == drop_key->type) { + if (key->offset < drop_key->offset) + return 1; + } + } + return 0; +} + static int run_next_block(struct btrfs_root *root, struct block_info *bits, int bits_nr, @@ -3612,7 +3633,8 @@ static int run_next_block(struct btrfs_root *root, struct cache_tree *chunk_cache, struct rb_root *dev_cache, struct block_group_tree *block_group_cache, - struct device_extent_tree *dev_extent_cache) + struct device_extent_tree *dev_extent_cache, + struct btrfs_root_item *ri) { struct extent_buffer *buf; u64 bytenr; @@ -3817,6 +3839,15 @@ static int run_next_block(struct btrfs_root *root, ptr = btrfs_node_blockptr(buf, i); size = btrfs_level_size(root, level - 1); btrfs_node_key_to_cpu(buf, &key, i); + if (ri != NULL) { + struct btrfs_key drop_key; + btrfs_disk_key_to_cpu(&drop_key, + &ri->drop_progress); + if ((level == ri->drop_level) + && is_dropped_key(&key, &drop_key)) { + continue; + } + } ret = add_extent_rec(extent_cache, &key, ptr, size, 0, 0, 1, 0, 1, 0, size); @@ -5446,6 +5477,7 @@ static int check_chunks_and_extents(struct btrfs_root *root) struct btrfs_trans_handle *trans = NULL; int slot; struct btrfs_root_item ri; + struct list_head dropping_trees; dev_cache = RB_ROOT; cache_tree_init(&chunk_cache); @@ -5458,6 +5490,7 @@ static int check_chunks_and_extents(struct btrfs_root *root) cache_tree_init(&nodes); cache_tree_init(&reada); cache_tree_init(&corrupt_blocks); + INIT_LIST_HEAD(&dropping_trees); if (repair) { trans = btrfs_start_transaction(root, 1); @@ -5510,26 +5543,74 @@ again: offset = btrfs_item_ptr_offset(leaf, path.slots[0]); read_extent_buffer(leaf, &ri, offset, sizeof(ri)); - buf = read_tree_block(root->fs_info->tree_root, - btrfs_root_bytenr(&ri), - btrfs_level_size(root, - btrfs_root_level(&ri)), 0); - add_root_to_pending(buf, &extent_cache, &pending, - &seen, &nodes, &found_key); - free_extent_buffer(buf); + if (btrfs_disk_key_objectid(&ri.drop_progress) == 0) { + buf = read_tree_block(root->fs_info->tree_root, + btrfs_root_bytenr(&ri), + btrfs_level_size(root, + btrfs_root_level(&ri)), + 0); + add_root_to_pending(buf, &extent_cache, + &pending, &seen, &nodes, + &found_key); + free_extent_buffer(buf); + } else { + struct dropping_root_item_record *dri_rec; + dri_rec = malloc(sizeof(*dri_rec)); + if (!dri_rec) { + perror("malloc"); + exit(1); + } + memcpy(&dri_rec->ri, &ri, sizeof(ri)); + memcpy(&dri_rec->found_key, &found_key, + sizeof(found_key)); + list_add_tail(&dri_rec->list, &dropping_trees); + } } path.slots[0]++; } btrfs_release_path(&path); - while(1) { + while (1) { ret = run_next_block(root, bits, bits_nr, &last, &pending, &seen, &reada, &nodes, &extent_cache, &chunk_cache, &dev_cache, - &block_group_cache, &dev_extent_cache); + &block_group_cache, &dev_extent_cache, + NULL); if (ret != 0) break; } + while (!list_empty(&dropping_trees)) { + struct dropping_root_item_record *rec; + struct extent_buffer *buf; + rec = list_entry(dropping_trees.next, + struct dropping_root_item_record, list); + last = 0; + if (!bits) { + perror("realloc"); + exit(1); + } + buf = read_tree_block(root->fs_info->tree_root, + btrfs_root_bytenr(&rec->ri), + btrfs_level_size(root, + btrfs_root_level(&rec->ri)), 0); + add_root_to_pending(buf, &extent_cache, &pending, + &seen, &nodes, &rec->found_key); + while (1) { + ret = run_next_block(root, bits, bits_nr, &last, + &pending, &seen, &reada, + &nodes, &extent_cache, + &chunk_cache, &dev_cache, + &block_group_cache, + &dev_extent_cache, + &rec->ri); + if (ret != 0) + break; + } + free_extent_buffer(buf); + list_del(&rec->list); + free(rec); + } + ret = check_extent_refs(trans, root, &extent_cache); if (ret == -EAGAIN) { ret = btrfs_commit_transaction(trans, root);