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;