[6/7] btrfs-progs: Introduce new function reset_(one_root/roots)_csum() to reset one/all tree's csum in tree root.
diff mbox

Message ID 1423034213-14018-9-git-send-email-quwenruo@cn.fujitsu.com
State New, archived
Headers show

Commit Message

Qu Wenruo Feb. 4, 2015, 7:16 a.m. UTC
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(+)

Patch
diff mbox

diff --git a/cmds-check.c b/cmds-check.c
index e4b4f4a..535a518 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -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.",