summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosef Bacik <josef@redhat.com>2011-08-26 09:51:36 -0400
committerChris Mason <chris.mason@oracle.com>2011-10-27 12:49:54 -0400
commitbe826706b5c0fcca3bdeff6934cd8d46d046ddfe (patch)
tree181c26d6f5d769dbbcbff217dc75f80eaa46bc51
parentbed7475d77c4891abeb457a8f0b08353501a907c (diff)
btrfs-progs: add a recovery utility to pull files from damanged filesystems
Signed-off-by: Josef Bacik <josef@redhat.com> Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r--.gitignore15
-rw-r--r--Makefile16
-rw-r--r--calc-size.c268
-rw-r--r--ctree.c10
-rw-r--r--ctree.h16
-rw-r--r--disk-io.c222
-rw-r--r--disk-io.h2
-rw-r--r--extent-tree.c13
-rw-r--r--extent_io.c1
-rw-r--r--find-root.c458
-rw-r--r--restore.c854
-rw-r--r--volumes.c57
-rw-r--r--volumes.h5
13 files changed, 1867 insertions, 70 deletions
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 <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 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 <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
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 <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;
+}
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)
@@ -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)
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);