@@ -9193,6 +9193,182 @@ out:
return ret;
}
+static int reset_one_root_csum(struct btrfs_root *root)
+{
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ struct btrfs_path *path;
+ struct btrfs_key key;
+ struct extent_buffer *node;
+ u32 sectorsize = root->sectorsize;
+ int max_level = btrfs_root_level(&root->root_item);
+ int slot;
+ int i;
+ int ret = 0;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+ path->reada = 0;
+
+ /* Iterate all levels except level 0 */
+ for (i = 0; i < max_level; i++) {
+ int cur_level = max_level - i;
+
+ path->lowest_level = cur_level;
+ key.offset = 0;
+ key.objectid = 0;
+ key.type = 0;
+
+ /*
+ * btrfs_search_slot() can't work with lowest_level and cow,
+ * so use inslen=0 and cow=0 and later modify will be done
+ * directly to disk.
+ */
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ if (ret < 0)
+ goto out;
+
+ /* Iterate all node slots in this level */
+ while (1) {
+ u64 bytenr;
+
+ node = path->nodes[0];
+ slot = path->slots[0];
+ bytenr = btrfs_node_blockptr(node, slot);
+
+ if (bytenr != round_down(bytenr, sectorsize)) {
+ bytenr = round_down(bytenr, sectorsize);
+ btrfs_set_node_blockptr(node, slot, bytenr);
+ ret = write_tree_block(NULL, root, node);
+ if (ret < 0) {
+ fprintf(stderr,
+ "Fail to write extent at %llu\n",
+ bytenr);
+ goto out;
+ }
+ }
+ ret = reset_tree_block_csum(fs_info, bytenr,
+ root->nodesize);
+ if (ret < 0) {
+ fprintf(stderr,
+ "Fail to reset csum for tree block at %llu\n",
+ bytenr);
+ goto out;
+ }
+
+ ret = btrfs_next_slot(root, path, cur_level);
+ /*
+ * Error should not happen since higher level iteration
+ * has already reset the csum of this level.
+ * Either way, goto next level should be OK.
+ */
+ if (ret)
+ break;
+ }
+ btrfs_release_path(path);
+ }
+out:
+ btrfs_free_path(path);
+ return ret;
+}
+
+static int reset_roots_csum(struct btrfs_root *tree_root)
+{
+ struct btrfs_fs_info *fs_info = tree_root->fs_info;
+ struct btrfs_key key;
+ struct btrfs_path *path;
+ struct btrfs_root_item *ri;
+ struct btrfs_root *root;
+ struct extent_buffer *node;
+ u32 nodesize = tree_root->nodesize;
+ u32 sectorsize = tree_root->sectorsize;
+ u64 bytenr;
+ int slot;
+ int ret = 0;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ key.objectid = 0;
+ key.offset = 0;
+ key.type = 0;
+
+ /*
+ * Tree root is OK and we can do cow. But in case extent tree is
+ * corrupted, we still use the nocow method.
+ */
+ ret = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0);
+ if (ret < 0)
+ goto out;
+ while (1) {
+ slot = path->slots[0];
+ node = path->nodes[0];
+ btrfs_item_key_to_cpu(node, &key, slot);
+ if (key.type != BTRFS_ROOT_ITEM_KEY)
+ goto next;
+
+ /*
+ * skip tree reloc tree, it's not support by
+ * btrfs_read_fs_root() yet.
+ */
+ if (key.objectid == BTRFS_TREE_RELOC_OBJECTID)
+ goto next;
+
+ ri = btrfs_item_ptr(node, slot, struct btrfs_root_item);
+ bytenr = btrfs_disk_root_bytenr(node, ri);
+
+ /*
+ * Check if the bytenr is aligned.
+ * If the error bit is only in the low all zero bits,
+ * no real damage will happen since we will align it.
+ */
+ if (round_down(bytenr, sectorsize) != bytenr) {
+ bytenr = round_down(bytenr, sectorsize);
+ btrfs_set_disk_root_bytenr(node, ri, bytenr);
+ ret = write_tree_block(NULL, root, node);
+ if (ret < 0) {
+ fprintf(stderr,
+ "Fail to write extent at %llu\n",
+ bytenr);
+ goto out;
+ }
+ }
+
+ ret = reset_tree_block_csum(fs_info, bytenr, nodesize);
+ if (ret < 0) {
+ fprintf(stderr,
+ "Failed to reset root for tree %llu, skip it\n",
+ key.objectid);
+ goto next;
+ }
+ key.offset = (u64)-1;
+ root = btrfs_read_fs_root(fs_info, &key);
+ if (!root || IS_ERR(root) ||
+ !extent_buffer_uptodate(root->node)) {
+ fprintf(stderr,
+ "Root of tree %lld is still corrupted, skip it\n",
+ key.objectid);
+ goto next;
+ }
+ ret = reset_one_root_csum(root);
+ if (ret < 0)
+ goto next;
+
+next:
+ ret = btrfs_next_item(tree_root, path);
+ if (ret < 0)
+ goto out;
+ if (ret > 0) {
+ ret = 0;
+ goto out;
+ }
+ }
+out:
+ btrfs_free_path(path);
+ return ret;
+}
+
const char * const cmd_check_usage[] = {
"btrfs check [options] <device>",
"Check an unmounted btrfs filesystem.",
New function reset_one_root_csum() will reset all csum in one root. And reset_roots_csum() will reset all csum of all trees in tree root. which provides the basis for later dangerous options. Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com> --- cmds-check.c | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+)