From 25db1dd11de2f255894896d691121795c60ee2bb Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jan 2015 13:53:02 +0800 Subject: btrfs-progs: Make csum tree rebuild works with extent tree rebuild. Before this patch, csum tree rebuild will not work with extent tree rebuild, since extent tree rebuild will only build up basic block groups, but csum tree rebuild needs data extents to rebuild. So if one use btrfsck with --init-csum-tree and --init-extent-tree, csum tree will be empty and tons of "missing csum" error will be outputted. This patch allows csum tree rebuild get its data from fs/subvol trees using regular file extents (which is also the only one using csum tree currently). Signed-off-by: Qu Wenruo [renamed to fill_csum_tree_from_one_fs_root] Signed-off-by: David Sterba --- cmds-check.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 155 insertions(+), 3 deletions(-) (limited to 'cmds-check.c') diff --git a/cmds-check.c b/cmds-check.c index 62aee830..ed8c6984 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -8622,8 +8622,142 @@ static int populate_csum(struct btrfs_trans_handle *trans, return ret; } -static int fill_csum_tree(struct btrfs_trans_handle *trans, - struct btrfs_root *csum_root) +static int fill_csum_tree_from_one_fs_root(struct btrfs_trans_handle *trans, + struct btrfs_root *csum_root, + struct btrfs_root *cur_root) +{ + struct btrfs_path *path; + struct btrfs_key key; + struct extent_buffer *node; + struct btrfs_file_extent_item *fi; + char *buf = NULL; + u64 start = 0; + u64 len = 0; + int slot = 0; + int ret = 0; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + buf = malloc(cur_root->fs_info->csum_root->sectorsize); + if (!buf) { + ret = -ENOMEM; + goto out; + } + + key.objectid = 0; + key.offset = 0; + key.type = 0; + + ret = btrfs_search_slot(NULL, cur_root, &key, path, 0, 0); + if (ret < 0) + goto out; + /* Iterate all regular file extents and fill its csum */ + while (1) { + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + + if (key.type != BTRFS_EXTENT_DATA_KEY) + goto next; + node = path->nodes[0]; + slot = path->slots[0]; + fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item); + if (btrfs_file_extent_type(node, fi) != BTRFS_FILE_EXTENT_REG) + goto next; + start = btrfs_file_extent_disk_bytenr(node, fi); + len = btrfs_file_extent_disk_num_bytes(node, fi); + + ret = populate_csum(trans, csum_root, buf, start, len); + if (ret == -EEXIST) + ret = 0; + if (ret < 0) + goto out; +next: + /* + * TODO: if next leaf is corrupted, jump to nearest next valid + * leaf. + */ + ret = btrfs_next_item(cur_root, path); + if (ret < 0) + goto out; + if (ret > 0) { + ret = 0; + goto out; + } + } + +out: + btrfs_free_path(path); + free(buf); + return ret; +} + +static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans, + struct btrfs_root *csum_root) +{ + struct btrfs_fs_info *fs_info = csum_root->fs_info; + struct btrfs_path *path; + struct btrfs_root *tree_root = fs_info->tree_root; + struct btrfs_root *cur_root; + struct extent_buffer *node; + struct btrfs_key key; + int slot = 0; + int ret = 0; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + key.objectid = BTRFS_FS_TREE_OBJECTID; + key.offset = 0; + key.type = BTRFS_ROOT_ITEM_KEY; + + ret = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0); + if (ret < 0) + goto out; + if (ret > 0) { + ret = -ENOENT; + goto out; + } + + while (1) { + node = path->nodes[0]; + slot = path->slots[0]; + btrfs_item_key_to_cpu(node, &key, slot); + if (key.objectid > BTRFS_LAST_FREE_OBJECTID) + goto out; + if (key.type != BTRFS_ROOT_ITEM_KEY) + goto next; + if (!is_fstree(key.objectid)) + goto next; + key.offset = (u64)-1; + + cur_root = btrfs_read_fs_root(fs_info, &key); + if (IS_ERR(cur_root) || !cur_root) { + fprintf(stderr, "Fail to read fs/subvol tree: %lld\n", + key.objectid); + goto out; + } + ret = fill_csum_tree_from_one_fs_root(trans, csum_root, + cur_root); + if (ret < 0) + goto out; +next: + ret = btrfs_next_item(tree_root, path); + if (ret > 0) { + ret = 0; + goto out; + } + if (ret < 0) + goto out; + } + +out: + btrfs_free_path(path); + return ret; +} + +static int fill_csum_tree_from_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *csum_root) { struct btrfs_root *extent_root = csum_root->fs_info->extent_root; struct btrfs_path *path; @@ -8691,6 +8825,23 @@ static int fill_csum_tree(struct btrfs_trans_handle *trans, return ret; } +/* + * Recalculate the csum and put it into the csum tree. + * + * Extent tree init will wipe out all the extent info, so in that case, we + * can't depend on extent tree, but use fs tree. If search_fs_tree is set, we + * will use fs/subvol trees to init the csum tree. + */ +static int fill_csum_tree(struct btrfs_trans_handle *trans, + struct btrfs_root *csum_root, + int search_fs_tree) +{ + if (search_fs_tree) + return fill_csum_tree_from_fs(trans, csum_root); + else + return fill_csum_tree_from_extent(trans, csum_root); +} + struct root_item_info { /* level of the root */ u8 level; @@ -9250,7 +9401,8 @@ int cmd_check(int argc, char **argv) goto close_out; } - ret = fill_csum_tree(trans, info->csum_root); + ret = fill_csum_tree(trans, info->csum_root, + init_extent_tree); if (ret) { fprintf(stderr, "crc refilling failed\n"); return -EIO; -- cgit v1.2.3