From bed7475d77c4891abeb457a8f0b08353501a907c Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 15 Dec 2010 16:03:00 -0500 Subject: Print the root generation in btrfs-debug-tree Signed-off-by: Chris Mason --- print-tree.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/print-tree.c b/print-tree.c index 59c23c52..60396996 100644 --- a/print-tree.c +++ b/print-tree.c @@ -504,11 +504,12 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) case BTRFS_ROOT_ITEM_KEY: ri = btrfs_item_ptr(l, i, struct btrfs_root_item); read_extent_buffer(l, &root_item, (unsigned long)ri, sizeof(root_item)); - printf("\t\troot data bytenr %llu level %d dirid %llu refs %u\n", + printf("\t\troot data bytenr %llu level %d dirid %llu refs %u gen %llu\n", (unsigned long long)btrfs_root_bytenr(&root_item), btrfs_root_level(&root_item), (unsigned long long)btrfs_root_dirid(&root_item), - btrfs_root_refs(&root_item)); + btrfs_root_refs(&root_item), + (unsigned long long)btrfs_root_generation(&root_item)); if (btrfs_root_refs(&root_item) == 0) { struct btrfs_key drop_key; btrfs_disk_key_to_cpu(&drop_key, -- cgit v1.2.3 From be826706b5c0fcca3bdeff6934cd8d46d046ddfe Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 26 Aug 2011 09:51:36 -0400 Subject: btrfs-progs: add a recovery utility to pull files from damanged filesystems Signed-off-by: Josef Bacik Signed-off-by: Chris Mason --- .gitignore | 15 ++ Makefile | 16 +- calc-size.c | 268 ++++++++++++++++++ ctree.c | 10 +- ctree.h | 16 ++ disk-io.c | 222 +++++++++++---- disk-io.h | 2 + extent-tree.c | 13 + extent_io.c | 1 - find-root.c | 458 +++++++++++++++++++++++++++++++ restore.c | 854 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ volumes.c | 57 +++- volumes.h | 5 + 13 files changed, 1867 insertions(+), 70 deletions(-) create mode 100644 .gitignore create mode 100644 calc-size.c create mode 100644 find-root.c create mode 100644 restore.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0e560d51 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.o +.*.o.d +version.h +man/*.gz +btrfs +btrfs-debug-tree +btrfs-map-logical +btrfs-show +btrfs-vol +btrfsck +btrfsctl +find-root +mkfs.btrfs +repair +restore diff --git a/Makefile b/Makefile index edee1a0e..e85c7266 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC = gcc AM_CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 -CFLAGS = -g -Werror -Os +CFLAGS = -g -Os objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ root-tree.o dir-item.o file-item.o inode-item.o \ inode-map.o crc32c.o rbtree.o extent-cache.o extent_io.o \ @@ -13,10 +13,11 @@ DEPFLAGS = -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@ INSTALL = install prefix ?= /usr/local bindir = $(prefix)/bin -LIBS = -luuid +LIBS=-luuid +RESTORE_LIBS=-lz progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \ - btrfs btrfs-map-logical + btrfs btrfs-map-logical restore find-root calc-size # make C=1 to enable sparse ifdef C @@ -39,6 +40,15 @@ btrfs: $(objects) btrfs.o btrfs_cmds.o scrub.o $(CC) -lpthread $(CFLAGS) -o btrfs btrfs.o btrfs_cmds.o scrub.o \ $(objects) $(LDFLAGS) $(LIBS) +calc-size: $(objects) calc-size.o + gcc $(CFLAGS) -o calc-size calc-size.o $(objects) $(LDFLAGS) $(LIBS) + +find-root: $(objects) find-root.o + gcc $(CFLAGS) -o find-root find-root.o $(objects) $(LDFLAGS) $(LIBS) + +restore: $(objects) restore.o + gcc $(CFLAGS) -o restore restore.o $(objects) $(LDFLAGS) $(LIBS) $(RESTORE_LIBS) + btrfsctl: $(objects) btrfsctl.o $(CC) $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS) diff --git a/calc-size.c b/calc-size.c new file mode 100644 index 00000000..c4adfb0c --- /dev/null +++ b/calc-size.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2011 Red Hat. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#define _XOPEN_SOURCE 500 +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "disk-io.h" +#include "print-tree.h" +#include "transaction.h" +#include "list.h" +#include "version.h" +#include "volumes.h" +#include "utils.h" + +static int verbose = 0; +static int no_pretty = 0; + +struct root_stats { + u64 total_nodes; + u64 total_leaves; + u64 total_bytes; + u64 total_inline; + int total_levels; +}; + +struct fs_root { + struct btrfs_key key; + struct btrfs_key *snaps; +}; + +static int walk_leaf(struct btrfs_root *root, struct btrfs_path *path, + struct root_stats *stat, int find_inline) +{ + struct extent_buffer *b = path->nodes[0]; + struct btrfs_file_extent_item *fi; + struct btrfs_key found_key; + int i; + + stat->total_bytes += root->leafsize; + stat->total_leaves++; + + if (!find_inline) + return 0; + + for (i = 0; i < btrfs_header_nritems(b); i++) { + btrfs_item_key_to_cpu(b, &found_key, i); + if (found_key.type != BTRFS_EXTENT_DATA_KEY) + continue; + + fi = btrfs_item_ptr(b, i, struct btrfs_file_extent_item); + if (btrfs_file_extent_type(b, fi) == BTRFS_FILE_EXTENT_INLINE) + stat->total_inline += + btrfs_file_extent_inline_item_len(b, + btrfs_item_nr(b, i)); + } + + return 0; +} + +static int walk_nodes(struct btrfs_root *root, struct btrfs_path *path, + struct root_stats *stat, int level, int find_inline) +{ + struct extent_buffer *b = path->nodes[level]; + int i; + int ret = 0; + + stat->total_bytes += root->nodesize; + stat->total_nodes++; + + for (i = 0; i < btrfs_header_nritems(b); i++) { + struct extent_buffer *tmp = NULL; + + path->slots[level] = i; + if ((level - 1) > 0 || find_inline) { + tmp = read_tree_block(root, btrfs_node_blockptr(b, i), + btrfs_level_size(root, level - 1), + btrfs_node_ptr_generation(b, i)); + if (!tmp) { + fprintf(stderr, "Failed to read blocknr %Lu\n", + btrfs_node_blockptr(b, i)); + continue; + } + path->nodes[level - 1] = tmp; + } + if (level - 1) + ret = walk_nodes(root, path, stat, level - 1, + find_inline); + else + ret = walk_leaf(root, path, stat, find_inline); + free_extent_buffer(tmp); + if (ret) { + fprintf(stderr, "Error walking down path\n"); + break; + } + } + + return ret; +} + +static int calc_root_size(struct btrfs_root *tree_root, struct btrfs_key *key, + int find_inline) +{ + struct btrfs_root *root; + struct btrfs_path *path; + struct root_stats stat; + int level; + int ret = 0; + int size_fail = 0; + + root = btrfs_read_fs_root(tree_root->fs_info, key); + if (!root) { + fprintf(stderr, "Failed to read root %Lu\n", key->objectid); + return 1; + } + + path = btrfs_alloc_path(); + if (!path) { + fprintf(stderr, "Could not allocate path\n"); + return 1; + } + + memset(&stat, 0, sizeof(stat)); + level = btrfs_header_level(root->node); + path->nodes[level] = root->node; + if (!level) { + ret = walk_leaf(root, path, &stat, find_inline); + if (ret) + goto out; + goto out_print; + } + + ret = walk_nodes(root, path, &stat, level, find_inline); + if (ret) + goto out; +out_print: + if (no_pretty || size_fail) { + printf("\t%Lu total bytes, %Lu inline data bytes, %Lu nodes, " + "%Lu leaves, %d levels\n", stat.total_bytes, + stat.total_inline, stat.total_nodes, stat.total_leaves, + level + 1); + } else { + char *total_size; + char *inline_size; + + total_size = pretty_sizes(stat.total_bytes); + inline_size = pretty_sizes(stat.total_inline); + + printf("\t%s total size, %s inline data, %Lu nodes, " + "%Lu leaves, %d levels\n", + total_size, inline_size, stat.total_nodes, + stat.total_leaves, level + 1); + free(total_size); + free(inline_size); + } +out: + btrfs_free_path(path); + return ret; +} + +static void usage() +{ + fprintf(stderr, "Usage: calc-size [-v] [-b] \n"); +} + +int main(int argc, char **argv) +{ + struct btrfs_key key; + struct fs_root *roots; + struct btrfs_root *root; + size_t fs_roots_size = sizeof(struct fs_root); + int opt; + int ret = 0; + + while ((opt = getopt(argc, argv, "vb")) != -1) { + switch (opt) { + case 'v': + verbose++; + break; + case 'b': + no_pretty = 1; + break; + default: + usage(); + exit(1); + } + } + + if (optind >= argc) { + usage(); + exit(1); + } + + /* + if ((ret = check_mounted(argv[optind])) < 0) { + fprintf(stderr, "Could not check mount status: %d\n", ret); + if (ret == -EACCES) + fprintf(stderr, "Maybe you need to run as root?\n"); + return ret; + } else if (ret) { + fprintf(stderr, "%s is currently mounted. Aborting.\n", + argv[optind]); + return -EBUSY; + } + */ + + root = open_ctree(argv[optind], 0, 0); + if (!root) { + fprintf(stderr, "Couldn't open ctree\n"); + exit(1); + } + + roots = malloc(fs_roots_size); + if (!roots) { + fprintf(stderr, "No memory\n"); + goto out; + } + + printf("Calculating size of root tree\n"); + key.objectid = BTRFS_ROOT_TREE_OBJECTID; + ret = calc_root_size(root, &key, 0); + if (ret) + goto out; + + printf("Calculating size of extent tree\n"); + key.objectid = BTRFS_EXTENT_TREE_OBJECTID; + ret = calc_root_size(root, &key, 0); + if (ret) + goto out; + + printf("Calculating size of csum tree\n"); + key.objectid = BTRFS_CSUM_TREE_OBJECTID; + ret = calc_root_size(root, &key, 0); + if (ret) + goto out; + + roots[0].key.objectid = BTRFS_FS_TREE_OBJECTID; + roots[0].key.offset = (u64)-1; + printf("Calculatin' size of fs tree\n"); + ret = calc_root_size(root, &roots[0].key, 1); + if (ret) + goto out; +out: + close_ctree(root); + return ret; +} diff --git a/ctree.c b/ctree.c index 12f1a55e..005550f1 100644 --- a/ctree.c +++ b/ctree.c @@ -765,7 +765,7 @@ static int bin_search(struct extent_buffer *eb, struct btrfs_key *key, return -1; } -static struct extent_buffer *read_node_slot(struct btrfs_root *root, +struct extent_buffer *read_node_slot(struct btrfs_root *root, struct extent_buffer *parent, int slot) { int level = btrfs_header_level(parent); @@ -1092,7 +1092,7 @@ static int noinline push_nodes_for_insert(struct btrfs_trans_handle *trans, /* * readahead one full node of leaves */ -static void reada_for_search(struct btrfs_root *root, struct btrfs_path *path, +void reada_for_search(struct btrfs_root *root, struct btrfs_path *path, int level, int slot, u64 objectid) { struct extent_buffer *node; @@ -1189,7 +1189,7 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root u8 lowest_level = 0; lowest_level = p->lowest_level; - WARN_ON(lowest_level && ins_len); + WARN_ON(lowest_level && ins_len > 0); WARN_ON(p->nodes[0] != NULL); /* WARN_ON(!mutex_is_locked(&root->fs_info->fs_mutex)); @@ -2915,6 +2915,8 @@ int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path) reada_for_search(root, path, level, slot, 0); next = read_node_slot(root, c, slot); + if (!next) + return -EIO; break; } path->slots[level] = slot; @@ -2929,6 +2931,8 @@ int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path) if (path->reada) reada_for_search(root, path, level, 0, 0); next = read_node_slot(root, next, 0); + if (!next) + return -EIO; } return 0; } diff --git a/ctree.h b/ctree.h index 6e1b80b7..b1a87c8e 100644 --- a/ctree.h +++ b/ctree.h @@ -79,6 +79,15 @@ struct btrfs_trans_handle; */ #define BTRFS_EXTENT_CSUM_OBJECTID -10ULL +/* For storing free space cache */ +#define BTRFS_FREE_SPACE_OBJECTID -11ULL + +/* + * The inode number assigned to the special inode for sotring + * free ino cache + */ +#define BTRFS_FREE_INO_OBJECTID -12ULL + /* dummy objectid represents multiple objectids */ #define BTRFS_MULTIPLE_OBJECTIDS -255ULL @@ -290,6 +299,9 @@ struct btrfs_header { #define BTRFS_MAX_INLINE_DATA_SIZE(r) (BTRFS_LEAF_DATA_SIZE(r) - \ sizeof(struct btrfs_item) - \ sizeof(struct btrfs_file_extent_item)) +#define BTRFS_MAX_XATTR_SIZE(r) (BTRFS_LEAF_DATA_SIZE(r) - \ + sizeof(struct btrfs_item) -\ + sizeof(struct btrfs_dir_item)) /* @@ -1735,6 +1747,10 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 bytenr, u64 num, int alloc, int mark_free); /* ctree.c */ +void reada_for_search(struct btrfs_root *root, struct btrfs_path *path, + int level, int slot, u64 objectid); +struct extent_buffer *read_node_slot(struct btrfs_root *root, + struct extent_buffer *parent, int slot); int btrfs_previous_item(struct btrfs_root *root, struct btrfs_path *path, u64 min_objectid, int type); diff --git a/disk-io.c b/disk-io.c index aed624ce..8429e53a 100644 --- a/disk-io.c +++ b/disk-io.c @@ -35,14 +35,19 @@ #include "utils.h" #include "print-tree.h" +static int close_all_devices(struct btrfs_fs_info *fs_info); + 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)) { + printk("Check tree block failed, want=%Lu, have=%Lu\n", + buf->start, btrfs_header_bytenr(buf)); return ret; + } fs_devices = root->fs_info->fs_devices; while (fs_devices) { @@ -147,7 +152,8 @@ int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize, } static int verify_parent_transid(struct extent_io_tree *io_tree, - struct extent_buffer *eb, u64 parent_transid) + struct extent_buffer *eb, u64 parent_transid, + int ignore) { int ret; @@ -163,6 +169,11 @@ static int verify_parent_transid(struct extent_io_tree *io_tree, (unsigned long long)eb->start, (unsigned long long)parent_transid, (unsigned long long)btrfs_header_generation(eb)); + if (ignore) { + printk("Ignoring transid failure\n"); + return 0; + } + ret = 1; out: clear_extent_buffer_uptodate(io_tree, eb); @@ -177,10 +188,13 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, int ret; struct extent_buffer *eb; u64 length; + u64 best_transid = 0; struct btrfs_multi_bio *multi = NULL; struct btrfs_device *device; int mirror_num = 0; + int good_mirror = 0; int num_copies; + int ignore = 0; eb = btrfs_find_create_tree_block(root, bytenr, blocksize); if (!eb) @@ -193,7 +207,10 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, while (1) { ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, eb->start, &length, &multi, mirror_num); - BUG_ON(ret); + if (ret) { + printk("Couldn't map the block %Lu\n", bytenr); + break; + } device = multi->stripes[0].dev; eb->fd = device->fd; device->total_ios++; @@ -203,18 +220,33 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, if (ret == 0 && check_tree_block(root, eb) == 0 && csum_tree_block(root, eb, 1) == 0 && - verify_parent_transid(eb->tree, eb, parent_transid) == 0) { + verify_parent_transid(eb->tree, eb, parent_transid, ignore) + == 0) { btrfs_set_buffer_uptodate(eb); 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"); + break; + } num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, eb->start, eb->len); if (num_copies == 1) { - break; + ignore = 1; + continue; + } + if (btrfs_header_generation(eb) > best_transid) { + best_transid = btrfs_header_generation(eb); + good_mirror = mirror_num; } mirror_num++; if (mirror_num > num_copies) { - break; + mirror_num = good_mirror; + ignore = 1; + continue; } } free_extent_buffer(eb); @@ -234,6 +266,7 @@ int write_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, if (!btrfs_buffer_uptodate(eb, trans->transid)) BUG(); + printf("writing out a block\n"); btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN); csum_tree_block(root, eb, 0); @@ -361,6 +394,7 @@ static int __commit_transaction(struct btrfs_trans_handle *trans, int btrfs_commit_transaction(struct btrfs_trans_handle *trans, struct btrfs_root *root) { + u64 transid = trans->transid; int ret = 0; struct btrfs_fs_info *fs_info = root->fs_info; @@ -388,6 +422,7 @@ commit_tree: free_extent_buffer(root->commit_root); root->commit_root = NULL; fs_info->running_transaction = NULL; + fs_info->last_trans_committed = transid; return 0; } @@ -567,28 +602,8 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info, return root; } -struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes) -{ - int fp; - struct btrfs_root *root; - int flags = O_CREAT | O_RDWR; - - if (!writes) - flags = O_RDONLY; - - fp = open(filename, flags, 0600); - if (fp < 0) { - fprintf (stderr, "Could not open %s\n", filename); - return NULL; - } - root = open_ctree_fd(fp, filename, sb_bytenr, writes); - close(fp); - - return root; -} - -struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, - int writes) +struct btrfs_root *__open_ctree_fd(int fp, const char *path, u64 sb_bytenr, + u64 root_tree_bytenr, int writes) { u32 sectorsize; u32 nodesize; @@ -617,12 +632,13 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, if (ret) { fprintf(stderr, "No valid Btrfs found on %s\n", path); - return NULL; + goto out; } if (total_devs != 1) { ret = btrfs_scan_for_fsid(fs_devices, total_devs, 1); - BUG_ON(ret); + if (ret) + goto out; } memset(fs_info, 0, sizeof(*fs_info)); @@ -657,7 +673,8 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, ret = btrfs_open_devices(fs_devices, O_RDWR); else ret = btrfs_open_devices(fs_devices, O_RDONLY); - BUG_ON(ret); + if (ret) + goto out_cleanup; fs_info->super_bytenr = sb_bytenr; disk_super = &fs_info->super_copy; @@ -665,7 +682,7 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, disk_super, sb_bytenr); if (ret) { printk("No valid btrfs found\n"); - BUG_ON(1); + goto out_devices; } memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE); @@ -677,7 +694,7 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, printk("couldn't open because of unsupported " "option features (%Lx).\n", (unsigned long long)features); - BUG_ON(1); + goto out_devices; } features = btrfs_super_incompat_flags(disk_super); @@ -692,7 +709,7 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, printk("couldn't open RDWR because of unsupported " "option features (%Lx).\n", (unsigned long long)features); - BUG_ON(1); + goto out_devices; } nodesize = btrfs_super_nodesize(disk_super); @@ -705,7 +722,8 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, tree_root->stripesize = stripesize; ret = btrfs_read_sys_array(tree_root); - BUG_ON(ret); + if (ret) + goto out_devices; blocksize = btrfs_level_size(tree_root, btrfs_super_chunk_root_level(disk_super)); generation = btrfs_super_chunk_root_generation(disk_super); @@ -716,8 +734,10 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, chunk_root->node = read_tree_block(chunk_root, btrfs_super_chunk_root(disk_super), blocksize, generation); - - BUG_ON(!chunk_root->node); + if (!chunk_root->node) { + printk("Couldn't read chunk root\n"); + goto out_devices; + } read_extent_buffer(chunk_root->node, fs_info->chunk_tree_uuid, (unsigned long)btrfs_header_chunk_tree_uuid(chunk_root->node), @@ -725,37 +745,51 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, if (!(btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP)) { ret = btrfs_read_chunk_tree(chunk_root); - BUG_ON(ret); + if (ret) + goto out_chunk; } blocksize = btrfs_level_size(tree_root, btrfs_super_root_level(disk_super)); generation = btrfs_super_generation(disk_super); + if (!root_tree_bytenr) + root_tree_bytenr = btrfs_super_root(disk_super); tree_root->node = read_tree_block(tree_root, - btrfs_super_root(disk_super), + root_tree_bytenr, blocksize, generation); - BUG_ON(!tree_root->node); + if (!tree_root->node) { + printk("Couldn't read tree root\n"); + goto out_chunk; + } ret = find_and_setup_root(tree_root, fs_info, BTRFS_EXTENT_TREE_OBJECTID, extent_root); - BUG_ON(ret); + if (ret) { + printk("Couldn't setup extent tree\n"); + goto out_tree; + } extent_root->track_dirty = 1; ret = find_and_setup_root(tree_root, fs_info, BTRFS_DEV_TREE_OBJECTID, dev_root); - BUG_ON(ret); + if (ret) { + printk("Couldn't setup device tree\n"); + goto out_extent; + } dev_root->track_dirty = 1; ret = find_and_setup_root(tree_root, fs_info, BTRFS_CSUM_TREE_OBJECTID, csum_root); - BUG_ON(ret); + if (ret) { + printk("Couldn't setup csum tree\n"); + goto out_dev; + } csum_root->track_dirty = 1; - BUG_ON(ret); - find_and_setup_log_root(tree_root, fs_info, disk_super); - fs_info->generation = generation + 1; + fs_info->generation = generation; + fs_info->last_trans_committed = generation; btrfs_read_block_groups(fs_info->tree_root); key.objectid = BTRFS_FS_TREE_OBJECTID; @@ -763,11 +797,84 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, key.offset = (u64)-1; fs_info->fs_root = btrfs_read_fs_root(fs_info, &key); + if (!fs_info->fs_root) + goto out_csum; + fs_info->data_alloc_profile = (u64)-1; fs_info->metadata_alloc_profile = (u64)-1; fs_info->system_alloc_profile = fs_info->metadata_alloc_profile; return fs_info->fs_root; +out_csum: + free_extent_buffer(fs_info->csum_root->node); +out_dev: + free_extent_buffer(fs_info->dev_root->node); +out_extent: + free_extent_buffer(fs_info->extent_root->node); +out_tree: + free_extent_buffer(fs_info->tree_root->node); +out_chunk: + free_extent_buffer(fs_info->chunk_root->node); +out_devices: + close_all_devices(fs_info); +out_cleanup: + extent_io_tree_cleanup(&fs_info->extent_cache); + extent_io_tree_cleanup(&fs_info->free_space_cache); + extent_io_tree_cleanup(&fs_info->block_group_cache); + extent_io_tree_cleanup(&fs_info->pinned_extents); + extent_io_tree_cleanup(&fs_info->pending_del); + extent_io_tree_cleanup(&fs_info->extent_ins); +out: + free(tree_root); + free(extent_root); + free(chunk_root); + free(dev_root); + free(csum_root); + free(fs_info); + return NULL; +} + +struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes) +{ + int fp; + struct btrfs_root *root; + int flags = O_CREAT | O_RDWR; + + if (!writes) + flags = O_RDONLY; + + fp = open(filename, flags, 0600); + if (fp < 0) { + fprintf (stderr, "Could not open %s\n", filename); + return NULL; + } + root = __open_ctree_fd(fp, filename, sb_bytenr, 0, writes); + close(fp); + + return root; +} + +struct btrfs_root *open_ctree_recovery(const char *filename, u64 sb_bytenr, + u64 root_tree_bytenr) +{ + int fp; + struct btrfs_root *root; + + fp = open(filename, O_RDONLY); + if (fp < 0) { + fprintf (stderr, "Could not open %s\n", filename); + return NULL; + } + root = __open_ctree_fd(fp, filename, sb_bytenr, root_tree_bytenr, 0); + close(fp); + + return root; +} + +struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, + int writes) +{ + return __open_ctree_fd(fp, path, sb_bytenr, 0, writes); } int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr) @@ -945,15 +1052,18 @@ int close_ctree(struct btrfs_root *root) struct btrfs_trans_handle *trans; struct btrfs_fs_info *fs_info = root->fs_info; - trans = btrfs_start_transaction(root, 1); - btrfs_commit_transaction(trans, root); - trans = btrfs_start_transaction(root, 1); - ret = commit_tree_roots(trans, fs_info); - BUG_ON(ret); - ret = __commit_transaction(trans, root); - BUG_ON(ret); - write_ctree_super(trans, root); - btrfs_free_transaction(root, trans); + if (fs_info->last_trans_committed != + fs_info->generation) { + trans = btrfs_start_transaction(root, 1); + btrfs_commit_transaction(trans, root); + trans = btrfs_start_transaction(root, 1); + ret = commit_tree_roots(trans, fs_info); + BUG_ON(ret); + ret = __commit_transaction(trans, root); + BUG_ON(ret); + write_ctree_super(trans, root); + btrfs_free_transaction(root, trans); + } btrfs_free_block_groups(fs_info); free_fs_roots(fs_info); @@ -1018,7 +1128,7 @@ int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid) if (!ret) return ret; - ret = verify_parent_transid(buf->tree, buf, parent_transid); + ret = verify_parent_transid(buf->tree, buf, parent_transid, 1); return !ret; } diff --git a/disk-io.h b/disk-io.h index 7ebec24e..2048fcfc 100644 --- a/disk-io.h +++ b/disk-io.h @@ -46,6 +46,8 @@ int clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes); struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, int writes); +struct btrfs_root *open_ctree_recovery(const char *filename, u64 sb_bytenr, + u64 root_tree_bytenr); int close_ctree(struct btrfs_root *root); int write_all_supers(struct btrfs_root *root); int write_ctree_super(struct btrfs_trans_handle *trans, diff --git a/extent-tree.c b/extent-tree.c index b5b7aaa5..5bed3c27 100644 --- a/extent-tree.c +++ b/extent-tree.c @@ -1039,6 +1039,13 @@ static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans, err = ret; goto out; } + if (ret) { + printf("Failed to find [%llu, %u, %llu]\n", key.objectid, key.type, key.offset); + btrfs_print_leaf(root, path->nodes[0]); + btrfs_free_path(path); + return -ENOENT; + } + BUG_ON(ret); leaf = path->nodes[0]; @@ -1059,6 +1066,12 @@ static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans, item_size = btrfs_item_size_nr(leaf, path->slots[0]); } #endif + if (item_size < sizeof(*ei)) { + printf("Size is %u, needs to be %u, slot %d\n", item_size, + sizeof(*ei), path->slots[0]); + btrfs_print_leaf(root, leaf); + return -EINVAL; + } BUG_ON(item_size < sizeof(*ei)); ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); diff --git a/extent_io.c b/extent_io.c index 8f0a8763..973e9181 100644 --- a/extent_io.c +++ b/extent_io.c @@ -654,7 +654,6 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree, if (cache) { eb = container_of(cache, struct extent_buffer, cache_node); - BUG_ON(eb->refs != 1); free_extent_buffer(eb); } eb = __alloc_extent_buffer(tree, bytenr, blocksize); diff --git a/find-root.c b/find-root.c new file mode 100644 index 00000000..c0f38b88 --- /dev/null +++ b/find-root.c @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2011 Red Hat. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#define _XOPEN_SOURCE 500 +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "disk-io.h" +#include "print-tree.h" +#include "transaction.h" +#include "list.h" +#include "version.h" +#include "volumes.h" +#include "utils.h" +#include "crc32c.h" + +static int verbose = 0; +static u16 csum_size = 0; +static u64 search_objectid = BTRFS_ROOT_TREE_OBJECTID; + +static void usage() +{ + fprintf(stderr, "Usage: find-roots [-v] \n"); +} + +int csum_block(void *buf, u32 len) +{ + 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; +} + +static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, + u32 stripesize, struct btrfs_root *root, + struct btrfs_fs_info *fs_info, u64 objectid) +{ + root->node = NULL; + root->commit_root = NULL; + root->sectorsize = sectorsize; + root->nodesize = nodesize; + root->leafsize = leafsize; + root->stripesize = stripesize; + root->ref_cows = 0; + root->track_dirty = 0; + + root->fs_info = fs_info; + root->objectid = objectid; + root->last_trans = 0; + root->highest_inode = 0; + root->last_inode_alloc = 0; + + INIT_LIST_HEAD(&root->dirty_list); + memset(&root->root_key, 0, sizeof(root->root_key)); + memset(&root->root_item, 0, sizeof(root->root_item)); + root->root_key.objectid = objectid; + return 0; +} + +static int close_all_devices(struct btrfs_fs_info *fs_info) +{ + struct list_head *list; + struct list_head *next; + struct btrfs_device *device; + + return 0; + + list = &fs_info->fs_devices->devices; + list_for_each(next, list) { + device = list_entry(next, struct btrfs_device, dev_list); + close(device->fd); + } + return 0; +} + +static struct btrfs_root *open_ctree_broken(int fd, const char *device) +{ + u32 sectorsize; + u32 nodesize; + u32 leafsize; + u32 blocksize; + u32 stripesize; + u64 generation; + struct btrfs_root *tree_root = malloc(sizeof(struct btrfs_root)); + struct btrfs_root *extent_root = malloc(sizeof(struct btrfs_root)); + struct btrfs_root *chunk_root = malloc(sizeof(struct btrfs_root)); + struct btrfs_root *dev_root = malloc(sizeof(struct btrfs_root)); + struct btrfs_root *csum_root = malloc(sizeof(struct btrfs_root)); + struct btrfs_fs_info *fs_info = malloc(sizeof(*fs_info)); + int ret; + struct btrfs_super_block *disk_super; + struct btrfs_fs_devices *fs_devices = NULL; + u64 total_devs; + u64 features; + + ret = btrfs_scan_one_device(fd, device, &fs_devices, + &total_devs, BTRFS_SUPER_INFO_OFFSET); + + if (ret) { + fprintf(stderr, "No valid Btrfs found on %s\n", device); + goto out; + } + + if (total_devs != 1) { + ret = btrfs_scan_for_fsid(fs_devices, total_devs, 1); + if (ret) + goto out; + } + + memset(fs_info, 0, sizeof(*fs_info)); + fs_info->tree_root = tree_root; + fs_info->extent_root = extent_root; + fs_info->chunk_root = chunk_root; + fs_info->dev_root = dev_root; + fs_info->csum_root = csum_root; + + fs_info->readonly = 1; + + extent_io_tree_init(&fs_info->extent_cache); + extent_io_tree_init(&fs_info->free_space_cache); + extent_io_tree_init(&fs_info->block_group_cache); + extent_io_tree_init(&fs_info->pinned_extents); + extent_io_tree_init(&fs_info->pending_del); + extent_io_tree_init(&fs_info->extent_ins); + cache_tree_init(&fs_info->fs_root_cache); + + cache_tree_init(&fs_info->mapping_tree.cache_tree); + + mutex_init(&fs_info->fs_mutex); + fs_info->fs_devices = fs_devices; + INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots); + INIT_LIST_HEAD(&fs_info->space_info); + + __setup_root(4096, 4096, 4096, 4096, tree_root, + fs_info, BTRFS_ROOT_TREE_OBJECTID); + + ret = btrfs_open_devices(fs_devices, O_RDONLY); + if (ret) + goto out_cleanup; + + fs_info->super_bytenr = BTRFS_SUPER_INFO_OFFSET; + disk_super = &fs_info->super_copy; + ret = btrfs_read_dev_super(fs_devices->latest_bdev, + disk_super, BTRFS_SUPER_INFO_OFFSET); + if (ret) { + printk("No valid btrfs found\n"); + goto out_devices; + } + + memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE); + + + features = btrfs_super_incompat_flags(disk_super) & + ~BTRFS_FEATURE_INCOMPAT_SUPP; + if (features) { + printk("couldn't open because of unsupported " + "option features (%Lx).\n", features); + goto out_devices; + } + + features = btrfs_super_incompat_flags(disk_super); + if (!(features & BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF)) { + features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF; + btrfs_set_super_incompat_flags(disk_super, features); + } + + nodesize = btrfs_super_nodesize(disk_super); + leafsize = btrfs_super_leafsize(disk_super); + sectorsize = btrfs_super_sectorsize(disk_super); + stripesize = btrfs_super_stripesize(disk_super); + tree_root->nodesize = nodesize; + tree_root->leafsize = leafsize; + tree_root->sectorsize = sectorsize; + tree_root->stripesize = stripesize; + + ret = btrfs_read_sys_array(tree_root); + if (ret) + goto out_devices; + blocksize = btrfs_level_size(tree_root, + btrfs_super_chunk_root_level(disk_super)); + generation = btrfs_super_chunk_root_generation(disk_super); + + __setup_root(nodesize, leafsize, sectorsize, stripesize, + chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID); + + chunk_root->node = read_tree_block(chunk_root, + btrfs_super_chunk_root(disk_super), + blocksize, generation); + if (!chunk_root->node) { + printk("Couldn't read chunk root\n"); + goto out_devices; + } + + read_extent_buffer(chunk_root->node, fs_info->chunk_tree_uuid, + (unsigned long)btrfs_header_chunk_tree_uuid(chunk_root->node), + BTRFS_UUID_SIZE); + + if (!(btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP)) { + ret = btrfs_read_chunk_tree(chunk_root); + if (ret) + goto out_chunk; + } + + return fs_info->chunk_root; +out_chunk: + free_extent_buffer(fs_info->chunk_root->node); +out_devices: + close_all_devices(fs_info); +out_cleanup: + extent_io_tree_cleanup(&fs_info->extent_cache); + extent_io_tree_cleanup(&fs_info->free_space_cache); + extent_io_tree_cleanup(&fs_info->block_group_cache); + extent_io_tree_cleanup(&fs_info->pinned_extents); + extent_io_tree_cleanup(&fs_info->pending_del); + extent_io_tree_cleanup(&fs_info->extent_ins); +out: + free(tree_root); + free(extent_root); + free(chunk_root); + free(dev_root); + free(csum_root); + free(fs_info); + return NULL; +} + +static int search_iobuf(struct btrfs_root *root, void *iobuf, + size_t iobuf_size, off_t offset) +{ + u64 gen = btrfs_super_generation(&root->fs_info->super_copy); + u64 objectid = search_objectid; + u32 size = btrfs_super_nodesize(&root->fs_info->super_copy); + u8 level = root->fs_info->super_copy.root_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 = le64_to_cpu(header->bytenr); + h_owner = le64_to_cpu(header->owner); + h_level = header->level; + h_gen = le64_to_cpu(header->generation); + + if (h_owner != objectid) + goto next; + if (h_byte != (offset + block_off)) + goto next; + if (h_level != level) + goto next; + if (csum_block(block, size)) { + fprintf(stderr, "Well block %Lu seems good, " + "but the csum doesn't match\n", + h_byte); + goto next; + } + if (h_gen != gen) { + fprintf(stderr, "Well block %Lu seems great, " + "but generation doesn't match, " + "have=%Lu, want=%Lu\n", h_byte, h_gen, + gen); + goto next; + } + printf("Found tree root at %Lu\n", h_byte); + return 0; +next: + block_off += size; + } + + return 1; +} + +static int read_physical(struct btrfs_root *root, int fd, u64 offset, + u64 bytenr, u64 len) +{ + char *iobuf = malloc(len); + ssize_t done; + size_t total_read = 0; + int ret = 1; + + if (!iobuf) { + fprintf(stderr, "No memory\n"); + return -1; + } + + 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; + } + total_read += done; + } + + ret = search_iobuf(root, iobuf, total_read, offset); +out: + free(iobuf); + return ret; +} + +static int find_root(struct btrfs_root *root) +{ + 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; + + 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"); + 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); + if (err) { + offset += map_length; + continue; + } + + if (!(type & BTRFS_BLOCK_GROUP_METADATA)) { + offset += map_length; + 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; + } + offset += map_length; + } + return ret; +} + +int main(int argc, char **argv) +{ + struct btrfs_root *root; + int dev_fd; + int opt; + int ret; + + while ((opt = getopt(argc, argv, "vo:")) != -1) { + switch(opt) { + case 'v': + verbose++; + break; + case 'o': + errno = 0; + search_objectid = (u64)strtoll(optarg, NULL, + 10); + if (errno) { + fprintf(stderr, "Error parsing " + "objectid\n"); + exit(1); + } + break; + default: + usage(); + exit(1); + } + } + + if (optind >= argc) { + 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) + exit(1); + + csum_size = btrfs_super_csum_size(&root->fs_info->super_copy); + ret = find_root(root); + close_ctree(root); + return ret; +} diff --git a/restore.c b/restore.c new file mode 100644 index 00000000..c20f4e1b --- /dev/null +++ b/restore.c @@ -0,0 +1,854 @@ +/* + * Copyright (C) 2011 Red Hat. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#define _XOPEN_SOURCE 500 +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "disk-io.h" +#include "print-tree.h" +#include "transaction.h" +#include "list.h" +#include "version.h" +#include "volumes.h" +#include "utils.h" + +static char path_name[4096]; +static int get_snaps = 0; +static int verbose = 0; +static int ignore_errors = 0; +static int overwrite = 0; + +static int decompress(char *inbuf, char *outbuf, u64 compress_len, + u64 decompress_len) +{ + z_stream strm; + int ret; + + memset(&strm, 0, sizeof(strm)); + ret = inflateInit(&strm); + if (ret != Z_OK) { + fprintf(stderr, "inflate init returnd %d\n", ret); + return -1; + } + + strm.avail_in = compress_len; + strm.next_in = (unsigned char *)inbuf; + strm.avail_out = decompress_len; + strm.next_out = (unsigned char *)outbuf; + ret = inflate(&strm, Z_NO_FLUSH); + if (ret != Z_STREAM_END) { + (void)inflateEnd(&strm); + fprintf(stderr, "ret is %d\n", ret); + return -1; + } + + (void)inflateEnd(&strm); + return 0; +} + +int next_leaf(struct btrfs_root *root, struct btrfs_path *path) +{ + int slot; + int level = 1; + struct extent_buffer *c; + struct extent_buffer *next = NULL; + + for (; level < BTRFS_MAX_LEVEL; level++) { + if (path->nodes[level]) + break; + } + + if (level == BTRFS_MAX_LEVEL) + return 1; + + slot = path->slots[level] + 1; + + while(level < BTRFS_MAX_LEVEL) { + if (!path->nodes[level]) + return 1; + + slot = path->slots[level] + 1; + c = path->nodes[level]; + if (slot >= btrfs_header_nritems(c)) { + level++; + if (level == BTRFS_MAX_LEVEL) + return 1; + continue; + } + + if (next) + free_extent_buffer(next); + + if (path->reada) + reada_for_search(root, path, level, slot, 0); + + next = read_node_slot(root, c, slot); + break; + } + path->slots[level] = slot; + while(1) { + level--; + c = path->nodes[level]; + free_extent_buffer(c); + path->nodes[level] = next; + path->slots[level] = 0; + if (!level) + break; + if (path->reada) + reada_for_search(root, path, level, 0, 0); + next = read_node_slot(root, next, 0); + } + return 0; +} + +static int copy_one_inline(int fd, struct btrfs_path *path, u64 pos) +{ + struct extent_buffer *leaf = path->nodes[0]; + struct btrfs_file_extent_item *fi; + char buf[4096]; + char *outbuf; + ssize_t done; + unsigned long ptr; + int ret; + int len; + int ram_size; + int compress; + + fi = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_file_extent_item); + ptr = btrfs_file_extent_inline_start(fi); + len = btrfs_file_extent_inline_item_len(leaf, + btrfs_item_nr(leaf, path->slots[0])); + read_extent_buffer(leaf, buf, ptr, len); + + compress = btrfs_file_extent_compression(leaf, fi); + if (compress == BTRFS_COMPRESS_NONE) { + done = pwrite(fd, buf, len, pos); + if (done < len) { + fprintf(stderr, "Short inline write, wanted %d, did " + "%zd: %d\n", len, done, errno); + return -1; + } + return 0; + } + + ram_size = btrfs_file_extent_ram_bytes(leaf, fi); + outbuf = malloc(ram_size); + if (!outbuf) { + fprintf(stderr, "No memory\n"); + return -1; + } + + ret = decompress(buf, outbuf, len, ram_size); + if (ret) { + free(outbuf); + return ret; + } + + done = pwrite(fd, outbuf, ram_size, pos); + free(outbuf); + if (done < len) { + fprintf(stderr, "Short compressed inline write, wanted %d, " + "did %zd: %d\n", ram_size, done, errno); + return -1; + } + + return 0; +} + +static int copy_one_extent(struct btrfs_root *root, int fd, + struct extent_buffer *leaf, + struct btrfs_file_extent_item *fi, u64 pos) +{ + struct btrfs_multi_bio *multi = NULL; + struct btrfs_device *device; + char *inbuf, *outbuf = NULL; + ssize_t done, total = 0; + u64 bytenr; + u64 ram_size; + u64 disk_size; + u64 length; + u64 size_left; + u64 dev_bytenr; + u64 count = 0; + int compress; + int ret; + int dev_fd; + + compress = btrfs_file_extent_compression(leaf, fi); + bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); + disk_size = btrfs_file_extent_disk_num_bytes(leaf, fi); + ram_size = btrfs_file_extent_ram_bytes(leaf, fi); + size_left = disk_size; + + inbuf = malloc(disk_size); + if (!inbuf) { + fprintf(stderr, "No memory\n"); + return -1; + } + + if (compress != BTRFS_COMPRESS_NONE) { + outbuf = malloc(ram_size); + if (!outbuf) { + fprintf(stderr, "No memory\n"); + free(inbuf); + return -1; + } + } +again: + length = size_left; + ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, + bytenr, &length, &multi, 0); + if (ret) { + free(inbuf); + free(outbuf); + fprintf(stderr, "Error mapping block %d\n", ret); + return ret; + } + device = multi->stripes[0].dev; + dev_fd = device->fd; + device->total_ios++; + dev_bytenr = multi->stripes[0].physical; + kfree(multi); + + if (size_left < length) + length = size_left; + size_left -= length; + + done = pread(dev_fd, inbuf+count, length, dev_bytenr); + if (done < length) { + free(inbuf); + free(outbuf); + fprintf(stderr, "Short read %d\n", errno); + return -1; + } + + count += length; + bytenr += length; + if (size_left) + goto again; + + + if (compress == BTRFS_COMPRESS_NONE) { + while (total < ram_size) { + done = pwrite(fd, inbuf+total, ram_size-total, + pos+total); + if (done < 0) { + free(inbuf); + fprintf(stderr, "Error writing: %d\n", errno); + return -1; + } + total += done; + } + free(inbuf); + return 0; + } + + ret = decompress(inbuf, outbuf, disk_size, ram_size); + free(inbuf); + if (ret) { + free(outbuf); + return ret; + } + + while (total < ram_size) { + done = pwrite(fd, outbuf+total, ram_size-total, pos+total); + if (done < 0) { + free(outbuf); + fprintf(stderr, "Error writing: %d\n", errno); + return -1; + } + total += done; + } + free(outbuf); + + return 0; +} + +static int ask_to_continue(const char *file) +{ + char buf[2]; + char *ret; + + printf("We seem to be looping a lot on %s, do you want to keep going " + "on ? (y/N): ", file); +again: + ret = fgets(buf, 2, stdin); + if (*ret == '\n' || tolower(*ret) == 'n') + return 1; + if (tolower(*ret) != 'y') { + printf("Please enter either 'y' or 'n': "); + goto again; + } + + return 0; +} + + +static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, + const char *file) +{ + struct extent_buffer *leaf; + struct btrfs_path *path; + struct btrfs_file_extent_item *fi; + struct btrfs_key found_key; + int ret; + int extent_type; + int compression; + int loops = 0; + + path = btrfs_alloc_path(); + if (!path) { + fprintf(stderr, "Ran out of memory\n"); + return -1; + } + path->skip_locking = 1; + + key->offset = 0; + key->type = BTRFS_EXTENT_DATA_KEY; + + ret = btrfs_search_slot(NULL, root, key, path, 0, 0); + if (ret < 0) { + fprintf(stderr, "Error searching %d\n", ret); + btrfs_free_path(path); + return ret; + } + + leaf = path->nodes[0]; + while (!leaf) { + ret = next_leaf(root, path); + if (ret < 0) { + fprintf(stderr, "Error getting next leaf %d\n", + ret); + btrfs_free_path(path); + return ret; + } else if (ret > 0) { + /* No more leaves to search */ + btrfs_free_path(path); + return 0; + } + leaf = path->nodes[0]; + } + + while (1) { + if (loops++ >= 1024) { + ret = ask_to_continue(file); + if (ret) + break; + loops = 0; + } + if (path->slots[0] >= btrfs_header_nritems(leaf)) { + do { + ret = next_leaf(root, path); + if (ret < 0) { + fprintf(stderr, "Error searching %d\n", ret); + btrfs_free_path(path); + return ret; + } else if (ret) { + /* No more leaves to search */ + btrfs_free_path(path); + return 0; + } + leaf = path->nodes[0]; + } while (!leaf); + continue; + } + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + if (found_key.objectid != key->objectid) + break; + if (found_key.type != key->type) + break; + fi = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_file_extent_item); + extent_type = btrfs_file_extent_type(leaf, fi); + compression = btrfs_file_extent_compression(leaf, fi); + if (compression >= BTRFS_COMPRESS_LAST) { + fprintf(stderr, "Don't support compression yet %d\n", + compression); + btrfs_free_path(path); + return -1; + } + + if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) + goto next; + if (extent_type == BTRFS_FILE_EXTENT_INLINE) { + ret = copy_one_inline(fd, path, found_key.offset); + if (ret) { + btrfs_free_path(path); + return -1; + } + } else if (extent_type == BTRFS_FILE_EXTENT_REG) { + ret = copy_one_extent(root, fd, leaf, fi, + found_key.offset); + if (ret) { + btrfs_free_path(path); + return ret; + } + } else { + printf("Weird extent type %d\n", extent_type); + } +next: + path->slots[0]++; + } + + btrfs_free_path(path); + return 0; +} + +static int search_dir(struct btrfs_root *root, struct btrfs_key *key, + const char *dir) +{ + struct btrfs_path *path; + struct extent_buffer *leaf; + struct btrfs_dir_item *dir_item; + struct btrfs_key found_key, location; + char filename[BTRFS_NAME_LEN + 1]; + unsigned long name_ptr; + int name_len; + int ret; + int fd; + int loops = 0; + u8 type; + + path = btrfs_alloc_path(); + if (!path) { + fprintf(stderr, "Ran out of memory\n"); + return -1; + } + path->skip_locking = 1; + + key->offset = 0; + key->type = BTRFS_DIR_INDEX_KEY; + + ret = btrfs_search_slot(NULL, root, key, path, 0, 0); + if (ret < 0) { + fprintf(stderr, "Error searching %d\n", ret); + btrfs_free_path(path); + return ret; + } + + leaf = path->nodes[0]; + while (!leaf) { + if (verbose > 1) + printf("No leaf after search, looking for the next " + "leaf\n"); + ret = next_leaf(root, path); + if (ret < 0) { + fprintf(stderr, "Error getting next leaf %d\n", + ret); + btrfs_free_path(path); + return ret; + } else if (ret > 0) { + /* No more leaves to search */ + if (verbose) + printf("Reached the end of the tree looking " + "for the directory\n"); + btrfs_free_path(path); + return 0; + } + leaf = path->nodes[0]; + } + + while (leaf) { + if (loops++ >= 1024) { + printf("We have looped trying to restore files in %s " + "too many times to be making progress, " + "stopping\n", dir); + break; + } + + if (path->slots[0] >= btrfs_header_nritems(leaf)) { + do { + ret = next_leaf(root, path); + if (ret < 0) { + fprintf(stderr, "Error searching %d\n", + ret); + btrfs_free_path(path); + return ret; + } else if (ret > 0) { + /* No more leaves to search */ + if (verbose) + printf("Reached the end of " + "the tree searching the" + " directory\n"); + btrfs_free_path(path); + return 0; + } + leaf = path->nodes[0]; + } while (!leaf); + continue; + } + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + if (found_key.objectid != key->objectid) { + if (verbose > 1) + printf("Found objectid=%Lu, key=%Lu\n", + found_key.objectid, key->objectid); + break; + } + if (found_key.type != key->type) { + if (verbose > 1) + printf("Found type=%u, want=%u\n", + found_key.type, key->type); + break; + } + dir_item = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_dir_item); + name_ptr = (unsigned long)(dir_item + 1); + name_len = btrfs_dir_name_len(leaf, dir_item); + read_extent_buffer(leaf, filename, name_ptr, name_len); + filename[name_len] = '\0'; + type = btrfs_dir_type(leaf, dir_item); + btrfs_dir_item_key_to_cpu(leaf, dir_item, &location); + + snprintf(path_name, 4096, "%s/%s", dir, filename); + + + /* + * At this point we're only going to restore directories and + * files, no symlinks or anything else. + */ + if (type == BTRFS_FT_REG_FILE) { + if (!overwrite) { + static int warn = 0; + struct stat st; + + ret = stat(path_name, &st); + if (!ret) { + loops = 0; + if (verbose || !warn) + printf("Skipping existing file" + " %s\n", path_name); + if (warn) + goto next; + printf("If you wish to overwrite use " + "the -o option to overwrite\n"); + warn = 1; + goto next; + } + ret = 0; + } + if (verbose) + printf("Restoring %s\n", path_name); + fd = open(path_name, O_CREAT|O_WRONLY, 0644); + if (fd < 0) { + fprintf(stderr, "Error creating %s: %d\n", + path_name, errno); + if (ignore_errors) + goto next; + btrfs_free_path(path); + return -1; + } + loops = 0; + ret = copy_file(root, fd, &location, path_name); + close(fd); + if (ret) { + if (ignore_errors) + goto next; + btrfs_free_path(path); + return ret; + } + } else if (type == BTRFS_FT_DIR) { + struct btrfs_root *search_root = root; + char *dir = strdup(path_name); + + if (!dir) { + fprintf(stderr, "Ran out of memory\n"); + btrfs_free_path(path); + return -1; + } + + if (location.type == BTRFS_ROOT_ITEM_KEY) { + /* + * If we are a snapshot and this is the index + * object to ourselves just skip it. + */ + if (location.objectid == + root->root_key.objectid) { + free(dir); + goto next; + } + + search_root = btrfs_read_fs_root(root->fs_info, + &location); + if (IS_ERR(search_root)) { + free(dir); + fprintf(stderr, "Error reading " + "subvolume %s: %lu\n", + path_name, + PTR_ERR(search_root)); + if (ignore_errors) + goto next; + return PTR_ERR(search_root); + } + + /* + * A subvolume will have a key.offset of 0, a + * snapshot will have key.offset of a transid. + */ + if (search_root->root_key.offset != 0 && + get_snaps == 0) { + free(dir); + printf("Skipping snapshot %s\n", + filename); + goto next; + } + location.objectid = BTRFS_FIRST_FREE_OBJECTID; + } + + if (verbose) + printf("Restoring %s\n", path_name); + + errno = 0; + ret = mkdir(path_name, 0755); + if (ret && errno != EEXIST) { + free(dir); + fprintf(stderr, "Error mkdiring %s: %d\n", + path_name, errno); + if (ignore_errors) + goto next; + btrfs_free_path(path); + return -1; + } + loops = 0; + ret = search_dir(search_root, &location, dir); + free(dir); + if (ret) { + if (ignore_errors) + goto next; + btrfs_free_path(path); + return ret; + } + } +next: + path->slots[0]++; + } + + if (verbose) + printf("Done searching %s\n", dir); + btrfs_free_path(path); + return 0; +} + +static void usage() +{ + fprintf(stderr, "Usage: restore [-svio] [-t disk offset] " + "\n"); +} + +static struct btrfs_root *open_fs(const char *dev, u64 root_location, int super_mirror) +{ + struct btrfs_root *root; + u64 bytenr; + int i; + + for (i = super_mirror; i < BTRFS_SUPER_MIRROR_MAX; i++) { + bytenr = btrfs_sb_offset(i); + root = open_ctree_recovery(dev, bytenr, root_location); + if (root) + return root; + fprintf(stderr, "Could not open root, trying backup super\n"); + } + + return NULL; +} + +static int find_first_dir(struct btrfs_root *root, u64 *objectid) +{ + struct btrfs_path *path; + struct btrfs_key found_key; + struct btrfs_key key; + int ret = -1; + int i; + + key.objectid = 0; + key.type = BTRFS_DIR_INDEX_KEY; + key.offset = 0; + + path = btrfs_alloc_path(); + if (!path) { + fprintf(stderr, "Ran out of memory\n"); + goto out; + } + + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) { + fprintf(stderr, "Error searching %d\n", ret); + goto out; + } + + if (!path->nodes[0]) { + fprintf(stderr, "No leaf!\n"); + goto out; + } +again: + for (i = path->slots[0]; + i < btrfs_header_nritems(path->nodes[0]); i++) { + btrfs_item_key_to_cpu(path->nodes[0], &found_key, i); + if (found_key.type != key.type) + continue; + + printf("Using objectid %Lu for first dir\n", + found_key.objectid); + *objectid = found_key.objectid; + ret = 0; + goto out; + } + do { + ret = next_leaf(root, path); + if (ret < 0) { + fprintf(stderr, "Error getting next leaf %d\n", + ret); + goto out; + } else if (ret > 0) { + fprintf(stderr, "No more leaves\n"); + goto out; + } + } while (!path->nodes[0]); + if (path->nodes[0]) + goto again; + printf("Couldn't find a dir index item\n"); +out: + btrfs_free_path(path); + return ret; +} + +int main(int argc, char **argv) +{ + struct btrfs_root *root; + struct btrfs_key key; + char dir_name[128]; + u64 tree_location = 0; + u64 fs_location = 0; + int len; + int ret; + int opt; + int super_mirror = 0; + int find_dir = 0; + + while ((opt = getopt(argc, argv, "sviot:u:df:")) != -1) { + switch (opt) { + case 's': + get_snaps = 1; + break; + case 'v': + verbose++; + break; + case 'i': + ignore_errors = 1; + break; + case 'o': + overwrite = 1; + break; + case 't': + errno = 0; + tree_location = (u64)strtoll(optarg, NULL, 10); + if (errno != 0) { + fprintf(stderr, "Tree location not valid\n"); + exit(1); + } + break; + case 'f': + errno = 0; + fs_location = (u64)strtoll(optarg, NULL, 10); + if (errno != 0) { + fprintf(stderr, "Fs location not valid\n"); + exit(1); + } + break; + case 'u': + errno = 0; + super_mirror = (int)strtol(optarg, NULL, 10); + if (errno != 0 || + super_mirror >= BTRFS_SUPER_MIRROR_MAX) { + fprintf(stderr, "Super mirror not " + "valid\n"); + exit(1); + } + break; + case 'd': + find_dir = 1; + break; + default: + usage(); + exit(1); + } + } + + if (optind + 1 >= argc) { + usage(); + exit(1); + } + + if ((ret = check_mounted(argv[optind])) < 0) { + fprintf(stderr, "Could not check mount status: %s\n", + strerror(ret)); + return ret; + } else if (ret) { + fprintf(stderr, "%s is currently mounted. Aborting.\n", argv[optind + 1]); + return -EBUSY; + } + + root = open_fs(argv[optind], tree_location, super_mirror); + if (root == NULL) + return 1; + + if (fs_location != 0) { + free_extent_buffer(root->node); + root->node = read_tree_block(root, fs_location, 4096, 0); + if (!root->node) { + fprintf(stderr, "Failed to read fs location\n"); + goto out; + } + } + + printf("Root objectid is %Lu\n", root->objectid); + + memset(path_name, 0, 4096); + + strncpy(dir_name, argv[optind + 1], 128); + + /* Strip the trailing / on the dir name */ + while (1) { + len = strlen(dir_name); + if (dir_name[len - 1] != '/') + break; + dir_name[len - 1] = '\0'; + } + + if (find_dir) { + ret = find_first_dir(root, &key.objectid); + if (ret) + goto out; + } else { + key.objectid = BTRFS_FIRST_FREE_OBJECTID; + } + + ret = search_dir(root->fs_info->fs_root, &key, dir_name); + +out: + close_ctree(root); + return ret; +} diff --git a/volumes.c b/volumes.c index 37f68df0..03bfb8cc 100644 --- a/volumes.c +++ b/volumes.c @@ -982,6 +982,30 @@ int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len) return ret; } +int btrfs_next_metadata(struct btrfs_mapping_tree *map_tree, u64 *logical, + u64 *size) +{ + struct cache_extent *ce; + struct map_lookup *map; + + ce = find_first_cache_extent(&map_tree->cache_tree, *logical); + + while (ce) { + ce = next_cache_extent(ce); + if (!ce) + return -ENOENT; + + map = container_of(ce, struct map_lookup, ce); + if (map->type & BTRFS_BLOCK_GROUP_METADATA) { + *logical = ce->start; + *size = ce->size; + return 0; + } + } + + return -ENOENT; +} + int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree, u64 chunk_start, u64 physical, u64 devid, u64 **logical, int *naddrs, int *stripe_len) @@ -1041,6 +1065,14 @@ int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree, int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, u64 logical, u64 *length, struct btrfs_multi_bio **multi_ret, int mirror_num) +{ + return __btrfs_map_block(map_tree, rw, logical, length, NULL, + multi_ret, mirror_num); +} + +int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, + u64 logical, u64 *length, u64 *type, + struct btrfs_multi_bio **multi_ret, int mirror_num) { struct cache_extent *ce; struct map_lookup *map; @@ -1057,16 +1089,24 @@ int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, stripes_allocated = 1; } again: + ce = find_first_cache_extent(&map_tree->cache_tree, logical); + if (!ce) { + if (multi) + kfree(multi); + return -ENOENT; + } + if (ce->start > logical || ce->start + ce->size < logical) { + if (multi) + kfree(multi); + return -ENOENT; + } + if (multi_ret) { multi = kzalloc(btrfs_multi_bio_size(stripes_allocated), GFP_NOFS); if (!multi) return -ENOMEM; } - - ce = find_first_cache_extent(&map_tree->cache_tree, logical); - BUG_ON(!ce); - BUG_ON(ce->start > logical || ce->start + ce->size < logical); map = container_of(ce, struct map_lookup, ce); offset = logical - ce->start; @@ -1158,6 +1198,8 @@ again: stripe_index++; } *multi_ret = multi; + if (type) + *type = map->type; out: return 0; } @@ -1442,7 +1484,7 @@ int btrfs_read_sys_array(struct btrfs_root *root) u8 *ptr; unsigned long sb_ptr; u32 cur; - int ret; + int ret = 0; sb = btrfs_find_create_tree_block(root, BTRFS_SUPER_INFO_OFFSET, BTRFS_SUPER_INFO_SIZE); @@ -1473,7 +1515,8 @@ int btrfs_read_sys_array(struct btrfs_root *root) if (key.type == BTRFS_CHUNK_ITEM_KEY) { chunk = (struct btrfs_chunk *)sb_ptr; ret = read_one_chunk(root, &key, sb, chunk); - BUG_ON(ret); + if (ret) + break; num_stripes = btrfs_chunk_num_stripes(sb, chunk); len = btrfs_chunk_item_size(num_stripes); } else { @@ -1484,7 +1527,7 @@ int btrfs_read_sys_array(struct btrfs_root *root) cur += len; } free_extent_buffer(sb); - return 0; + return ret; } int btrfs_read_chunk_tree(struct btrfs_root *root) diff --git a/volumes.h b/volumes.h index 93b0e48f..7104d36e 100644 --- a/volumes.h +++ b/volumes.h @@ -96,9 +96,14 @@ int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans, u64 chunk_tree, u64 chunk_objectid, u64 chunk_offset, u64 num_bytes, u64 *start); +int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, + u64 logical, u64 *length, u64 *type, + struct btrfs_multi_bio **multi_ret, int mirror_num); int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, u64 logical, u64 *length, struct btrfs_multi_bio **multi_ret, int mirror_num); +int btrfs_next_metadata(struct btrfs_mapping_tree *map_tree, u64 *logical, + u64 *size); int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree, u64 chunk_start, u64 physical, u64 devid, u64 **logical, int *naddrs, int *stripe_len); -- cgit v1.2.3 From ba1aa28496cf2a2c7ffff0521c6c858a0505d4c2 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 27 Oct 2011 16:23:14 -0400 Subject: btrfs-progs: fixup is_mounted checks /proc/mounts contains device names that don't exist, we end up erroring out because we're not able to stat the device (that doesn't exist). Fix this by allowing the mkfs when the target device doesn't exist. Signed-off-by: Chris Mason --- utils.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/utils.c b/utils.c index 86c643c1..178d1b9d 100644 --- a/utils.c +++ b/utils.c @@ -687,6 +687,8 @@ int is_same_blk_file(const char* a, const char* b) if(stat(a, &st_buf_a) < 0 || stat(b, &st_buf_b) < 0) { + if (errno == ENOENT) + return 0; return -errno; } @@ -723,9 +725,11 @@ int is_same_loop_file(const char* a, const char* b) /* Resolve a if it is a loop device */ if((ret = is_loop_device(a)) < 0) { - return ret; - } else if(ret) { - if((ret = resolve_loop_device(a, res_a, sizeof(res_a))) < 0) + if (ret == -ENOENT) + return 0; + return ret; + } else if (ret) { + if ((ret = resolve_loop_device(a, res_a, sizeof(res_a))) < 0) return ret; final_a = res_a; @@ -734,9 +738,11 @@ int is_same_loop_file(const char* a, const char* b) } /* Resolve b if it is a loop device */ - if((ret = is_loop_device(b)) < 0) { - return ret; - } else if(ret) { + if ((ret = is_loop_device(b)) < 0) { + if (ret == -ENOENT) + return 0; + return ret; + } else if (ret) { if((ret = resolve_loop_device(b, res_b, sizeof(res_b))) < 0) return ret; -- cgit v1.2.3 From ff91b185f7ddad0f16fb0be4c6713d65daf1b03e Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 27 Oct 2011 16:25:12 -0400 Subject: restore: deal with holes and set i_size correctly This changes restore to set the i_size of the files it copies out based on the size in the inode. It also changes it to skip over holes. Signed-off-by: Chris Mason --- Makefile | 2 +- restore.c | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index e85c7266..a5024f1b 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC = gcc AM_CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 -CFLAGS = -g -Os +CFLAGS = -g -O0 objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ root-tree.o dir-item.o file-item.o inode-item.o \ inode-map.o crc32c.o rbtree.o extent-cache.o extent_io.o \ diff --git a/restore.c b/restore.c index c20f4e1b..250c9d3c 100644 --- a/restore.c +++ b/restore.c @@ -204,6 +204,10 @@ static int copy_one_extent(struct btrfs_root *root, int fd, ram_size = btrfs_file_extent_ram_bytes(leaf, fi); size_left = disk_size; + /* we found a hole */ + if (disk_size == 0) + return 0; + inbuf = malloc(disk_size); if (!inbuf) { fprintf(stderr, "No memory\n"); @@ -258,7 +262,7 @@ again: pos+total); if (done < 0) { free(inbuf); - fprintf(stderr, "Error writing: %d\n", errno); + fprintf(stderr, "Error writing: %d %s\n", errno, strerror(errno)); return -1; } total += done; @@ -278,7 +282,7 @@ again: done = pwrite(fd, outbuf+total, ram_size-total, pos+total); if (done < 0) { free(outbuf); - fprintf(stderr, "Error writing: %d\n", errno); + fprintf(stderr, "Error writing: %d %s\n", errno, strerror(errno)); return -1; } total += done; @@ -314,11 +318,13 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, struct extent_buffer *leaf; struct btrfs_path *path; struct btrfs_file_extent_item *fi; + struct btrfs_inode_item *inode_item; struct btrfs_key found_key; int ret; int extent_type; int compression; int loops = 0; + u64 found_size = 0; path = btrfs_alloc_path(); if (!path) { @@ -327,6 +333,14 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, } path->skip_locking = 1; + ret = btrfs_lookup_inode(NULL, root, path, key, 0); + if (ret == 0) { + inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], + struct btrfs_inode_item); + found_size = btrfs_inode_size(path->nodes[0], inode_item); + } + btrfs_release_path(root, path); + key->offset = 0; key->type = BTRFS_EXTENT_DATA_KEY; @@ -370,6 +384,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, } else if (ret) { /* No more leaves to search */ btrfs_free_path(path); + goto set_size; return 0; } leaf = path->nodes[0]; @@ -415,6 +430,9 @@ next: } btrfs_free_path(path); +set_size: + if (found_size) + ftruncate(fd, (loff_t)found_size); return 0; } -- cgit v1.2.3 From b670b9285efb2cd5b8a1668d6e80727d03e2a3d2 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 27 Oct 2011 17:42:26 -0400 Subject: btrfs-debug-tree: add -r option to print only the roots Signed-off-by: Chris Mason --- debug-tree.c | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/debug-tree.c b/debug-tree.c index 34cefe9f..91e0064f 100644 --- a/debug-tree.c +++ b/debug-tree.c @@ -76,6 +76,7 @@ int main(int ac, char **av) int slot; int extent_only = 0; int device_only = 0; + int roots_only = 0; u64 block_only = 0; struct btrfs_root *tree_root_scan; @@ -83,7 +84,7 @@ int main(int ac, char **av) while(1) { int c; - c = getopt(ac, av, "deb:"); + c = getopt(ac, av, "deb:r"); if (c < 0) break; switch(c) { @@ -93,6 +94,9 @@ int main(int ac, char **av) case 'd': device_only = 1; break; + case 'r': + roots_only = 1; + break; case 'b': block_only = atoll(optarg); break; @@ -134,13 +138,22 @@ int main(int ac, char **av) } if (!extent_only) { - printf("root tree\n"); - btrfs_print_tree(root->fs_info->tree_root, - root->fs_info->tree_root->node, 1); + if (roots_only) { + printf("root tree: %llu level %d\n", + (unsigned long long)root->fs_info->tree_root->node->start, + btrfs_header_level(root->fs_info->tree_root->node)); + printf("chunk tree: %llu level %d\n", + (unsigned long long)root->fs_info->chunk_root->node->start, + btrfs_header_level(root->fs_info->chunk_root->node)); + } else { + printf("root tree\n"); + btrfs_print_tree(root->fs_info->tree_root, + root->fs_info->tree_root->node, 1); - printf("chunk tree\n"); - btrfs_print_tree(root->fs_info->chunk_root, - root->fs_info->chunk_root->node, 1); + printf("chunk tree\n"); + btrfs_print_tree(root->fs_info->chunk_root, + root->fs_info->chunk_root->node, 1); + } } tree_root_scan = root->fs_info->tree_root; @@ -253,8 +266,14 @@ again: } else if (!skip) { printf(" tree "); btrfs_print_key(&disk_key); - printf(" \n"); - btrfs_print_tree(tree_root_scan, buf, 1); + if (roots_only) { + printf(" %llu level %d\n", + (unsigned long long)buf->start, + btrfs_header_level(buf)); + } else { + printf(" \n"); + btrfs_print_tree(tree_root_scan, buf, 1); + } } } path.slots[0]++; -- cgit v1.2.3 From 18c970767f21714efcbfe8121d30962c9b262545 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 27 Oct 2011 22:02:19 -0400 Subject: btrfs-progs: add a utility to corrupt a single block --- Makefile | 5 +- btrfs-corrupt-block.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 btrfs-corrupt-block.c diff --git a/Makefile b/Makefile index a5024f1b..96e20020 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ LIBS=-luuid RESTORE_LIBS=-lz progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \ - btrfs btrfs-map-logical restore find-root calc-size + btrfs btrfs-map-logical restore find-root calc-size btrfs-corrupt-block # make C=1 to enable sparse ifdef C @@ -79,6 +79,9 @@ btrfstune: $(objects) btrfstune.o btrfs-map-logical: $(objects) btrfs-map-logical.o $(CC) $(CFLAGS) -o btrfs-map-logical $(objects) btrfs-map-logical.o $(LDFLAGS) $(LIBS) +btrfs-corrupt-block: $(objects) btrfs-corrupt-block.o + $(CC) $(CFLAGS) -o btrfs-corrupt-block $(objects) btrfs-corrupt-block.o $(LDFLAGS) $(LIBS) + btrfs-image: $(objects) btrfs-image.o $(CC) $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS) diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c new file mode 100644 index 00000000..ace61c13 --- /dev/null +++ b/btrfs-corrupt-block.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2009 Oracle. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#define _XOPEN_SOURCE 500 +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "volumes.h" +#include "disk-io.h" +#include "print-tree.h" +#include "transaction.h" +#include "list.h" +#include "version.h" + +/* we write the mirror info to stdout unless they are dumping the data + * to stdout + * */ +static FILE *info_file; + +struct extent_buffer *debug_corrupt_block(struct btrfs_root *root, u64 bytenr, + u32 blocksize, int copy) +{ + int ret; + struct extent_buffer *eb; + u64 length; + struct btrfs_multi_bio *multi = NULL; + struct btrfs_device *device; + int num_copies; + int mirror_num = 1; + + eb = btrfs_find_create_tree_block(root, bytenr, blocksize); + if (!eb) + return NULL; + + length = blocksize; + while (1) { + ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, + eb->start, &length, &multi, mirror_num); + BUG_ON(ret); + device = multi->stripes[0].dev; + eb->fd = device->fd; + device->total_ios++; + eb->dev_bytenr = multi->stripes[0].physical; + + fprintf(info_file, "mirror %d logical %Lu physical %Lu " + "device %s\n", mirror_num, (unsigned long long)bytenr, + (unsigned long long)eb->dev_bytenr, device->name); + kfree(multi); + + if (!copy || mirror_num == copy) { + ret = read_extent_from_disk(eb); + printf("corrupting %llu copy %d\n", eb->start, + mirror_num); + memset(eb->data, 0, eb->len); + write_extent_to_disk(eb); + fsync(eb->fd); + } + + num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, + eb->start, eb->len); + if (num_copies == 1) + break; + + mirror_num++; + if (mirror_num > num_copies) + break; + } + return eb; +} + +static void print_usage(void) +{ + fprintf(stderr, "usage: btrfs-map-logical [options] mount_point\n"); + fprintf(stderr, "\t-l Logical extent to map\n"); + fprintf(stderr, "\t-c Copy of the extent to read (usually 1 or 2)\n"); + fprintf(stderr, "\t-o Output file to hold the extent\n"); + fprintf(stderr, "\t-b Number of bytes to read\n"); + exit(1); +} + +static struct option long_options[] = { + /* { "byte-count", 1, NULL, 'b' }, */ + { "logical", 1, NULL, 'l' }, + { "copy", 1, NULL, 'c' }, + { "bytes", 1, NULL, 'b' }, + { 0, 0, 0, 0} +}; + +int main(int ac, char **av) +{ + struct cache_tree root_cache; + struct btrfs_root *root; + struct extent_buffer *eb; + char *dev; + char *output_file = NULL; + u64 logical = 0; + int ret = 0; + int option_index = 0; + int copy = 0; + u64 bytes = 4096; + int out_fd = 0; + int err; + + while(1) { + int c; + c = getopt_long(ac, av, "l:c:", long_options, + &option_index); + if (c < 0) + break; + switch(c) { + case 'l': + logical = atoll(optarg); + if (logical == 0) { + fprintf(stderr, + "invalid extent number\n"); + print_usage(); + } + break; + case 'c': + copy = atoi(optarg); + if (copy == 0) { + fprintf(stderr, + "invalid copy number\n"); + print_usage(); + } + break; + case 'b': + bytes = atoll(optarg); + if (bytes == 0) { + fprintf(stderr, + "invalid byte count\n"); + print_usage(); + } + break; + default: + print_usage(); + } + } + ac = ac - optind; + if (ac == 0) + print_usage(); + if (logical == 0) + print_usage(); + if (copy < 0) + print_usage(); + + dev = av[optind]; + + radix_tree_init(); + cache_tree_init(&root_cache); + + root = open_ctree(dev, 0, 1); + if (!root) { + fprintf(stderr, "Open ctree failed\n"); + exit(1); + } + + info_file = stdout; + if (output_file) { + if (strcmp(output_file, "-") == 0) { + out_fd = 1; + info_file = stderr; + } else { + out_fd = open(output_file, O_RDWR | O_CREAT, 0600); + if (out_fd < 0) + goto close; + err = ftruncate(out_fd, 0); + if (err) { + close(out_fd); + goto close; + } + info_file = stdout; + } + } + + if (bytes == 0) + bytes = root->sectorsize; + + bytes = (bytes + root->sectorsize - 1) / root->sectorsize; + bytes *= root->sectorsize; + + while (bytes > 0) { + eb = debug_corrupt_block(root, logical, root->sectorsize, copy); + if (eb && output_file) { + err = write(out_fd, eb->data, eb->len); + if (err < 0 || err != eb->len) { + fprintf(stderr, "output file write failed\n"); + goto out_close_fd; + } + } + free_extent_buffer(eb); + logical += root->sectorsize; + bytes -= root->sectorsize; + } + +out_close_fd: + if (output_file && out_fd != 1) + close(out_fd); +close: + return ret; +} -- cgit v1.2.3 From f25e1d16a5f35fb43c577cd33163c6aa290a587b Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 27 Oct 2011 22:40:23 -0400 Subject: btrfs-progs: remove old debugging statement Signed-off-by: Chris Mason --- disk-io.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/disk-io.c b/disk-io.c index 8429e53a..408b2d58 100644 --- a/disk-io.c +++ b/disk-io.c @@ -266,7 +266,6 @@ int write_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, if (!btrfs_buffer_uptodate(eb, trans->transid)) BUG(); - printf("writing out a block\n"); btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN); csum_tree_block(root, eb, 0); @@ -934,7 +933,6 @@ int write_dev_supers(struct btrfs_root *root, struct btrfs_super_block *sb, if (root->fs_info->super_bytenr != BTRFS_SUPER_INFO_OFFSET) { btrfs_set_super_bytenr(sb, root->fs_info->super_bytenr); -printk("speiiiiiiiiiiiiiiiiiiiiiiiiiiiii\n"); crc = ~(u32)0; crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE); -- cgit v1.2.3