From patchwork Tue Nov 4 09:18:15 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qu Wenruo X-Patchwork-Id: 5224141 Return-Path: X-Original-To: patchwork-linux-btrfs@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 44BBF9F39B for ; Tue, 4 Nov 2014 09:18:19 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 90BF920173 for ; Tue, 4 Nov 2014 09:18:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E679020160 for ; Tue, 4 Nov 2014 09:18:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751292AbaKDJSL (ORCPT ); Tue, 4 Nov 2014 04:18:11 -0500 Received: from cn.fujitsu.com ([59.151.112.132]:23254 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1751162AbaKDJSJ (ORCPT ); Tue, 4 Nov 2014 04:18:09 -0500 X-IronPort-AV: E=Sophos;i="5.04,845,1406563200"; d="scan'208";a="42818628" Received: from localhost (HELO edo.cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 04 Nov 2014 17:14:56 +0800 Received: from G08CNEXCHPEKD02.g08.fujitsu.local (localhost.localdomain [127.0.0.1]) by edo.cn.fujitsu.com (8.14.3/8.13.1) with ESMTP id sA49HvG3021779 for ; Tue, 4 Nov 2014 17:17:57 +0800 Received: from localhost.localdomain (10.167.226.33) by G08CNEXCHPEKD02.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.181.6; Tue, 4 Nov 2014 17:18:10 +0800 From: Qu Wenruo To: Subject: [PATCH] btrfs-progs: Enhance btrfs-find-root. Date: Tue, 4 Nov 2014 17:18:15 +0800 Message-ID: <1415092695-26220-1-git-send-email-quwenruo@cn.fujitsu.com> X-Mailer: git-send-email 2.1.3 MIME-Version: 1.0 X-Originating-IP: [10.167.226.33] Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Spam-Status: No, score=-7.5 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Enhance btrfs-find-root in the following way: 1. Use existing or lightly modified btrfs infrastructure Don't use btrfs-find-root local defined open_ctree or csum check. Slightly modify open_ctree() and csum_tree_block() to provide the chunk-only open_ctree and suprress error output for csum_tree_block() 2. Output the trees in an ascending order of generation Normally, end user running btrfs-find-root is hoping to find out a recently new old root. So output the search result in an ascending order of generation. 3. Hide the obviously not tree root node/leaf Only output the node/leaf with the highest level among its generation to simplify the output. Signed-off-by: Qu Wenruo --- Documentation/btrfs-find-root.txt | 2 + btrfs-find-root.c | 434 ++++++++++++++++++-------------------- ctree.h | 2 + disk-io.c | 24 ++- disk-io.h | 15 +- 5 files changed, 234 insertions(+), 243 deletions(-) diff --git a/Documentation/btrfs-find-root.txt b/Documentation/btrfs-find-root.txt index c934b4c..7ff5239 100644 --- a/Documentation/btrfs-find-root.txt +++ b/Documentation/btrfs-find-root.txt @@ -22,6 +22,8 @@ Filter root tree by it's original transaction id, tree root's generation in defa Filter root tree by it's objectid,tree root's objectid in default. -l :: Filter root tree by B-+ tree's level, level 0 in default. +-a:: +Search for all root even the desired root is already founded. EXIT STATUS ----------- diff --git a/btrfs-find-root.c b/btrfs-find-root.c index 6fa61cc..33df9e1 100644 --- a/btrfs-find-root.c +++ b/btrfs-find-root.c @@ -35,267 +35,248 @@ #include "utils.h" #include "crc32c.h" -static u16 csum_size = 0; -static u64 search_objectid = BTRFS_ROOT_TREE_OBJECTID; -static u64 search_generation = 0; -static unsigned long search_level = 0; +/* + * find root result is a list like the following: + * + * result_list + * | + * gen_list <-> gen_list <-> gen_list ... + * gen:4 gen:5 gen:6 + * level:0 level:1 level:2 + * level_list level_list level_list + * |-l 0's eb |-l 1'eb(possible root) |-l 2'eb(possible root) + * |-l 0's eb + * ... + * level_list only contains the highest level's eb. + * if level_list only contains 1 eb, that may be root. + * if multiple, the root is already overwritten. + */ +struct eb_entry{ + struct list_head list; + struct extent_buffer *eb; +}; + +struct generation_entry{ + struct list_head gen_list; + struct list_head eb_list; + u64 generation; + u8 level; +}; + +struct search_filter { + u64 objectid; + u64 generation; + u64 level; + u64 super_gen; + int search_all; +}; static void usage(void) { fprintf(stderr, "Usage: find-roots [-o search_objectid] " - "[ -g search_generation ] [ -l search_level ] \n"); + "[ -g search_generation ] [ -l search_level ] [ -a ]" + "[ -s [+-]{objectid|generation} ] \n"); } -static int csum_block(void *buf, u32 len) +static inline void print_message(struct eb_entry *ebe, + struct btrfs_super_block *super) { - char *result; - u32 crc = ~(u32)0; - int ret = 0; - - result = malloc(csum_size * sizeof(char)); - if (!result) { - fprintf(stderr, "No memory\n"); - return 1; - } - - len -= BTRFS_CSUM_SIZE; - crc = crc32c(crc, buf + BTRFS_CSUM_SIZE, len); - btrfs_csum_final(crc, result); - - if (memcmp(buf, result, csum_size)) - ret = 1; - free(result); - return ret; + u64 generation = btrfs_header_generation(ebe->eb); + + if (generation != btrfs_super_generation(super)) + printf("Well block %llu seems great, but generation doesn't match, have=%llu, want=%llu level %u\n", + btrfs_header_bytenr(ebe->eb), + btrfs_header_generation(ebe->eb), + btrfs_super_generation(super), + btrfs_header_level(ebe->eb)); + else + printf("Found tree root at %llu gen %llu level %u\n", + btrfs_header_bytenr(ebe->eb), + btrfs_header_generation(ebe->eb), + btrfs_header_level(ebe->eb)); } -static struct btrfs_root *open_ctree_broken(int fd, const char *device) +static void print_result(struct btrfs_root *chunk_root, + struct list_head *result_list) { - struct btrfs_fs_info *fs_info; - struct btrfs_super_block *disk_super; - struct btrfs_fs_devices *fs_devices = NULL; - struct extent_buffer *eb; - int ret; - - fs_info = btrfs_new_fs_info(0, BTRFS_SUPER_INFO_OFFSET); - if (!fs_info) { - fprintf(stderr, "Failed to allocate memory for fs_info\n"); - return NULL; - } - - ret = btrfs_scan_fs_devices(fd, device, &fs_devices, 0, 1); - if (ret) - goto out; - - fs_info->fs_devices = fs_devices; - - ret = btrfs_open_devices(fs_devices, O_RDONLY); - if (ret) - goto out_devices; - - disk_super = fs_info->super_copy; - ret = btrfs_read_dev_super(fs_devices->latest_bdev, - disk_super, fs_info->super_bytenr, 1); - if (ret) { - printk("No valid btrfs found\n"); - goto out_devices; - } - - memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE); - - ret = btrfs_check_fs_compatibility(disk_super, 0); - if (ret) - goto out_devices; - - ret = btrfs_setup_chunk_tree_and_device_map(fs_info); - if (ret) - goto out_chunk; - - eb = fs_info->chunk_root->node; - read_extent_buffer(eb, fs_info->chunk_tree_uuid, - btrfs_header_chunk_tree_uuid(eb), BTRFS_UUID_SIZE); - - return fs_info->chunk_root; -out_chunk: - free_extent_buffer(fs_info->chunk_root->node); - btrfs_cleanup_all_caches(fs_info); -out_devices: - btrfs_close_devices(fs_info->fs_devices); -out: - btrfs_free_fs_info(fs_info); - return NULL; + struct btrfs_super_block *super = chunk_root->fs_info->super_copy; + struct eb_entry *ebe; + struct generation_entry *gene; + + printf("Super think's the tree root is at %llu, chunk root %llu\n", + btrfs_super_root(super), btrfs_super_chunk_root(super)); + list_for_each_entry(gene, result_list, gen_list) + list_for_each_entry(ebe, &gene->eb_list, list) + print_message(ebe, super); } -static int search_iobuf(struct btrfs_root *root, void *iobuf, - size_t iobuf_size, off_t offset) +static int add_eb_to_gen(struct extent_buffer *eb, + struct generation_entry *gene) { - u64 gen = search_generation; - u64 objectid = search_objectid; - u32 size = btrfs_super_nodesize(root->fs_info->super_copy); - u8 level = search_level; - size_t block_off = 0; - - while (block_off < iobuf_size) { - void *block = iobuf + block_off; - struct btrfs_header *header = block; - u64 h_byte, h_level, h_gen, h_owner; - -// printf("searching %Lu\n", offset + block_off); - h_byte = btrfs_stack_header_bytenr(header); - h_owner = btrfs_stack_header_owner(header); - h_level = header->level; - h_gen = btrfs_stack_header_generation(header); - - if (h_owner != objectid) - goto next; - if (h_byte != (offset + block_off)) - goto next; - if (h_level < level) - goto next; - level = h_level; - if (csum_block(block, size)) { - fprintf(stderr, "Well block %Lu seems good, " - "but the csum doesn't match\n", - h_byte); - goto next; + struct list_head *pos; + struct eb_entry *ebe; + struct eb_entry *n; + struct eb_entry *new; + u8 level = btrfs_header_level(eb); + u64 bytenr = btrfs_header_bytenr(eb); + + if (level < gene->level) + goto free_out; + + new = malloc(sizeof(*new)); + if (!new) + return -ENOMEM; + new->eb = eb; + + if (level > gene->level) { + gene->level = level; + list_for_each_entry_safe(ebe, n, &gene->eb_list, list) { + list_del(&ebe->list); + free_extent_buffer(ebe->eb); + free(ebe); } - if (h_gen != gen) { - fprintf(stderr, "Well block %Lu seems great, " - "but generation doesn't match, " - "have=%Lu, want=%Lu level %Lu\n", h_byte, - h_gen, gen, h_level); - goto next; - } - printf("Found tree root at %Lu gen %Lu level %Lu\n", h_byte, - h_gen, h_level); + list_add(&new->list, &gene->eb_list); return 0; -next: - block_off += size; } - - return 1; + list_for_each(pos, &gene->eb_list) { + ebe = list_entry(pos, struct eb_entry, list); + if (btrfs_header_bytenr(ebe->eb) > bytenr) { + pos = pos->prev; + break; + } + } + list_add_tail(&new->list, pos); + return 0; +free_out: + free_extent_buffer(eb); + return 0; } -static int read_physical(struct btrfs_root *root, int fd, u64 offset, - u64 bytenr, u64 len) +static int add_eb_to_result(struct extent_buffer *eb, + struct list_head *result_list, + struct search_filter *search) { - char *iobuf = malloc(len); - ssize_t done; - size_t total_read = 0; - int ret = 1; + struct list_head *pos; + struct generation_entry *gene; + struct generation_entry *new; + u64 generation = btrfs_header_generation(eb); + u64 level = btrfs_header_level(eb); + u64 owner = btrfs_header_owner(eb); + int found = 0; + int ret = 0; - if (!iobuf) { - fprintf(stderr, "No memory\n"); - return -1; - } + if (owner != search->objectid || level < search->level || + generation < search->generation) + goto free_out; - while (total_read < len) { - done = pread64(fd, iobuf + total_read, len - total_read, - bytenr + total_read); - if (done < 0) { - fprintf(stderr, "Failed to read: %s\n", - strerror(errno)); - ret = -1; - goto out; + list_for_each(pos, result_list) { + gene = list_entry(pos, struct generation_entry, gen_list); + if (gene->generation == generation) { + found = 1; + break; + } + if (gene->generation > generation) { + pos = pos->prev; + break; } - total_read += done; } - - ret = search_iobuf(root, iobuf, total_read, offset); -out: - free(iobuf); + if (found) { + ret = add_eb_to_gen(eb, gene); + } else { + new = malloc(sizeof(*new)); + if (!new) { + ret = -ENOMEM; + goto free_out; + } + new->generation = generation; + new->level = 0; + INIT_LIST_HEAD(&new->gen_list); + INIT_LIST_HEAD(&new->eb_list); + list_add_tail(&new->gen_list, pos); + ret = add_eb_to_gen(eb, new); + } + if (ret) + goto free_out; + if (generation == search->super_gen) + ret = 1; + return ret; +free_out: + free_extent_buffer(eb); return ret; } - -static int find_root(struct btrfs_root *root) +static int find_root(struct btrfs_root *chunk_root, + struct list_head *result_list, + struct search_filter *search) { - struct btrfs_multi_bio *multi = NULL; - struct btrfs_device *device; - u64 metadata_offset = 0, metadata_size = 0; - off_t offset = 0; - off_t bytenr; - int fd; - int err; - int ret = 1; - - printf("Super think's the tree root is at %Lu, chunk root %Lu\n", - btrfs_super_root(root->fs_info->super_copy), - btrfs_super_chunk_root(root->fs_info->super_copy)); - - err = btrfs_next_metadata(&root->fs_info->mapping_tree, - &metadata_offset, &metadata_size); - if (err) - return ret; + struct extent_buffer *eb; + struct btrfs_fs_info *fs_info = chunk_root->fs_info; + u64 metadata_offset = 0; + u64 metadata_size = 0; + u64 offset; + u64 leafsize = btrfs_super_leafsize(fs_info->super_copy); + int ret = 0; - offset = metadata_offset; while (1) { - u64 map_length = 4096; - u64 type; - - if (offset > - btrfs_super_total_bytes(root->fs_info->super_copy)) { - printf("Went past the fs size, exiting"); + ret = btrfs_next_metadata(&chunk_root->fs_info->mapping_tree, + &metadata_offset, &metadata_size); + if (ret) { + if (ret == -ENOENT) + ret = 0; break; } - if (offset >= (metadata_offset + metadata_size)) { - err = btrfs_next_metadata(&root->fs_info->mapping_tree, - &metadata_offset, - &metadata_size); - if (err) { - printf("No more metdata to scan, exiting\n"); - break; - } - offset = metadata_offset; - } - err = __btrfs_map_block(&root->fs_info->mapping_tree, READ, - offset, &map_length, &type, - &multi, 0, NULL); - if (err) { - offset += map_length; - continue; + for (offset = metadata_offset; + offset < metadata_offset + metadata_size; + offset += leafsize) { + eb = read_tree_block(chunk_root, offset, leafsize, 0); + if (!eb || IS_ERR(eb)) + continue; + ret = add_eb_to_result(eb, result_list, search); + if (ret) + return ret; } + } + return ret; +} - if (!(type & BTRFS_BLOCK_GROUP_METADATA)) { - offset += map_length; - kfree(multi); - continue; - } - - device = multi->stripes[0].dev; - fd = device->fd; - bytenr = multi->stripes[0].physical; - kfree(multi); - - err = read_physical(root, fd, offset, bytenr, map_length); - if (!err) { - ret = 0; - break; - } else if (err < 0) { - ret = err; - break; +static void free_result_list(struct list_head *result_list) +{ + struct eb_entry *ebe; + struct eb_entry *ebtmp; + struct generation_entry *gene; + struct generation_entry *gentmp; + + list_for_each_entry_safe(gene, gentmp, result_list, gen_list) { + list_for_each_entry_safe(ebe, ebtmp,&gene->eb_list, list) { + list_del(&ebe->list); + free_extent_buffer(ebe->eb); + free(ebe); } - offset += map_length; + list_del(&gene->gen_list); + free(gene); } - return ret; } int main(int argc, char **argv) { - struct btrfs_root *root; - int dev_fd; + struct btrfs_root *chunk_root; + struct list_head result_list; + struct search_filter search = {BTRFS_ROOT_TREE_OBJECTID, 0 ,0, 0}; int opt; int ret; - while ((opt = getopt(argc, argv, "l:o:g:")) != -1) { + while ((opt = getopt(argc, argv, "l:o:g:a")) != -1) { switch(opt) { case 'o': - search_objectid = arg_strtou64(optarg); + search.objectid = arg_strtou64(optarg); break; case 'g': - search_generation = arg_strtou64(optarg); + search.generation = arg_strtou64(optarg); break; case 'l': - search_level = arg_strtou64(optarg); + search.level = arg_strtou64(optarg); break; + case 'a': + search.search_all = 1; default: usage(); exit(1); @@ -304,30 +285,23 @@ int main(int argc, char **argv) set_argv0(argv); argc = argc - optind; - if (check_argc_min(argc, 1)) { + if (check_argc_exact(argc, 1)) { usage(); exit(1); } - dev_fd = open(argv[optind], O_RDONLY); - if (dev_fd < 0) { - fprintf(stderr, "Failed to open device %s\n", argv[optind]); - exit(1); - } - - root = open_ctree_broken(dev_fd, argv[optind]); - close(dev_fd); - - if (!root) { + chunk_root = open_ctree(argv[optind], 0, OPEN_CTREE_CHUNK_ONLY); + if (!chunk_root) { fprintf(stderr, "Open ctree failed\n"); exit(1); } - if (search_generation == 0) - search_generation = btrfs_super_generation(root->fs_info->super_copy); - - csum_size = btrfs_super_csum_size(root->fs_info->super_copy); - ret = find_root(root); - close_ctree(root); + search.super_gen = + btrfs_super_generation(chunk_root->fs_info->super_copy); + INIT_LIST_HEAD(&result_list); + ret = find_root(chunk_root, &result_list, &search); + print_result(chunk_root, &result_list); + free_result_list(&result_list); + close_ctree(chunk_root); return ret; } diff --git a/ctree.h b/ctree.h index 89036de..ee67404 100644 --- a/ctree.h +++ b/ctree.h @@ -995,6 +995,7 @@ struct btrfs_fs_info { unsigned int on_restoring:1; unsigned int is_chunk_recover:1; unsigned int quota_enabled:1; + unsigned int suppress_error:1; int (*free_extent_hook)(struct btrfs_trans_handle *trans, struct btrfs_root *root, @@ -1003,6 +1004,7 @@ struct btrfs_fs_info { int refs_to_drop); struct cache_tree *fsck_extent_cache; struct cache_tree *corrupt_blocks; + }; /* diff --git a/disk-io.c b/disk-io.c index 77fc610..4df4ef9 100644 --- a/disk-io.c +++ b/disk-io.c @@ -42,7 +42,8 @@ static int check_tree_block(struct btrfs_root *root, struct extent_buffer *buf) struct btrfs_fs_devices *fs_devices; int ret = 1; - if (buf->start != btrfs_header_bytenr(buf)) { + if (buf->start != btrfs_header_bytenr(buf) && + !root->fs_info->suppress_error) { printk("Check tree block failed, want=%Lu, have=%Lu\n", buf->start, btrfs_header_bytenr(buf)); return ret; @@ -118,6 +119,8 @@ int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf, { u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy); + if (root->fs_info->suppress_error) + return verify_tree_block_csum_silent(buf, csum_size); return csum_tree_block_size(buf, csum_size, verify); } @@ -282,10 +285,13 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, return eb; } if (ignore) { - if (check_tree_block(root, eb)) - printk("read block failed check_tree_block\n"); - else - printk("Csum didn't match\n"); + if (check_tree_block(root, eb)) { + if (!root->fs_info->suppress_error) + printk("read block failed check_tree_block\n"); + } else { + if (!root->fs_info->suppress_error) + printk("Csum didn't match\n"); + } break; } num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, @@ -1091,6 +1097,8 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path, } if (flags & OPEN_CTREE_RESTORE) fs_info->on_restoring = 1; + if (flags & OPEN_CTREE_CHUNK_ONLY) + fs_info->suppress_error = 1; ret = btrfs_scan_fs_devices(fp, path, &fs_devices, sb_bytenr, (flags & OPEN_CTREE_RECOVER_SUPER)); @@ -1138,7 +1146,7 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path, BTRFS_UUID_SIZE); ret = btrfs_setup_all_roots(fs_info, root_tree_bytenr, flags); - if (ret) + if (ret && !(flags & OPEN_CTREE_CHUNK_ONLY)) goto out_chunk; return fs_info; @@ -1183,6 +1191,8 @@ struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, info = open_ctree_fs_info(filename, sb_bytenr, 0, flags); if (!info) return NULL; + if (flags & OPEN_CTREE_CHUNK_ONLY) + return info->chunk_root; return info->fs_root; } @@ -1193,6 +1203,8 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, info = __open_ctree_fd(fp, path, sb_bytenr, 0, flags); if (!info) return NULL; + if (flags & OPEN_CTREE_CHUNK_ONLY) + return info->chunk_root; return info->fs_root; } diff --git a/disk-io.h b/disk-io.h index 4818109..7506a5a 100644 --- a/disk-io.h +++ b/disk-io.h @@ -26,13 +26,14 @@ #define BTRFS_SUPER_MIRROR_SHIFT 12 enum btrfs_open_ctree_flags { - OPEN_CTREE_WRITES = 1, - OPEN_CTREE_PARTIAL = 2, - OPEN_CTREE_BACKUP_ROOT = 4, - OPEN_CTREE_RECOVER_SUPER = 8, - OPEN_CTREE_RESTORE = 16, - OPEN_CTREE_NO_BLOCK_GROUPS = 32, - OPEN_CTREE_EXCLUSIVE = 64, + OPEN_CTREE_WRITES = (1 << 0), + OPEN_CTREE_PARTIAL = (1 << 1), + OPEN_CTREE_BACKUP_ROOT = (1 << 2), + OPEN_CTREE_RECOVER_SUPER = (1 << 3), + OPEN_CTREE_RESTORE = (1 << 4), + OPEN_CTREE_NO_BLOCK_GROUPS = (1 << 5), + OPEN_CTREE_EXCLUSIVE = (1 << 6), + OPEN_CTREE_CHUNK_ONLY = (1 << 7), }; static inline u64 btrfs_sb_offset(int mirror)