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>
master
Josef Bacik 2011-08-26 09:51:36 -04:00 committed by Chris Mason
parent bed7475d77
commit be826706b5
13 changed files with 1867 additions and 70 deletions

15
.gitignore vendored 100644
View File

@ -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

View File

@ -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)

268
calc-size.c 100644
View File

@ -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;
}

10
ctree.c
View File

@ -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;
}

16
ctree.h
View File

@ -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);

222
disk-io.c
View File

@ -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;
}

View File

@ -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,

View 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);

View 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);

458
find-root.c 100644
View File

@ -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;
}

854
restore.c 100644
View File

@ -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;
}

View File

@ -982,6 +982,30 @@ int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len)
return ret;
}
int btrfs_next_metadata(struct btrfs_mapping_tree *map_tree, u64 *logical,
u64 *size)
{
struct cache_extent *ce;
struct map_lookup *map;
ce = find_first_cache_extent(&map_tree->cache_tree, *logical);
while (ce) {
ce = next_cache_extent(ce);
if (!ce)
return -ENOENT;
map = container_of(ce, struct map_lookup, ce);
if (map->type & BTRFS_BLOCK_GROUP_METADATA) {
*logical = ce->start;
*size = ce->size;
return 0;
}
}
return -ENOENT;
}
int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree,
u64 chunk_start, u64 physical, u64 devid,
u64 **logical, int *naddrs, int *stripe_len)
@ -1041,6 +1065,14 @@ int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree,
int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
u64 logical, u64 *length,
struct btrfs_multi_bio **multi_ret, int mirror_num)
{
return __btrfs_map_block(map_tree, rw, logical, length, NULL,
multi_ret, mirror_num);
}
int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
u64 logical, u64 *length, u64 *type,
struct btrfs_multi_bio **multi_ret, int mirror_num)
{
struct cache_extent *ce;
struct map_lookup *map;
@ -1057,16 +1089,24 @@ int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
stripes_allocated = 1;
}
again:
ce = find_first_cache_extent(&map_tree->cache_tree, logical);
if (!ce) {
if (multi)
kfree(multi);
return -ENOENT;
}
if (ce->start > logical || ce->start + ce->size < logical) {
if (multi)
kfree(multi);
return -ENOENT;
}
if (multi_ret) {
multi = kzalloc(btrfs_multi_bio_size(stripes_allocated),
GFP_NOFS);
if (!multi)
return -ENOMEM;
}
ce = find_first_cache_extent(&map_tree->cache_tree, logical);
BUG_ON(!ce);
BUG_ON(ce->start > logical || ce->start + ce->size < logical);
map = container_of(ce, struct map_lookup, ce);
offset = logical - ce->start;
@ -1158,6 +1198,8 @@ again:
stripe_index++;
}
*multi_ret = multi;
if (type)
*type = map->type;
out:
return 0;
}
@ -1442,7 +1484,7 @@ int btrfs_read_sys_array(struct btrfs_root *root)
u8 *ptr;
unsigned long sb_ptr;
u32 cur;
int ret;
int ret = 0;
sb = btrfs_find_create_tree_block(root, BTRFS_SUPER_INFO_OFFSET,
BTRFS_SUPER_INFO_SIZE);
@ -1473,7 +1515,8 @@ int btrfs_read_sys_array(struct btrfs_root *root)
if (key.type == BTRFS_CHUNK_ITEM_KEY) {
chunk = (struct btrfs_chunk *)sb_ptr;
ret = read_one_chunk(root, &key, sb, chunk);
BUG_ON(ret);
if (ret)
break;
num_stripes = btrfs_chunk_num_stripes(sb, chunk);
len = btrfs_chunk_item_size(num_stripes);
} else {
@ -1484,7 +1527,7 @@ int btrfs_read_sys_array(struct btrfs_root *root)
cur += len;
}
free_extent_buffer(sb);
return 0;
return ret;
}
int btrfs_read_chunk_tree(struct btrfs_root *root)

View File

@ -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);