diff --git a/.gitignore b/.gitignore index 0e560d518..cc5d69075 100644 --- a/.gitignore +++ b/.gitignore @@ -3,12 +3,16 @@ version.h man/*.gz btrfs +btrfs-corrupt-block btrfs-debug-tree +btrfs-dump-super btrfs-map-logical +btrfs-select-super btrfs-show btrfs-vol btrfsck btrfsctl +calc-size find-root mkfs.btrfs repair diff --git a/Makefile b/Makefile index 96e200201..ac1a150b1 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 -O0 +CFLAGS = -g -O0 -pthread 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 \ @@ -14,10 +14,11 @@ INSTALL = install prefix ?= /usr/local bindir = $(prefix)/bin LIBS=-luuid -RESTORE_LIBS=-lz +RESTORE_LIBS=-lz -llzo2 progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \ - btrfs btrfs-map-logical restore find-root calc-size btrfs-corrupt-block + btrfs btrfs-map-logical restore find-root calc-size btrfs-corrupt-block \ + btrfs-dump-super # make C=1 to enable sparse ifdef C @@ -73,6 +74,9 @@ btrfs-zero-log: $(objects) btrfs-zero-log.o btrfs-select-super: $(objects) btrfs-select-super.o $(CC) $(CFLAGS) -o btrfs-select-super $(objects) btrfs-select-super.o $(LDFLAGS) $(LIBS) +btrfs-dump-super: $(objects) btrfs-dump-super.o + $(CC) $(CFLAGS) -o btrfs-dump-super $(objects) btrfs-dump-super.o $(LDFLAGS) $(LIBS) + btrfstune: $(objects) btrfstune.o $(CC) $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS) @@ -105,7 +109,8 @@ install-man: clean : rm -f $(progs) cscope.out *.o .*.d btrfs-convert btrfs-image btrfs-select-super \ - btrfs-zero-log btrfstune dir-test ioctl-test quick-test version.h + btrfs-dump-super btrfs-zero-log btrfstune dir-test ioctl-test quick-test \ + version.h cd man; make clean install: $(progs) install-man diff --git a/btrfs-dump-super.c b/btrfs-dump-super.c new file mode 100644 index 000000000..033140c56 --- /dev/null +++ b/btrfs-dump-super.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2011 Google. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#define _XOPEN_SOURCE 500 +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "disk-io.h" +#include "version.h" + +static void print_usage(void) +{ + fprintf(stderr, "usage: btrfs-dump-super dev\n"); + fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION); + exit(1); +} + +static int read_block(const char* filename, u64 bytenr, struct btrfs_super_block* sb) { + int fd = open(filename, O_RDONLY, 0600); + int block_size = sizeof(struct btrfs_super_block); + int bytes_read = 0; + + if (fd < 0) { + fprintf(stderr, "Could not open %s\n", filename); + return -1; + } + + bytes_read = pread(fd, sb, block_size, bytenr); + if (bytes_read < block_size) { + fprintf(stderr, "Only read %d bytes of %d.\n", bytes_read, block_size); + } + + close(fd); + return bytes_read; +} + +int main(int ac, char **av) +{ + int i; + + if (ac != 2) + print_usage(); + + for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { + u64 bytenr = btrfs_sb_offset(i); + int fd; + struct btrfs_super_block sb; + int block_size = sizeof(struct btrfs_super_block); + char filename[1024]; + int bytes_read = read_block(av[optind], bytenr, &sb); + if (bytes_read < block_size) + continue; + + sprintf(filename, "/tmp/block.%s.%llu", + strrchr(av[optind], '/') + 1, bytenr); + fd = open(filename, O_CREAT|O_WRONLY, 0644); + if (block_size != pwrite(fd, &sb, block_size, 0)) { + fprintf(stderr, "Failed to dump superblock %d", i); + continue; + } + fprintf(stderr, "Dumped superblock %s:%d, gen %llu to %s.\n", + av[optind], i, sb.generation, filename); + close(fd); + } + + return 0; +} diff --git a/btrfs-select-super.c b/btrfs-select-super.c index 51eb9c969..079986aff 100644 --- a/btrfs-select-super.c +++ b/btrfs-select-super.c @@ -34,7 +34,9 @@ static void print_usage(void) { - fprintf(stderr, "usage: btrfs-select-super -s number dev\n"); + fprintf(stderr, "usage: btrfs-select-super [-c] [-e] -s number dev\n"); + fprintf(stderr, " -c Commit changes to disk [IRREVERSIBLE]\n"); + fprintf(stderr, " -e Use the earliest super found, may help recover transid verify problems\n"); fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION); exit(1); } @@ -45,10 +47,13 @@ int main(int ac, char **av) int ret; int num; u64 bytenr = 0; + int commit = 0; + int use_earliest_bdev = 0; + int fp; while(1) { int c; - c = getopt(ac, av, "s:"); + c = getopt(ac, av, "s:ce"); if (c < 0) break; switch(c) { @@ -58,6 +63,12 @@ int main(int ac, char **av) printf("using SB copy %d, bytenr %llu\n", num, (unsigned long long)bytenr); break; + case 'c': + commit = 1; + break; + case 'e': + use_earliest_bdev = 1; + break; default: print_usage(); } @@ -74,22 +85,33 @@ int main(int ac, char **av) radix_tree_init(); - if((ret = check_mounted(av[optind])) < 0) { + if ((ret = check_mounted(av[optind])) < 0) { fprintf(stderr, "Could not check mount status: %s\n", strerror(-ret)); return ret; - } else if(ret) { + } else if (ret) { fprintf(stderr, "%s is currently mounted. Aborting.\n", av[optind]); return -EBUSY; } - root = open_ctree(av[optind], bytenr, 1); + fp = open(av[optind], O_CREAT|O_RDWR, 0600); + if (fp < 0) { + fprintf(stderr, "Could not open %s\n", av[optind]); + return 1; + } + root = open_ctree_fd(fp, av[optind], bytenr, 1, use_earliest_bdev); if (root == NULL) return 1; - /* make the super writing code think we've read the first super */ - root->fs_info->super_bytenr = BTRFS_SUPER_INFO_OFFSET; - ret = write_all_supers(root); + fprintf(stderr, "Found superblock with generation %llu.\n", root->fs_info->super_copy.generation); + + if (commit) { + fprintf(stderr, "Committing...\n"); + + /* make the super writing code think we've read the first super */ + root->fs_info->super_bytenr = BTRFS_SUPER_INFO_OFFSET; + ret = write_all_supers(root); + } /* we don't close the ctree or anything, because we don't want a real * transaction commit. We just want the super copy we pulled off the diff --git a/btrfs_cmds.c b/btrfs_cmds.c index f2b635512..887bdd4e2 100644 --- a/btrfs_cmds.c +++ b/btrfs_cmds.c @@ -613,7 +613,7 @@ int do_scan(int argc, char **argv) if( argc >= 2 && !strcmp(argv[1],"--all-devices")){ if( argc >2 ){ - fprintf(stderr, "ERROR: too may arguments\n"); + fprintf(stderr, "ERROR: too many arguments\n"); return 22; } diff --git a/btrfsck.c b/btrfsck.c index 3a23e6656..43bff9e33 100644 --- a/btrfsck.c +++ b/btrfsck.c @@ -2800,7 +2800,7 @@ static int check_extents(struct btrfs_root *root) static void print_usage(void) { - fprintf(stderr, "usage: btrfsck dev\n"); + fprintf(stderr, "usage: btrfsck [-s superblock] [-t tree root] [-g generation] dev\n"); fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION); exit(1); } @@ -2810,12 +2810,15 @@ int main(int ac, char **av) struct cache_tree root_cache; struct btrfs_root *root; u64 bytenr = 0; + u64 root_tree_bytenr = 0; + u64 root_tree_generation = 0; int ret; int num; - + int writes=0; + while(1) { int c; - c = getopt(ac, av, "s:"); + c = getopt(ac, av, "s:t:g:c"); if (c < 0) break; switch(c) { @@ -2825,6 +2828,17 @@ int main(int ac, char **av) printf("using SB copy %d, bytenr %llu\n", num, (unsigned long long)bytenr); break; + case 't': + root_tree_bytenr = atoll(optarg); + printf("Using root tree %llu\n", root_tree_bytenr); + break; + case 'g': + root_tree_generation = atoll(optarg); + printf("Using generation %llu\n", root_tree_generation); + break; + case 'c': + writes=1; + break; default: print_usage(); } @@ -2845,18 +2859,22 @@ int main(int ac, char **av) return -EBUSY; } - root = open_ctree(av[optind], bytenr, 0); + root = open_ctree_recovery(av[optind], bytenr, root_tree_bytenr, root_tree_generation, writes); if (root == NULL) return 1; + printf("Checking extents...\n"); ret = check_extents(root); if (ret) goto out; + + printf("Checking fs roots...\n"); ret = check_fs_roots(root, &root_cache); if (ret) goto out; + printf("Checking root references...\n"); ret = check_root_refs(root, &root_cache); out: free_root_recs(&root_cache); diff --git a/convert.c b/convert.c index 291dc27d4..c036f4696 100644 --- a/convert.c +++ b/convert.c @@ -2386,7 +2386,7 @@ int do_convert(const char *devname, int datacsum, int packing, int noxattr) fprintf(stderr, "unable to update system chunk\n"); goto fail; } - root = open_ctree_fd(fd, devname, super_bytenr, O_RDWR); + root = open_ctree_fd(fd, devname, super_bytenr, O_RDWR, 0); if (!root) { fprintf(stderr, "unable to open ctree\n"); goto fail; @@ -2447,7 +2447,7 @@ int do_convert(const char *devname, int datacsum, int packing, int noxattr) goto fail; } - root = open_ctree_fd(fd, devname, 0, O_RDWR); + root = open_ctree_fd(fd, devname, 0, O_RDWR, 0); if (!root) { fprintf(stderr, "unable to open ctree\n"); goto fail; @@ -2546,7 +2546,7 @@ int do_rollback(const char *devname, int force) fprintf(stderr, "unable to open %s\n", devname); goto fail; } - root = open_ctree_fd(fd, devname, 0, O_RDWR); + root = open_ctree_fd(fd, devname, 0, O_RDWR, 0); if (!root) { fprintf(stderr, "unable to open ctree\n"); goto fail; diff --git a/disk-io.c b/disk-io.c index 408b2d58c..e0adf5ab5 100644 --- a/disk-io.c +++ b/disk-io.c @@ -438,13 +438,15 @@ static int find_and_setup_root(struct btrfs_root *tree_root, root, fs_info, objectid); ret = btrfs_find_last_root(tree_root, objectid, &root->root_item, &root->root_key); - BUG_ON(ret); + if (ret) + return ret; blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item)); generation = btrfs_root_generation(&root->root_item); root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item), blocksize, generation); - BUG_ON(!root->node); + if (!root->node) + return -ENOENT; return 0; } @@ -580,7 +582,7 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info, return fs_info->dev_root; if (location->objectid == BTRFS_CSUM_TREE_OBJECTID) return fs_info->csum_root; - + BUG_ON(location->objectid == BTRFS_TREE_RELOC_OBJECTID || location->offset != (u64)-1); @@ -602,7 +604,8 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info, } struct btrfs_root *__open_ctree_fd(int fp, const char *path, u64 sb_bytenr, - u64 root_tree_bytenr, int writes) + u64 root_tree_bytenr, u64 root_tree_generation, int writes, + int use_earliest_bdev) { u32 sectorsize; u32 nodesize; @@ -677,8 +680,14 @@ struct btrfs_root *__open_ctree_fd(int fp, const char *path, u64 sb_bytenr, fs_info->super_bytenr = sb_bytenr; disk_super = &fs_info->super_copy; - ret = btrfs_read_dev_super(fs_devices->latest_bdev, - disk_super, sb_bytenr); + if (use_earliest_bdev) { + ret = btrfs_read_dev_super(fs_devices->earliest_bdev, + disk_super, sb_bytenr); + } else { + ret = btrfs_read_dev_super(fs_devices->latest_bdev, + disk_super, sb_bytenr); + } + if (ret) { printk("No valid btrfs found\n"); goto out_devices; @@ -754,9 +763,13 @@ struct btrfs_root *__open_ctree_fd(int fp, const char *path, u64 sb_bytenr, if (!root_tree_bytenr) root_tree_bytenr = btrfs_super_root(disk_super); + + if (!root_tree_generation) + root_tree_generation = generation; + tree_root->node = read_tree_block(tree_root, root_tree_bytenr, - blocksize, generation); + blocksize, root_tree_generation); if (!tree_root->node) { printk("Couldn't read tree root\n"); goto out_chunk; @@ -787,7 +800,7 @@ struct btrfs_root *__open_ctree_fd(int fp, const char *path, u64 sb_bytenr, find_and_setup_log_root(tree_root, fs_info, disk_super); - fs_info->generation = generation; + fs_info->generation = root_tree_generation; fs_info->last_trans_committed = generation; btrfs_read_block_groups(fs_info->tree_root); @@ -847,14 +860,14 @@ struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes) fprintf (stderr, "Could not open %s\n", filename); return NULL; } - root = __open_ctree_fd(fp, filename, sb_bytenr, 0, writes); + root = __open_ctree_fd(fp, filename, sb_bytenr, 0, 0, writes, 0); close(fp); return root; } struct btrfs_root *open_ctree_recovery(const char *filename, u64 sb_bytenr, - u64 root_tree_bytenr) + u64 root_tree_bytenr, u64 root_tree_generation, int writes) { int fp; struct btrfs_root *root; @@ -864,16 +877,166 @@ struct btrfs_root *open_ctree_recovery(const char *filename, u64 sb_bytenr, fprintf (stderr, "Could not open %s\n", filename); return NULL; } - root = __open_ctree_fd(fp, filename, sb_bytenr, root_tree_bytenr, 0); + root = __open_ctree_fd(fp, filename, sb_bytenr, root_tree_bytenr, root_tree_generation, writes, 0); close(fp); return root; } struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, - int writes) + int writes, int use_earliest_bdev) +{ + return __open_ctree_fd(fp, path, sb_bytenr, 0, 0, writes, use_earliest_bdev); +} + +struct btrfs_root *open_ctree_broken(int fd, const char *device) { - return __open_ctree_fd(fp, path, sb_bytenr, 0, writes); + 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; } int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr) diff --git a/disk-io.h b/disk-io.h index 2048fcfcb..9edb9eccc 100644 --- a/disk-io.h +++ b/disk-io.h @@ -45,9 +45,10 @@ int clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct extent_buffer *buf); 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); + int writes, int use_earliest_bdev); struct btrfs_root *open_ctree_recovery(const char *filename, u64 sb_bytenr, - u64 root_tree_bytenr); + u64 root_tree_bytenr, u64 root_tree_generation, int writes); +struct btrfs_root *open_ctree_broken(int fd, const char *device); 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/find-root.c b/find-root.c index c0f38b884..3ad7af88a 100644 --- a/find-root.c +++ b/find-root.c @@ -38,6 +38,8 @@ static int verbose = 0; static u16 csum_size = 0; static u64 search_objectid = BTRFS_ROOT_TREE_OBJECTID; +u64 best_gen=0; +u64 best_hbyte=0; static void usage() { @@ -92,181 +94,73 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, return 0; } -static int close_all_devices(struct btrfs_fs_info *fs_info) +static int dump_root_bytenr(struct btrfs_root *root, u64 bytenr, u64 gen) { - 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)); + struct btrfs_root *tmp = malloc(sizeof(struct btrfs_root)); + struct btrfs_path *path; + struct btrfs_key key; + struct btrfs_root_item ri; + struct extent_buffer *leaf; + struct btrfs_disk_key disk_key; + struct btrfs_key found_key; + int slot; 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 (!tmp) + return -ENOMEM; - 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; - } + __setup_root(4096, 4096, 4096, 4096, tmp, + root->fs_info, BTRFS_ROOT_TREE_OBJECTID); - 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); + tmp->node = read_tree_block(root, bytenr, 4096, gen); + key.objectid = 0; + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = -1; - 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; - } + path = btrfs_alloc_path(); - 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; + /* Walk the slots of this root looking for BTRFS_ROOT_ITEM_KEYs. */ + ret = btrfs_search_slot(NULL, tmp, &key, path, 0, 0); + BUG_ON(ret < 0); + while (1) { + leaf = path->nodes[0]; + slot = path->slots[0]; + if (slot >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(tmp, path); + if (ret != 0) + break; + leaf = path->nodes[0]; + slot = path->slots[0]; + } + btrfs_item_key(leaf, &disk_key, path->slots[0]); + btrfs_disk_key_to_cpu(&found_key, &disk_key); + if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) { + unsigned long offset; + + offset = btrfs_item_ptr_offset(leaf, slot); + read_extent_buffer(leaf, &ri, offset, sizeof(ri)); + if (verbose) + printf("Generation: %Lu Root bytenr: %Lu " + "Root objectid: %Lu\n", gen, + btrfs_root_bytenr(&ri), found_key.objectid); + } + path->slots[0]++; } - - 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; + btrfs_free_path(path); + free_extent_buffer(leaf); + return 0; } static int search_iobuf(struct btrfs_root *root, void *iobuf, - size_t iobuf_size, off_t offset) + 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; @@ -285,18 +179,29 @@ static int search_iobuf(struct btrfs_root *root, void *iobuf, 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); + if (verbose) + fprintf(stderr, "Well block %Lu seems good, " + "but the csum doesn't match\n", + h_byte); goto next; } + /* Found some kind of root and it's fairly valid. */ + if (dump_root_bytenr(root, h_byte, h_gen)) + break; 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); + if (h_gen > best_gen){ + best_gen=h_gen; + best_hbyte=h_byte; + } + if (verbose) + fprintf(stderr, "Well block %Lu seems great, " + "but generation doesn't match, " + "have=%Lu, want=%Lu\n", h_byte, h_gen, + gen); goto next; } + best_gen=h_gen; + best_hbyte=h_byte; printf("Found tree root at %Lu\n", h_byte); return 0; next: @@ -358,16 +263,19 @@ static int find_root(struct btrfs_root *root) return ret; offset = metadata_offset; + if (verbose) + printf("Checking metadata chunk %Lu, size %Lu\n", + metadata_offset, metadata_size); + while (1) { u64 map_length = 4096; u64 type; + int mirror_num; + int num_copies; - 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)) { + if (verbose) + printf("Moving to the next metadata chunk\n"); err = btrfs_next_metadata(&root->fs_info->mapping_tree, &metadata_offset, &metadata_size); @@ -376,9 +284,14 @@ static int find_root(struct btrfs_root *root) break; } offset = metadata_offset; + if (verbose) + printf("Checking metadata chunk %Lu, size %Lu" + "\n", metadata_offset, metadata_size); } + mirror_num = 1; + again: err = __btrfs_map_block(&root->fs_info->mapping_tree, READ, - offset, &map_length, &type, &multi, 0); + offset, &map_length, &type, &multi, mirror_num); if (err) { offset += map_length; continue; @@ -396,9 +309,16 @@ static int find_root(struct btrfs_root *root) err = read_physical(root, fd, offset, bytenr, map_length); if (!err) { + /* Found the root. */ ret = 0; break; } else if (err < 0) { + num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, + offset, map_length); + mirror_num++; + if (mirror_num <= num_copies) + goto again; + /* Unrecoverable error in read. */ ret = err; break; } @@ -454,5 +374,10 @@ int main(int argc, char **argv) csum_size = btrfs_super_csum_size(&root->fs_info->super_copy); ret = find_root(root); close_ctree(root); + + if (best_gen) + printf("Best generation I found was gen=%Lu at block %Lu", best_gen, best_hbyte); + else + printf("I couldn't find any valid blocks."); return ret; } diff --git a/readme.ubuntu b/readme.ubuntu new file mode 100644 index 000000000..5bbc8710a --- /dev/null +++ b/readme.ubuntu @@ -0,0 +1,2 @@ +sudo apt-get install uuid-dev zlib1g-dev liblzo2-dev +And edit mkfs.c replacing with diff --git a/restore.c b/restore.c index 250c9d3c3..4e175f04b 100644 --- a/restore.c +++ b/restore.c @@ -25,6 +25,10 @@ #include #include #include +#include +#include +#include +#include #include "kerncompat.h" #include "ctree.h" #include "disk-io.h" @@ -35,14 +39,19 @@ #include "volumes.h" #include "utils.h" +static char fs_name[4096]; 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) +#define LZO_LEN 4 +#define PAGE_CACHE_SIZE 4096 +#define lzo1x_worst_compress(x) ((x) + ((x) / 16) + 64 + 3) + +static int decompress_zlib(char *inbuf, char *outbuf, u64 compress_len, + u64 decompress_len) { z_stream strm; int ret; @@ -61,13 +70,80 @@ static int decompress(char *inbuf, char *outbuf, u64 compress_len, ret = inflate(&strm, Z_NO_FLUSH); if (ret != Z_STREAM_END) { (void)inflateEnd(&strm); - fprintf(stderr, "ret is %d\n", ret); + fprintf(stderr, "failed to inflate: %d\n", ret); return -1; } (void)inflateEnd(&strm); return 0; } +static inline size_t read_compress_length(unsigned char *buf) +{ + __le32 dlen; + memcpy(&dlen, buf, LZO_LEN); + return le32_to_cpu(dlen); +} + +static int decompress_lzo(unsigned char *inbuf, char *outbuf, u64 compress_len, + u64 *decompress_len) +{ + size_t new_len; + size_t in_len; + size_t out_len = 0; + size_t tot_len; + size_t tot_in; + int ret; + + ret = lzo_init(); + if (ret != LZO_E_OK) { + fprintf(stderr, "lzo init returned %d\n", ret); + return -1; + } + + tot_len = read_compress_length(inbuf); + inbuf += LZO_LEN; + tot_in = LZO_LEN; + + while (tot_in < tot_len) { + in_len = read_compress_length(inbuf); + inbuf += LZO_LEN; + tot_in += LZO_LEN; + + new_len = lzo1x_worst_compress(PAGE_CACHE_SIZE); + ret = lzo1x_decompress_safe((const unsigned char *)inbuf, in_len, + (unsigned char *)outbuf, &new_len, NULL); + if (ret != LZO_E_OK) { + fprintf(stderr, "failed to inflate: %d\n", ret); + return -1; + } + out_len += new_len; + outbuf += new_len; + inbuf += in_len; + tot_in += in_len; + } + + *decompress_len = out_len; + + return 0; +} + +static int decompress(char *inbuf, char *outbuf, u64 compress_len, + u64 *decompress_len, int compress) +{ + switch (compress) { + case BTRFS_COMPRESS_ZLIB: + return decompress_zlib(inbuf, outbuf, compress_len, + *decompress_len); + case BTRFS_COMPRESS_LZO: + return decompress_lzo((unsigned char *)inbuf, outbuf, compress_len, + decompress_len); + default: + break; + } + + fprintf(stderr, "invalid compression type: %d\n", compress); + return -1; +} int next_leaf(struct btrfs_root *root, struct btrfs_path *path) { @@ -130,11 +206,11 @@ static int copy_one_inline(int fd, struct btrfs_path *path, u64 pos) struct btrfs_file_extent_item *fi; char buf[4096]; char *outbuf; + u64 ram_size; ssize_t done; unsigned long ptr; int ret; int len; - int ram_size; int compress; fi = btrfs_item_ptr(leaf, path->slots[0], @@ -162,7 +238,7 @@ static int copy_one_inline(int fd, struct btrfs_path *path, u64 pos) return -1; } - ret = decompress(buf, outbuf, len, ram_size); + ret = decompress(buf, outbuf, len, &ram_size, compress); if (ret) { free(outbuf); return ret; @@ -171,7 +247,7 @@ static int copy_one_inline(int fd, struct btrfs_path *path, u64 pos) done = pwrite(fd, outbuf, ram_size, pos); free(outbuf); if (done < len) { - fprintf(stderr, "Short compressed inline write, wanted %d, " + fprintf(stderr, "Short compressed inline write, wanted %Lu, " "did %zd: %d\n", ram_size, done, errno); return -1; } @@ -193,17 +269,23 @@ static int copy_one_extent(struct btrfs_root *root, int fd, u64 length; u64 size_left; u64 dev_bytenr; + u64 offset; u64 count = 0; int compress; int ret; int dev_fd; + int mirror_num = 1; + int num_copies; 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); + offset = btrfs_file_extent_offset(leaf, fi); size_left = disk_size; + if (offset) + printf("offset is %Lu\n", offset); /* we found a hole */ if (disk_size == 0) return 0; @@ -225,12 +307,10 @@ static int copy_one_extent(struct btrfs_root *root, int fd, again: length = size_left; ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, - bytenr, &length, &multi, 0); + bytenr, &length, &multi, mirror_num); if (ret) { - free(inbuf); - free(outbuf); fprintf(stderr, "Error mapping block %d\n", ret); - return ret; + goto out; } device = multi->stripes[0].dev; dev_fd = device->fd; @@ -240,56 +320,70 @@ static int copy_one_extent(struct btrfs_root *root, int fd, 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; + /* Need both checks, or we miss negative values due to u64 conversion */ + if (done < 0 || done < length) { + num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, + bytenr, length); + mirror_num++; + /* mirror_num is 1-indexed, so num_copies is a valid mirror. */ + if (mirror_num > num_copies) { + ret = -1; + fprintf(stderr, "Exhausted mirrors trying to read\n"); + goto out; + } + fprintf(stderr, "Trying another mirror\n"); + goto again; } + mirror_num = 1; + size_left -= length; 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); + ret = -1; fprintf(stderr, "Error writing: %d %s\n", errno, strerror(errno)); - return -1; + goto out; } total += done; } - free(inbuf); - return 0; + ret = 0; + goto out; } - ret = decompress(inbuf, outbuf, disk_size, ram_size); - free(inbuf); + ret = decompress(inbuf, outbuf, disk_size, &ram_size, compress); if (ret) { - free(outbuf); - return ret; + num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, + bytenr, length); + mirror_num++; + if (mirror_num >= num_copies) { + ret = -1; + goto out; + } + fprintf(stderr, "Trying another mirror\n"); + goto again; } while (total < ram_size) { done = pwrite(fd, outbuf+total, ram_size-total, pos+total); if (done < 0) { - free(outbuf); - fprintf(stderr, "Error writing: %d %s\n", errno, strerror(errno)); - return -1; + ret = -1; + goto out; } total += done; } +out: + free(inbuf); free(outbuf); - - return 0; + return ret; } static int ask_to_continue(const char *file) @@ -385,7 +479,6 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, /* No more leaves to search */ btrfs_free_path(path); goto set_size; - return 0; } leaf = path->nodes[0]; } while (!leaf); @@ -437,7 +530,8 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, } static int search_dir(struct btrfs_root *root, struct btrfs_key *key, - const char *dir) + const char *output_rootdir, const char *dir, + const regex_t *mreg) { struct btrfs_path *path; struct extent_buffer *leaf; @@ -541,8 +635,14 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key, 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); + /* full path from root of btrfs being restored */ + snprintf(fs_name, 4096, "%s/%s", dir, filename); + + if (mreg && REG_NOMATCH == regexec(mreg, fs_name, 0, NULL, 0)) + goto next; + /* full path from system root */ + snprintf(path_name, 4096, "%s%s", output_rootdir, fs_name); /* * At this point we're only going to restore directories and @@ -590,7 +690,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key, } } else if (type == BTRFS_FT_DIR) { struct btrfs_root *search_root = root; - char *dir = strdup(path_name); + char *dir = strdup(fs_name); if (!dir) { fprintf(stderr, "Ran out of memory\n"); @@ -651,7 +751,8 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key, return -1; } loops = 0; - ret = search_dir(search_root, &location, dir); + ret = search_dir(search_root, &location, + output_rootdir, dir, mreg); free(dir); if (ret) { if (ignore_errors) @@ -672,25 +773,197 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key, static void usage() { - fprintf(stderr, "Usage: restore [-svio] [-t disk offset] " - "\n"); + fprintf(stderr, "Usage: restore [-sviocl] [-t disk offset] " + "[-m regex] \n"); } -static struct btrfs_root *open_fs(const char *dev, u64 root_location, int super_mirror) +static int do_list_roots(struct btrfs_root *root) { + struct btrfs_key key; + struct btrfs_key found_key; + struct btrfs_disk_key disk_key; + struct btrfs_path *path; + struct extent_buffer *leaf; + struct btrfs_root_item ri; + unsigned long offset; + int slot; + int ret; + + root = root->fs_info->tree_root; + path = btrfs_alloc_path(); + if (!path) { + fprintf(stderr, "Failed to alloc path\n"); + return -1; + } + + key.offset = 0; + key.objectid = 0; + key.type = BTRFS_ROOT_ITEM_KEY; + + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) { + fprintf(stderr, "Failed to do search %d\n", ret); + btrfs_free_path(path); + return -1; + } + + while (1) { + leaf = path->nodes[0]; + slot = path->slots[0]; + if (slot >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(root, path); + if (ret) + break; + leaf = path->nodes[0]; + slot = path->slots[0]; + } + btrfs_item_key(leaf, &disk_key, slot); + btrfs_disk_key_to_cpu(&found_key, &disk_key); + if (btrfs_key_type(&found_key) != BTRFS_ROOT_ITEM_KEY) { + path->slots[0]++; + continue; + } + + offset = btrfs_item_ptr_offset(leaf, slot); + read_extent_buffer(leaf, &ri, offset, sizeof(ri)); + printf(" tree "); + btrfs_print_key(&disk_key); + printf(" %Lu level %d\n", btrfs_root_bytenr(&ri), + btrfs_root_level(&ri)); + path->slots[0]++; + } + btrfs_free_path(path); + + return 0; +} + +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 struct btrfs_root *open_fs(const char *dev, u64 root_location, + u64 fs_location, u64 root_objectid, + int super_mirror, int list_roots) +{ + struct btrfs_key key; struct btrfs_root *root; u64 bytenr; + u64 generation; + u32 blocksize; int i; + int dev_fd; for (i = super_mirror; i < BTRFS_SUPER_MIRROR_MAX; i++) { bytenr = btrfs_sb_offset(i); - root = open_ctree_recovery(dev, bytenr, root_location); + root = open_ctree_recovery(dev, bytenr, root_location, 0, 0); if (root) - return root; + goto out; fprintf(stderr, "Could not open root, trying backup super\n"); } - return NULL; + fprintf(stderr, "Ok couldn't open the root the normal way, trying " + "the broken way\n"); + + dev_fd = open(dev, O_RDONLY); + if (dev_fd < 0) { + fprintf(stderr, "Failed to open device %s\n", dev); + return NULL; + } + + root = open_ctree_broken(dev_fd, dev); + close(dev_fd); + if (!root) { + fprintf(stderr, "Broken ctree open failed\n"); + return NULL; + } + if (!root_location) + root_location = btrfs_super_root(&root->fs_info->super_copy); + + blocksize = btrfs_level_size(root, + btrfs_super_root_level(&root->fs_info->super_copy)); + generation = btrfs_super_generation(&root->fs_info->super_copy); + + root->fs_info->tree_root->node = read_tree_block(root, root_location, + blocksize, + generation); + if (!root->fs_info->tree_root->node) { + fprintf(stderr, "Couldn't read tree node\n"); + close_ctree(root); + return NULL; + } + + if (list_roots) + goto out; + + if (!root_objectid) + root_objectid = BTRFS_FS_TREE_OBJECTID; + +out: + if (fs_location) { + struct btrfs_root *fs_root = root->fs_info->fs_root; + if (!fs_root) { + fs_root = malloc(sizeof(struct btrfs_root)); + if (!fs_root) { + fprintf(stderr, "Out of memory\n"); + close_ctree(root); + return NULL; + } + __setup_root(4096, 4096, 4096, 4096, fs_root, + root->fs_info, root_objectid); + root->fs_info->fs_root = fs_root; + } + fs_root->node = read_tree_block(root, fs_location, 4096, 0); + if (!fs_root->node) { + fprintf(stderr, "Failed to read fs location\n"); + close_ctree(root); + return NULL; + } + } else if (root_objectid) { + key.objectid = root_objectid; + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = (u64)-1; + + root->fs_info->fs_root = + btrfs_read_fs_root_no_cache(root->fs_info, &key); + if (IS_ERR(root->fs_info->fs_root)) { + fprintf(stderr, "Error reading fs root %ld\n", + PTR_ERR(root->fs_info->fs_root)); + close_ctree(root); + return NULL; + } + } + + if (list_roots) { + int ret = do_list_roots(root); + if (ret) { + close_ctree(root); + return NULL; + } + } + + return root->fs_info->fs_root; } static int find_first_dir(struct btrfs_root *root, u64 *objectid) @@ -760,13 +1033,19 @@ int main(int argc, char **argv) char dir_name[128]; u64 tree_location = 0; u64 fs_location = 0; + u64 root_objectid = 0; int len; int ret; int opt; int super_mirror = 0; int find_dir = 0; + const char *match_regstr = NULL; + int match_cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; + regex_t match_reg, *mreg = NULL; + char reg_err[256]; + int list_roots = 0; - while ((opt = getopt(argc, argv, "sviot:u:df:")) != -1) { + while ((opt = getopt(argc, argv, "sviot:u:df:r:cm:l")) != -1) { switch (opt) { case 's': get_snaps = 1; @@ -809,13 +1088,33 @@ int main(int argc, char **argv) case 'd': find_dir = 1; break; + case 'r': + errno = 0; + root_objectid = (u64)strtoll(optarg, NULL, 10); + if (errno != 0) { + fprintf(stderr, "Root objectid not valid\n"); + exit(1); + } + break; + case 'c': + match_cflags |= REG_ICASE; + break; + case 'm': + match_regstr = optarg; + break; + case 'l': + list_roots = 1; + break; default: usage(); exit(1); } } - if (optind + 1 >= argc) { + if (!list_roots && optind + 1 >= argc) { + usage(); + exit(1); + } else if (list_roots && optind >= argc) { usage(); exit(1); } @@ -829,20 +1128,13 @@ int main(int argc, char **argv) return -EBUSY; } - root = open_fs(argv[optind], tree_location, super_mirror); + root = open_fs(argv[optind], tree_location, fs_location, root_objectid, + super_mirror, list_roots); 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); + if (list_roots) + goto out; memset(path_name, 0, 4096); @@ -864,9 +1156,21 @@ int main(int argc, char **argv) key.objectid = BTRFS_FIRST_FREE_OBJECTID; } - ret = search_dir(root->fs_info->fs_root, &key, dir_name); + if (match_regstr) { + ret = regcomp(&match_reg, match_regstr, match_cflags); + if (ret) { + regerror(ret, &match_reg, reg_err, sizeof(reg_err)); + fprintf(stderr, "Regex compile failed: %s\n", reg_err); + goto out; + } + mreg = &match_reg; + } + + ret = search_dir(root, &key, dir_name, "", mreg); out: + if (mreg) + regfree(mreg); close_ctree(root); return ret; } diff --git a/root-tree.c b/root-tree.c index 782472ca9..7a6ae0adc 100644 --- a/root-tree.c +++ b/root-tree.c @@ -43,6 +43,11 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, BUG_ON(ret == 0); l = path->nodes[0]; + if (path->slots[0] == 0) { + ret = -ENOENT; + goto out; + } + BUG_ON(path->slots[0] == 0); slot = path->slots[0] - 1; btrfs_item_key_to_cpu(l, &found_key, slot); diff --git a/volumes.c b/volumes.c index 03bfb8cca..cde20ad7e 100644 --- a/volumes.c +++ b/volumes.c @@ -99,6 +99,8 @@ static int device_list_add(const char *path, memcpy(fs_devices->fsid, disk_super->fsid, BTRFS_FSID_SIZE); fs_devices->latest_devid = devid; fs_devices->latest_trans = found_transid; + fs_devices->earliest_devid = devid; + fs_devices->earliest_trans = found_transid; fs_devices->lowest_devid = (u64)-1; device = NULL; } else { @@ -133,8 +135,11 @@ static int device_list_add(const char *path, if (found_transid > fs_devices->latest_trans) { fs_devices->latest_devid = devid; fs_devices->latest_trans = found_transid; + } else if (found_transid < fs_devices->earliest_trans) { + fs_devices->earliest_devid = devid; + fs_devices->earliest_trans = found_transid; } - if (fs_devices->lowest_devid > devid) { + if (devid < fs_devices->lowest_devid) { fs_devices->lowest_devid = devid; } *fs_devices_ret = fs_devices; @@ -183,6 +188,8 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, int flags) if (device->devid == fs_devices->latest_devid) fs_devices->latest_bdev = fd; + if (device->devid == fs_devices->earliest_devid) + fs_devices->earliest_bdev = fd; if (device->devid == fs_devices->lowest_devid) fs_devices->lowest_bdev = fd; device->fd = fd; diff --git a/volumes.h b/volumes.h index 7104d36e6..08c53e474 100644 --- a/volumes.h +++ b/volumes.h @@ -64,11 +64,15 @@ struct btrfs_device { struct btrfs_fs_devices { u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */ - /* the device with this id has the most recent coyp of the super */ + /* the device with this id has the most recent copy of the super */ u64 latest_devid; u64 latest_trans; + /* the device with this id has the least recent copy of the super */ + u64 earliest_devid; + u64 earliest_trans; u64 lowest_devid; int latest_bdev; + int earliest_bdev; int lowest_bdev; struct list_head devices; struct list_head list;