btrfs-progs: add a recovery utility to pull files from damanged filesystems
authorJosef Bacik <josef@redhat.com>
Fri, 26 Aug 2011 13:51:36 +0000 (09:51 -0400)
committerChris Mason <chris.mason@oracle.com>
Thu, 27 Oct 2011 16:49:54 +0000 (12:49 -0400)
Signed-off-by: Josef Bacik <josef@redhat.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
13 files changed:
.gitignore [new file with mode: 0644]
Makefile
calc-size.c [new file with mode: 0644]
ctree.c
ctree.h
disk-io.c
disk-io.h
extent-tree.c
extent_io.c
find-root.c [new file with mode: 0644]
restore.c [new file with mode: 0644]
volumes.c
volumes.h

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..0e560d5
--- /dev/null
@@ -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
index edee1a0..e85c726 100644 (file)
--- 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 (file)
index 0000000..c4adfb0
--- /dev/null
@@ -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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <zlib.h>
+#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] <device>\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 12f1a55..005550f 100644 (file)
--- 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 6e1b80b..b1a87c8 100644 (file)
--- 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);
index aed624c..8429e53 100644 (file)
--- a/disk-io.c
+++ b/disk-io.c
 #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;
 }
 
index 7ebec24..2048fcf 100644 (file)
--- 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,
index b5b7aaa..5bed3c2 100644 (file)
@@ -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);
index 8f0a876..973e918 100644 (file)
@@ -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 (file)
index 0000000..c0f38b8
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <zlib.h>
+#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] <device>\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 (file)
index 0000000..c20f4e1
--- /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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <zlib.h>
+#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] <device> "
+               "<directory>\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;
+}
index 37f68df..03bfb8c 100644 (file)
--- 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)
@@ -1042,6 +1066,14 @@ 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;
        u64 offset;
@@ -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)
index 93b0e48..7104d36 100644 (file)
--- 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);