diff options
Diffstat (limited to 'check/mode-common.c')
-rw-r--r-- | check/mode-common.c | 147 |
1 files changed, 142 insertions, 5 deletions
diff --git a/check/mode-common.c b/check/mode-common.c index e857d44d..15e2bbd1 100644 --- a/check/mode-common.c +++ b/check/mode-common.c @@ -379,18 +379,14 @@ int insert_inode_item(struct btrfs_trans_handle *trans, time_t now = time(NULL); int ret; + memset(&ii, 0, sizeof(ii)); btrfs_set_stack_inode_size(&ii, size); btrfs_set_stack_inode_nbytes(&ii, nbytes); btrfs_set_stack_inode_nlink(&ii, nlink); btrfs_set_stack_inode_mode(&ii, mode); btrfs_set_stack_inode_generation(&ii, trans->transid); - btrfs_set_stack_timespec_nsec(&ii.atime, 0); btrfs_set_stack_timespec_sec(&ii.ctime, now); - btrfs_set_stack_timespec_nsec(&ii.ctime, 0); btrfs_set_stack_timespec_sec(&ii.mtime, now); - btrfs_set_stack_timespec_nsec(&ii.mtime, 0); - btrfs_set_stack_timespec_sec(&ii.otime, 0); - btrfs_set_stack_timespec_nsec(&ii.otime, 0); ret = btrfs_insert_inode(trans, root, ino, &ii); ASSERT(!ret); @@ -605,3 +601,144 @@ void reset_cached_block_groups(struct btrfs_fs_info *fs_info) start = cache->key.objectid + cache->key.offset; } } + +static int traverse_tree_blocks(struct btrfs_fs_info *fs_info, + struct extent_buffer *eb, int tree_root, + int pin) +{ + struct extent_buffer *tmp; + struct btrfs_root_item *ri; + struct btrfs_key key; + struct extent_io_tree *tree; + u64 bytenr; + int level = btrfs_header_level(eb); + int nritems; + int ret; + int i; + u64 end = eb->start + eb->len; + + if (pin) + tree = &fs_info->pinned_extents; + else + tree = fs_info->excluded_extents; + /* + * If we have pinned/excluded this block before, don't do it again. + * This can not only avoid forever loop with broken filesystem + * but also give us some speedups. + */ + if (test_range_bit(tree, eb->start, end - 1, EXTENT_DIRTY, 0)) + return 0; + + if (pin) + btrfs_pin_extent(fs_info, eb->start, eb->len); + else + set_extent_dirty(tree, eb->start, end - 1); + + nritems = btrfs_header_nritems(eb); + for (i = 0; i < nritems; i++) { + if (level == 0) { + bool is_extent_root; + btrfs_item_key_to_cpu(eb, &key, i); + if (key.type != BTRFS_ROOT_ITEM_KEY) + continue; + /* Skip the extent root and reloc roots */ + if (key.objectid == BTRFS_TREE_RELOC_OBJECTID || + key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID) + continue; + is_extent_root = + key.objectid == BTRFS_EXTENT_TREE_OBJECTID; + /* If pin, skip the extent root */ + if (pin && is_extent_root) + continue; + ri = btrfs_item_ptr(eb, i, struct btrfs_root_item); + bytenr = btrfs_disk_root_bytenr(eb, ri); + + /* + * If at any point we start needing the real root we + * will have to build a stump root for the root we are + * in, but for now this doesn't actually use the root so + * just pass in extent_root. + */ + tmp = read_tree_block(fs_info, bytenr, 0); + if (!extent_buffer_uptodate(tmp)) { + fprintf(stderr, "Error reading root block\n"); + return -EIO; + } + ret = traverse_tree_blocks(fs_info, tmp, 0, pin); + free_extent_buffer(tmp); + if (ret) + return ret; + } else { + bytenr = btrfs_node_blockptr(eb, i); + + /* If we aren't the tree root don't read the block */ + if (level == 1 && !tree_root) { + btrfs_pin_extent(fs_info, bytenr, + fs_info->nodesize); + continue; + } + + tmp = read_tree_block(fs_info, bytenr, 0); + if (!extent_buffer_uptodate(tmp)) { + fprintf(stderr, "Error reading tree block\n"); + return -EIO; + } + ret = traverse_tree_blocks(fs_info, tmp, tree_root, + pin); + free_extent_buffer(tmp); + if (ret) + return ret; + } + } + + return 0; +} + +static int pin_down_tree_blocks(struct btrfs_fs_info *fs_info, + struct extent_buffer *eb, int tree_root) +{ + return traverse_tree_blocks(fs_info, eb, tree_root, 1); +} + +int pin_metadata_blocks(struct btrfs_fs_info *fs_info) +{ + int ret; + + ret = pin_down_tree_blocks(fs_info, fs_info->chunk_root->node, 0); + if (ret) + return ret; + + return pin_down_tree_blocks(fs_info, fs_info->tree_root->node, 1); +} + +static int exclude_tree_blocks(struct btrfs_fs_info *fs_info, + struct extent_buffer *eb, int tree_root) +{ + return traverse_tree_blocks(fs_info, eb, tree_root, 0); +} + +int exclude_metadata_blocks(struct btrfs_fs_info *fs_info) +{ + int ret; + struct extent_io_tree *excluded_extents; + + excluded_extents = malloc(sizeof(*excluded_extents)); + if (!excluded_extents) + return -ENOMEM; + extent_io_tree_init(excluded_extents); + fs_info->excluded_extents = excluded_extents; + + ret = exclude_tree_blocks(fs_info, fs_info->chunk_root->node, 0); + if (ret) + return ret; + return exclude_tree_blocks(fs_info, fs_info->tree_root->node, 1); +} + +void cleanup_excluded_extents(struct btrfs_fs_info *fs_info) +{ + if (fs_info->excluded_extents) { + extent_io_tree_cleanup(fs_info->excluded_extents); + free(fs_info->excluded_extents); + } + fs_info->excluded_extents = NULL; +} |