diff --git a/btrfs-progs-build-fixes.patch b/btrfs-progs-build-fixes.patch index a78b451..136ac62 100644 --- a/btrfs-progs-build-fixes.patch +++ b/btrfs-progs-build-fixes.patch @@ -1,7 +1,6 @@ -diff --git a/btrfsck.c b/btrfsck.c -index 63e44d1..1e040c4 100644 ---- a/btrfsck.c -+++ b/btrfsck.c +diff -up btrfs-progs-0.19/btrfsck.c.orig btrfs-progs-0.19/btrfsck.c +--- btrfs-progs-0.19/btrfsck.c.orig 2012-04-11 10:44:44.000000000 -0400 ++++ btrfs-progs-0.19/btrfsck.c 2012-04-11 10:45:35.769767880 -0400 @@ -22,7 +22,9 @@ #include #include @@ -9,23 +8,22 @@ index 63e44d1..1e040c4 100644 +#include #include +#include + #include #include "kerncompat.h" #include "ctree.h" - #include "disk-io.h" -diff --git a/mkfs.c b/mkfs.c -index 2e99b95..638f4c2 100644 ---- a/mkfs.c -+++ b/mkfs.c -@@ -348,7 +348,7 @@ int main(int ac, char **av) +diff -up btrfs-progs-0.19/mkfs.c.orig btrfs-progs-0.19/mkfs.c +--- btrfs-progs-0.19/mkfs.c.orig 2012-04-11 10:44:44.652047854 -0400 ++++ btrfs-progs-0.19/mkfs.c 2012-04-11 10:46:03.727553072 -0400 +@@ -1198,7 +1198,7 @@ int main(int ac, char **av) u64 alloc_start = 0; - u64 metadata_profile = BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_DUP; - u64 data_profile = BTRFS_BLOCK_GROUP_RAID0; + u64 metadata_profile = 0; + u64 data_profile = 0; - u32 leafsize = getpagesize(); + u32 leafsize = sysconf(_SC_PAGESIZE); u32 sectorsize = 4096; u32 nodesize = leafsize; u32 stripesize = 4096; -@@ -405,7 +405,7 @@ int main(int ac, char **av) +@@ -1270,7 +1270,7 @@ int main(int ac, char **av) print_usage(); } } diff --git a/btrfs-progs-fix-labels.patch b/btrfs-progs-fix-labels.patch index 92a52e4..c9b0c32 100644 --- a/btrfs-progs-fix-labels.patch +++ b/btrfs-progs-fix-labels.patch @@ -1,17 +1,16 @@ -diff --git a/mkfs.c b/mkfs.c -index d664254..138bcc9 100644 ---- a/mkfs.c -+++ b/mkfs.c -@@ -294,7 +294,6 @@ static u64 parse_profile(char *s) +diff -up btrfs-progs-0.19/mkfs.c.orig btrfs-progs-0.19/mkfs.c +--- btrfs-progs-0.19/mkfs.c.orig 2012-04-11 10:38:26.825973948 -0400 ++++ btrfs-progs-0.19/mkfs.c 2012-04-11 10:39:46.031360540 -0400 +@@ -372,7 +372,6 @@ static u64 parse_profile(char *s) static char *parse_label(char *input) { - int i; int len = strlen(input); - if (len > BTRFS_LABEL_SIZE) { -@@ -302,12 +301,6 @@ static char *parse_label(char *input) - BTRFS_LABEL_SIZE); + if (len >= BTRFS_LABEL_SIZE) { +@@ -380,12 +379,6 @@ static char *parse_label(char *input) + BTRFS_LABEL_SIZE - 1); exit(1); } - for (i = 0; i < len; i++) { diff --git a/btrfs-progs-upstream.patch b/btrfs-progs-upstream.patch index e81558f..83e06a0 100644 --- a/btrfs-progs-upstream.patch +++ b/btrfs-progs-upstream.patch @@ -1,74 +1,251 @@ +diff --git a/.gitignore b/.gitignore +new file mode 100644 +index 0000000..0e560d5 +--- /dev/null ++++ b/.gitignore +@@ -0,0 +1,15 @@ ++*.o ++.*.o.d ++version.h ++man/*.gz ++btrfs ++btrfs-debug-tree ++btrfs-map-logical ++btrfs-show ++btrfs-vol ++btrfsck ++btrfsctl ++find-root ++mkfs.btrfs ++repair ++restore +diff --git a/INSTALL b/INSTALL +index 16b45a5..6afbd90 100644 +--- a/INSTALL ++++ b/INSTALL +@@ -22,27 +22,38 @@ in the e2fsprogs sources, and is usually available as libuuid or + e2fsprogs-devel from various distros. + + Building the utilities is just make ; make install. The programs go +-into /usr/local/bin. The commands available are: ++into /usr/local/bin. The mains commands available are: + + mkfs.btrfs: create a filesystem + +-btrfsctl: control program to create snapshots and subvolumes: +- ++btrfs: control program to create snapshots and subvolumes: ++ # mount a btrfs filesystem + mount /dev/sda2 /mnt +- btrfsctl -s new_subvol_name /mnt +- btrfsctl -s snapshot_of_default /mnt/default +- btrfsctl -s snapshot_of_new_subvol /mnt/new_subvol_name +- btrfsctl -s snapshot_of_a_snapshot /mnt/snapshot_of_new_subvol ++ ++ # create a subvolume ++ btrfs subvolume create /mnt/new_subvol_name ++ ++ # snapshot of a subvolume ++ btrfs subvolume snapshot /mnt/default /mnt/snapshot_of_default ++ btrfs subvolume snapshot /mnt/snapshot_of_default \ ++ /mnt/snapshot_of_a_snapshot ++ ++ # list of the subvolumes + ls /mnt + default snapshot_of_a_snapshot snapshot_of_new_subvol + new_subvol_name snapshot_of_default + +- Snapshots and subvolumes cannot be deleted right now, but you can +- rm -rf all the files and directories inside them. ++ # removal of a subvolume or a snapshot ++ btrfs subvolume delete /mn/snapshot_of_a_snapshot ++ ++ # look a the btrfs man page for further information ++ man btrfs + + btrfsck: do a limited check of the FS extent trees. + +-debug-tree: print all of the FS metadata in text form. Example: ++btrfs-debug-tree: print all of the FS metadata in text form. Example: ++ ++ btrfs-debug-tree /dev/sda2 >& big_output_file ++ + +- debug-tree /dev/sda2 >& big_output_file + diff --git a/Makefile b/Makefile -index 8097b5a..6e6f6c6 100644 +index 8097b5a..79818e6 100644 --- a/Makefile +++ b/Makefile -@@ -4,7 +4,7 @@ CFLAGS = -g -Werror -Os +@@ -1,28 +1,32 @@ +-CC=gcc ++CC = gcc + AM_CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 +-CFLAGS = -g -Werror -Os ++CFLAGS = -g -O0 objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ root-tree.o dir-item.o file-item.o inode-item.o \ inode-map.o crc32c.o rbtree.o extent-cache.o extent_io.o \ - volumes.o utils.o -+ volumes.o utils.o btrfs-list.o ++ volumes.o utils.o btrfs-list.o btrfslabel.o repair.o ++cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \ ++ cmds-inspect.o cmds-balance.o - # - CHECKFLAGS=-D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \ -@@ -16,7 +16,9 @@ prefix ?= /usr/local +-# +-CHECKFLAGS=-D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \ +- -Wuninitialized -Wshadow -Wundef ++CHECKFLAGS= -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \ ++ -Wuninitialized -Wshadow -Wundef + DEPFLAGS = -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@ + +-INSTALL= install ++INSTALL = install + prefix ?= /usr/local bindir = $(prefix)/bin LIBS=-luuid ++RESTORE_LIBS=-lz -progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck +progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \ -+ btrfs \ -+ btrfs-map-logical ++ btrfs btrfs-map-logical btrfs-image btrfs-zero-log btrfs-convert \ ++ btrfs-find-root btrfs-restore btrfstune # make C=1 to enable sparse ifdef C -@@ -35,6 +37,10 @@ all: version $(progs) manpages +- check=sparse $(CHECKFLAGS) ++ check = sparse $(CHECKFLAGS) + else +- check=ls ++ check = ls + endif + + .c.o: +@@ -35,38 +39,66 @@ all: version $(progs) manpages version: bash version.sh -+btrfs: $(objects) btrfs.o btrfs_cmds.o -+ gcc $(CFLAGS) -o btrfs btrfs.o btrfs_cmds.o \ -+ $(objects) $(LDFLAGS) $(LIBS) ++btrfs: $(objects) btrfs.o help.o common.o $(cmds_objects) ++ $(CC) $(CFLAGS) -o btrfs btrfs.o help.o common.o $(cmds_objects) \ ++ $(objects) $(LDFLAGS) $(LIBS) -lpthread ++ ++calc-size: $(objects) calc-size.o ++ gcc $(CFLAGS) -o calc-size calc-size.o $(objects) $(LDFLAGS) $(LIBS) ++ ++btrfs-find-root: $(objects) find-root.o ++ gcc $(CFLAGS) -o btrfs-find-root find-root.o $(objects) $(LDFLAGS) $(LIBS) ++ ++btrfs-restore: $(objects) restore.o ++ gcc $(CFLAGS) -o btrfs-restore restore.o $(objects) $(LDFLAGS) $(LIBS) $(RESTORE_LIBS) + btrfsctl: $(objects) btrfsctl.o - gcc $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS) +- gcc $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS) ++ $(CC) $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS) -@@ -53,9 +59,15 @@ mkfs.btrfs: $(objects) mkfs.o - btrfs-debug-tree: $(objects) debug-tree.o - gcc $(CFLAGS) -o btrfs-debug-tree $(objects) debug-tree.o $(LDFLAGS) $(LIBS) + btrfs-vol: $(objects) btrfs-vol.o +- gcc $(CFLAGS) -o btrfs-vol btrfs-vol.o $(objects) $(LDFLAGS) $(LIBS) ++ $(CC) $(CFLAGS) -o btrfs-vol btrfs-vol.o $(objects) $(LDFLAGS) $(LIBS) + btrfs-show: $(objects) btrfs-show.o +- gcc $(CFLAGS) -o btrfs-show btrfs-show.o $(objects) $(LDFLAGS) $(LIBS) ++ $(CC) $(CFLAGS) -o btrfs-show btrfs-show.o $(objects) $(LDFLAGS) $(LIBS) + + btrfsck: $(objects) btrfsck.o +- gcc $(CFLAGS) -o btrfsck btrfsck.o $(objects) $(LDFLAGS) $(LIBS) ++ $(CC) $(CFLAGS) -o btrfsck btrfsck.o $(objects) $(LDFLAGS) $(LIBS) + + mkfs.btrfs: $(objects) mkfs.o +- gcc $(CFLAGS) -o mkfs.btrfs $(objects) mkfs.o $(LDFLAGS) $(LIBS) ++ $(CC) $(CFLAGS) -o mkfs.btrfs $(objects) mkfs.o $(LDFLAGS) $(LIBS) + + btrfs-debug-tree: $(objects) debug-tree.o +- gcc $(CFLAGS) -o btrfs-debug-tree $(objects) debug-tree.o $(LDFLAGS) $(LIBS) ++ $(CC) $(CFLAGS) -o btrfs-debug-tree $(objects) debug-tree.o $(LDFLAGS) $(LIBS) ++ +btrfs-zero-log: $(objects) btrfs-zero-log.o -+ gcc $(CFLAGS) -o btrfs-zero-log $(objects) btrfs-zero-log.o $(LDFLAGS) $(LIBS) ++ $(CC) $(CFLAGS) -o btrfs-zero-log $(objects) btrfs-zero-log.o $(LDFLAGS) $(LIBS) + - btrfstune: $(objects) btrfstune.o - gcc $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS) ++btrfs-select-super: $(objects) btrfs-select-super.o ++ $(CC) $(CFLAGS) -o btrfs-select-super $(objects) btrfs-select-super.o $(LDFLAGS) $(LIBS) + btrfstune: $(objects) btrfstune.o +- gcc $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS) ++ $(CC) $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS) ++ +btrfs-map-logical: $(objects) btrfs-map-logical.o -+ gcc $(CFLAGS) -o btrfs-map-logical $(objects) btrfs-map-logical.o $(LDFLAGS) $(LIBS) ++ $(CC) $(CFLAGS) -o btrfs-map-logical $(objects) btrfs-map-logical.o $(LDFLAGS) $(LIBS) + ++btrfs-corrupt-block: $(objects) btrfs-corrupt-block.o ++ $(CC) $(CFLAGS) -o btrfs-corrupt-block $(objects) btrfs-corrupt-block.o $(LDFLAGS) $(LIBS) + btrfs-image: $(objects) btrfs-image.o - gcc $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS) +- gcc $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS) ++ $(CC) $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS) -@@ -66,7 +78,10 @@ quick-test: $(objects) quick-test.o - gcc $(CFLAGS) -o quick-test $(objects) quick-test.o $(LDFLAGS) $(LIBS) + dir-test: $(objects) dir-test.o +- gcc $(CFLAGS) -o dir-test $(objects) dir-test.o $(LDFLAGS) $(LIBS) ++ $(CC) $(CFLAGS) -o dir-test $(objects) dir-test.o $(LDFLAGS) $(LIBS) - convert: $(objects) convert.o -- gcc $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs $(LDFLAGS) $(LIBS) -+ gcc $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs -lcom_err $(LDFLAGS) $(LIBS) + quick-test: $(objects) quick-test.o +- gcc $(CFLAGS) -o quick-test $(objects) quick-test.o $(LDFLAGS) $(LIBS) ++ $(CC) $(CFLAGS) -o quick-test $(objects) quick-test.o $(LDFLAGS) $(LIBS) + ++btrfs-convert: $(objects) convert.o ++ $(CC) $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs -lcom_err $(LDFLAGS) $(LIBS) + +-convert: $(objects) convert.o +- gcc $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs $(LDFLAGS) $(LIBS) +ioctl-test: $(objects) ioctl-test.o -+ gcc $(CFLAGS) -o ioctl-test $(objects) ioctl-test.o $(LDFLAGS) $(LIBS) ++ $(CC) $(CFLAGS) -o ioctl-test $(objects) ioctl-test.o $(LDFLAGS) $(LIBS) manpages: cd man; make -diff --git a/btrfs-defrag.c b/btrfs-defrag.c +@@ -75,12 +107,12 @@ install-man: + cd man; make install + + clean : +- rm -f $(progs) cscope.out *.o .*.d btrfs-convert ++ 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 + cd man; make clean + + install: $(progs) install-man + $(INSTALL) -m755 -d $(DESTDIR)$(bindir) + $(INSTALL) $(progs) $(DESTDIR)$(bindir) +- if [ -e btrfs-convert ]; then $(INSTALL) btrfs-convert $(DESTDIR)$(bindir); fi + + -include .*.d +diff --git a/bcp b/bcp +index 5729e91..e7ca641 100755 +--- a/bcp ++++ b/bcp +@@ -136,8 +136,7 @@ for srci in xrange(0, src_args): + srcname = os.path.join(dirpath, x) + statinfo = os.lstat(srcname) + +- if srcname.startswith(src): +- part = srcname[len(src) + 1:] ++ part = os.path.relpath(srcname, src) + + if stat.S_ISLNK(statinfo.st_mode): + copylink(srcname, dst, part, statinfo, None) +@@ -152,8 +151,7 @@ for srci in xrange(0, src_args): + + for f in filenames: + srcname = os.path.join(dirpath, f) +- if srcname.startswith(src): +- part = srcname[len(src) + 1:] ++ part = os.path.relpath(srcname, src) + + statinfo = os.lstat(srcname) + copyfile(srcname, dst, part, statinfo, None) +diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c new file mode 100644 -index 0000000..8f1525a +index 0000000..7051e99 --- /dev/null -+++ b/btrfs-defrag.c -@@ -0,0 +1,39 @@ ++++ b/btrfs-corrupt-block.c +@@ -0,0 +1,396 @@ +/* -+ * Copyright (C) 2010 Oracle. All rights reserved. ++ * Copyright (C) 2009 Oracle. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public @@ -85,1601 +262,1380 @@ index 0000000..8f1525a + * Boston, MA 021110-1307, USA. + */ + -+#ifndef __CHECKER__ -+#include -+#include -+#include "ioctl.h" -+#endif ++#define _XOPEN_SOURCE 500 ++#define _GNU_SOURCE 1 +#include +#include -+#include -+#include +#include -+#include +#include -+#include -+#include +#include +#include "kerncompat.h" +#include "ctree.h" ++#include "volumes.h" ++#include "disk-io.h" ++#include "print-tree.h" +#include "transaction.h" -+#include "utils.h" ++#include "list.h" +#include "version.h" + -diff --git a/btrfs-list.c b/btrfs-list.c -new file mode 100644 -index 0000000..93766a8 ---- /dev/null -+++ b/btrfs-list.c -@@ -0,0 +1,835 @@ -+/* -+ * Copyright (C) 2010 Oracle. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public -+ * License v2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public -+ * License along with this program; if not, write to the -+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, -+ * Boston, MA 021110-1307, USA. -+ */ -+ -+#define _GNU_SOURCE -+#ifndef __CHECKER__ -+#include -+#include -+#include "ioctl.h" -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "kerncompat.h" -+#include "ctree.h" -+#include "transaction.h" -+#include "utils.h" -+#include "version.h" ++struct extent_buffer *debug_corrupt_block(struct btrfs_root *root, u64 bytenr, ++ u32 blocksize, int copy) ++{ ++ int ret; ++ struct extent_buffer *eb; ++ u64 length; ++ struct btrfs_multi_bio *multi = NULL; ++ struct btrfs_device *device; ++ int num_copies; ++ int mirror_num = 1; + -+/* we store all the roots we find in an rbtree so that we can -+ * search for them later. -+ */ -+struct root_lookup { -+ struct rb_root root; -+}; ++ eb = btrfs_find_create_tree_block(root, bytenr, blocksize); ++ if (!eb) ++ return NULL; + -+/* -+ * one of these for each root we find. -+ */ -+struct root_info { -+ struct rb_node rb_node; ++ length = blocksize; ++ while (1) { ++ ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, ++ eb->start, &length, &multi, mirror_num); ++ BUG_ON(ret); ++ device = multi->stripes[0].dev; ++ eb->fd = device->fd; ++ device->total_ios++; ++ eb->dev_bytenr = multi->stripes[0].physical; + -+ /* this root's id */ -+ u64 root_id; ++ fprintf(stdout, "mirror %d logical %Lu physical %Lu " ++ "device %s\n", mirror_num, (unsigned long long)bytenr, ++ (unsigned long long)eb->dev_bytenr, device->name); ++ kfree(multi); + -+ /* the id of the root that references this one */ -+ u64 ref_tree; ++ if (!copy || mirror_num == copy) { ++ ret = read_extent_from_disk(eb); ++ printf("corrupting %llu copy %d\n", eb->start, ++ mirror_num); ++ memset(eb->data, 0, eb->len); ++ write_extent_to_disk(eb); ++ fsync(eb->fd); ++ } + -+ /* the dir id we're in from ref_tree */ -+ u64 dir_id; ++ num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, ++ eb->start, eb->len); ++ if (num_copies == 1) ++ break; + -+ /* path from the subvol we live in to this root, including the -+ * root's name. This is null until we do the extra lookup ioctl. -+ */ -+ char *path; ++ mirror_num++; ++ if (mirror_num > num_copies) ++ break; ++ } ++ return eb; ++} + -+ /* the name of this root in the directory it lives in */ -+ char name[]; -+}; ++static void print_usage(void) ++{ ++ fprintf(stderr, "usage: btrfs-map-logical [options] mount_point\n"); ++ fprintf(stderr, "\t-l Logical extent to map\n"); ++ fprintf(stderr, "\t-c Copy of the extent to read (usually 1 or 2)\n"); ++ fprintf(stderr, "\t-o Output file to hold the extent\n"); ++ fprintf(stderr, "\t-b Number of bytes to read\n"); ++ exit(1); ++} + -+static void root_lookup_init(struct root_lookup *tree) ++static void corrupt_keys(struct btrfs_trans_handle *trans, ++ struct btrfs_root *root, ++ struct extent_buffer *eb) +{ -+ tree->root.rb_node = NULL; ++ int slot; ++ int bad_slot; ++ int nr; ++ struct btrfs_disk_key bad_key;; ++ ++ nr = btrfs_header_nritems(eb); ++ if (nr == 0) ++ return; ++ ++ slot = rand() % nr; ++ bad_slot = rand() % nr; ++ ++ if (bad_slot == slot) ++ return; ++ ++ fprintf(stderr, "corrupting keys in block %llu slot %d swapping with %d\n", ++ (unsigned long long)eb->start, slot, bad_slot); ++ ++ if (btrfs_header_level(eb) == 0) { ++ btrfs_item_key(eb, &bad_key, bad_slot); ++ btrfs_set_item_key(eb, &bad_key, slot); ++ } else { ++ btrfs_node_key(eb, &bad_key, bad_slot); ++ btrfs_set_node_key(eb, &bad_key, slot); ++ } ++ btrfs_mark_buffer_dirty(eb); ++ if (!trans) { ++ csum_tree_block(root, eb, 0); ++ write_extent_to_disk(eb); ++ } +} + -+static int comp_entry(struct root_info *entry, u64 root_id, u64 ref_tree) ++ ++static int corrupt_keys_in_block(struct btrfs_root *root, u64 bytenr) +{ -+ if (entry->root_id > root_id) -+ return 1; -+ if (entry->root_id < root_id) -+ return -1; -+ if (entry->ref_tree > ref_tree) -+ return 1; -+ if (entry->ref_tree < ref_tree) -+ return -1; ++ struct extent_buffer *eb; ++ ++ eb = read_tree_block(root, bytenr, root->leafsize, 0); ++ if (!eb) ++ return -EIO;; ++ ++ corrupt_keys(NULL, root, eb); ++ free_extent_buffer(eb); + return 0; +} + -+/* -+ * insert a new root into the tree. returns the existing root entry -+ * if one is already there. Both root_id and ref_tree are used -+ * as the key -+ */ -+static struct rb_node *tree_insert(struct rb_root *root, u64 root_id, -+ u64 ref_tree, struct rb_node *node) ++static int corrupt_extent(struct btrfs_trans_handle *trans, ++ struct btrfs_root *root, u64 bytenr, int copy) +{ -+ struct rb_node ** p = &root->rb_node; -+ struct rb_node * parent = NULL; -+ struct root_info *entry; -+ int comp; ++ struct btrfs_key key; ++ struct extent_buffer *leaf; ++ u32 item_size; ++ unsigned long ptr; ++ struct btrfs_path *path; ++ int ret; ++ int slot; ++ int should_del = rand() % 3; + -+ while(*p) { -+ parent = *p; -+ entry = rb_entry(parent, struct root_info, rb_node); ++ path = btrfs_alloc_path(); + -+ comp = comp_entry(entry, root_id, ref_tree); ++ key.objectid = bytenr; ++ key.type = (u8)-1; ++ key.offset = (u64)-1; + -+ if (comp < 0) -+ p = &(*p)->rb_left; -+ else if (comp > 0) -+ p = &(*p)->rb_right; -+ else -+ return parent; ++ while(1) { ++ ret = btrfs_search_slot(trans, root->fs_info->extent_root, ++ &key, path, -1, 1); ++ if (ret < 0) ++ break; ++ ++ if (ret > 0) { ++ if (path->slots[0] == 0) ++ break; ++ path->slots[0]--; ++ ret = 0; ++ } ++ leaf = path->nodes[0]; ++ slot = path->slots[0]; ++ btrfs_item_key_to_cpu(leaf, &key, slot); ++ if (key.objectid != bytenr) ++ break; ++ ++ if (key.type != BTRFS_EXTENT_ITEM_KEY && ++ key.type != BTRFS_TREE_BLOCK_REF_KEY && ++ key.type != BTRFS_EXTENT_DATA_REF_KEY && ++ key.type != BTRFS_EXTENT_REF_V0_KEY && ++ key.type != BTRFS_SHARED_BLOCK_REF_KEY && ++ key.type != BTRFS_SHARED_DATA_REF_KEY) ++ goto next; ++ ++ if (should_del) { ++ fprintf(stderr, "deleting extent record: key %Lu %u %Lu\n", ++ key.objectid, key.type, key.offset); ++ ++ if (key.type == BTRFS_EXTENT_ITEM_KEY) { ++ /* make sure this extent doesn't get ++ * reused for other purposes */ ++ btrfs_pin_extent(root->fs_info, ++ key.objectid, key.offset); ++ } ++ ++ btrfs_del_item(trans, root, path); ++ } else { ++ fprintf(stderr, "corrupting extent record: key %Lu %u %Lu\n", ++ key.objectid, key.type, key.offset); ++ ptr = btrfs_item_ptr_offset(leaf, slot); ++ item_size = btrfs_item_size_nr(leaf, slot); ++ memset_extent_buffer(leaf, 0, ptr, item_size); ++ btrfs_mark_buffer_dirty(leaf); ++ } ++next: ++ btrfs_release_path(NULL, path); ++ ++ if (key.offset > 0) ++ key.offset--; ++ if (key.offset == 0) ++ break; + } + -+ entry = rb_entry(parent, struct root_info, rb_node); -+ rb_link_node(node, parent, p); -+ rb_insert_color(node, root); -+ return NULL; ++ btrfs_free_path(path); ++ return 0; +} + -+/* -+ * find a given root id in the tree. We return the smallest one, -+ * rb_next can be used to move forward looking for more if required -+ */ -+static struct root_info *tree_search(struct rb_root *root, u64 root_id) ++static void btrfs_corrupt_extent_leaf(struct btrfs_trans_handle *trans, ++ struct btrfs_root *root, struct extent_buffer *eb) +{ -+ struct rb_node * n = root->rb_node; -+ struct root_info *entry; ++ u32 nr = btrfs_header_nritems(eb); ++ u32 victim = rand() % nr; ++ u64 objectid; ++ struct btrfs_key key; + -+ while(n) { -+ entry = rb_entry(n, struct root_info, rb_node); ++ btrfs_item_key_to_cpu(eb, &key, victim); ++ objectid = key.objectid; ++ corrupt_extent(trans, root, objectid, 1); ++} + -+ if (entry->root_id < root_id) -+ n = n->rb_left; -+ else if (entry->root_id > root_id) -+ n = n->rb_right; -+ else { -+ struct root_info *prev; -+ struct rb_node *prev_n; -+ while (1) { -+ prev_n = rb_prev(n); -+ if (!prev_n) -+ break; -+ prev = rb_entry(prev_n, struct root_info, -+ rb_node); -+ if (prev->root_id != root_id) -+ break; -+ entry = prev; -+ n = prev_n; -+ } -+ return entry; -+ } ++static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans, ++ struct btrfs_root *root, struct extent_buffer *eb) ++{ ++ int i; ++ u32 nr; ++ ++ if (!eb) ++ return; ++ ++ nr = btrfs_header_nritems(eb); ++ if (btrfs_is_leaf(eb)) { ++ btrfs_corrupt_extent_leaf(trans, root, eb); ++ return; + } -+ return NULL; -+} + -+/* -+ * this allocates a new root in the lookup tree. -+ * -+ * root_id should be the object id of the root -+ * -+ * ref_tree is the objectid of the referring root. -+ * -+ * dir_id is the directory in ref_tree where this root_id can be found. -+ * -+ * name is the name of root_id in that directory -+ * -+ * name_len is the length of name -+ */ -+static int add_root(struct root_lookup *root_lookup, -+ u64 root_id, u64 ref_tree, u64 dir_id, char *name, -+ int name_len) -+{ -+ struct root_info *ri; -+ struct rb_node *ret; -+ ri = malloc(sizeof(*ri) + name_len + 1); -+ if (!ri) { -+ printf("memory allocation failed\n"); -+ exit(1); ++ if (btrfs_header_level(eb) == 1 && eb != root->node) { ++ if (rand() % 5) ++ return; + } -+ memset(ri, 0, sizeof(*ri) + name_len + 1); -+ ri->path = NULL; -+ ri->dir_id = dir_id; -+ ri->root_id = root_id; -+ ri->ref_tree = ref_tree; -+ strncpy(ri->name, name, name_len); + -+ ret = tree_insert(&root_lookup->root, root_id, ref_tree, &ri->rb_node); -+ if (ret) { -+ printf("failed to insert tree %llu\n", (unsigned long long)root_id); -+ exit(1); ++ for (i = 0; i < nr; i++) { ++ struct extent_buffer *next; ++ ++ next = read_tree_block(root, btrfs_node_blockptr(eb, i), ++ root->leafsize, btrfs_node_ptr_generation(eb, i)); ++ if (!next) ++ continue; ++ btrfs_corrupt_extent_tree(trans, root, next); ++ free_extent_buffer(next); + } -+ return 0; +} + -+/* -+ * for a given root_info, search through the root_lookup tree to construct -+ * the full path name to it. -+ * -+ * This can't be called until all the root_info->path fields are filled -+ * in by lookup_ino_path -+ */ -+static int resolve_root(struct root_lookup *rl, struct root_info *ri) -+{ -+ u64 top_id; -+ char *full_path = NULL; -+ int len = 0; -+ struct root_info *found; ++static struct option long_options[] = { ++ /* { "byte-count", 1, NULL, 'b' }, */ ++ { "logical", 1, NULL, 'l' }, ++ { "copy", 1, NULL, 'c' }, ++ { "bytes", 1, NULL, 'b' }, ++ { "extent-record", 0, NULL, 'e' }, ++ { "extent-tree", 0, NULL, 'E' }, ++ { "keys", 0, NULL, 'k' }, ++ { 0, 0, 0, 0} ++}; + -+ /* -+ * we go backwards from the root_info object and add pathnames -+ * from parent directories as we go. -+ */ -+ found = ri; -+ while (1) { -+ char *tmp; -+ u64 next; -+ int add_len = strlen(found->path); + -+ /* room for / and for null */ -+ tmp = malloc(add_len + 2 + len); -+ if (full_path) { -+ memcpy(tmp + add_len + 1, full_path, len); -+ tmp[add_len] = '/'; -+ memcpy(tmp, found->path, add_len); -+ tmp [add_len + len + 1] = '\0'; -+ free(full_path); -+ full_path = tmp; -+ len += add_len + 1; -+ } else { -+ full_path = strdup(found->path); -+ len = add_len; -+ } ++int main(int ac, char **av) ++{ ++ struct cache_tree root_cache; ++ struct btrfs_root *root; ++ struct extent_buffer *eb; ++ char *dev; ++ u64 logical = 0; ++ int ret = 0; ++ int option_index = 0; ++ int copy = 0; ++ u64 bytes = 4096; ++ int extent_rec = 0; ++ int extent_tree = 0; ++ int corrupt_block_keys = 0; + -+ next = found->ref_tree; -+ /* if the ref_tree refers to ourselves, we're at the top */ -+ if (next == found->root_id) { -+ top_id = next; -+ break; -+ } ++ srand(128); + -+ /* -+ * if the ref_tree wasn't in our tree of roots, we're -+ * at the top -+ */ -+ found = tree_search(&rl->root, next); -+ if (!found) { -+ top_id = next; ++ while(1) { ++ int c; ++ c = getopt_long(ac, av, "l:c:eEk", long_options, ++ &option_index); ++ if (c < 0) + break; ++ switch(c) { ++ case 'l': ++ logical = atoll(optarg); ++ if (logical == 0) { ++ fprintf(stderr, ++ "invalid extent number\n"); ++ print_usage(); ++ } ++ break; ++ case 'c': ++ copy = atoi(optarg); ++ if (copy == 0) { ++ fprintf(stderr, ++ "invalid copy number\n"); ++ print_usage(); ++ } ++ break; ++ case 'b': ++ bytes = atoll(optarg); ++ if (bytes == 0) { ++ fprintf(stderr, ++ "invalid byte count\n"); ++ print_usage(); ++ } ++ break; ++ case 'e': ++ extent_rec = 1; ++ break; ++ case 'E': ++ extent_tree = 1; ++ break; ++ case 'k': ++ corrupt_block_keys = 1; ++ break; ++ default: ++ print_usage(); + } + } -+ printf("ID %llu top level %llu path %s\n", ri->root_id, top_id, -+ full_path); -+ free(full_path); -+ return 0; -+} -+ -+/* -+ * for a single root_info, ask the kernel to give us a path name -+ * inside it's ref_root for the dir_id where it lives. -+ * -+ * This fills in root_info->path with the path to the directory and and -+ * appends this root's name. -+ */ -+static int lookup_ino_path(int fd, struct root_info *ri) -+{ -+ struct btrfs_ioctl_ino_lookup_args args; -+ int ret; ++ ac = ac - optind; ++ if (ac == 0) ++ print_usage(); ++ if (logical == 0 && !extent_tree) ++ print_usage(); ++ if (copy < 0) ++ print_usage(); + -+ if (ri->path) -+ return 0; ++ dev = av[optind]; + -+ memset(&args, 0, sizeof(args)); -+ args.treeid = ri->ref_tree; -+ args.objectid = ri->dir_id; ++ radix_tree_init(); ++ cache_tree_init(&root_cache); + -+ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); -+ if (ret) { -+ fprintf(stderr, "ERROR: Failed to lookup path for root %llu\n", -+ (unsigned long long)ri->ref_tree); -+ return ret; ++ root = open_ctree(dev, 0, 1); ++ if (!root) { ++ fprintf(stderr, "Open ctree failed\n"); ++ exit(1); ++ } ++ if (extent_rec) { ++ struct btrfs_trans_handle *trans; ++ trans = btrfs_start_transaction(root, 1); ++ ret = corrupt_extent (trans, root, logical, 0); ++ btrfs_commit_transaction(trans, root); ++ goto out_close; ++ } ++ if (extent_tree) { ++ struct btrfs_trans_handle *trans; ++ trans = btrfs_start_transaction(root, 1); ++ btrfs_corrupt_extent_tree(trans, root->fs_info->extent_root, ++ root->fs_info->extent_root->node); ++ btrfs_commit_transaction(trans, root); ++ goto out_close; + } + -+ if (args.name[0]) { -+ /* -+ * we're in a subdirectory of ref_tree, the kernel ioctl -+ * puts a / in there for us -+ */ -+ ri->path = malloc(strlen(ri->name) + strlen(args.name) + 1); -+ if (!ri->path) { -+ perror("malloc failed"); -+ exit(1); -+ } -+ strcpy(ri->path, args.name); -+ strcat(ri->path, ri->name); -+ } else { -+ /* we're at the root of ref_tree */ -+ ri->path = strdup(ri->name); -+ if (!ri->path) { -+ perror("strdup failed"); -+ exit(1); ++ if (bytes == 0) ++ bytes = root->sectorsize; ++ ++ bytes = (bytes + root->sectorsize - 1) / root->sectorsize; ++ bytes *= root->sectorsize; ++ ++ while (bytes > 0) { ++ if (corrupt_block_keys) { ++ corrupt_keys_in_block(root, logical); ++ } else { ++ eb = debug_corrupt_block(root, logical, ++ root->sectorsize, copy); ++ free_extent_buffer(eb); + } ++ logical += root->sectorsize; ++ bytes -= root->sectorsize; + } -+ return 0; ++ return ret; ++out_close: ++ close_ctree(root); ++ return ret; +} -+ -+/* finding the generation for a given path is a two step process. -+ * First we use the inode loookup routine to find out the root id +diff --git a/btrfs-defrag.c b/btrfs-defrag.c +new file mode 100644 +index 0000000..8f1525a +--- /dev/null ++++ b/btrfs-defrag.c +@@ -0,0 +1,39 @@ ++/* ++ * Copyright (C) 2010 Oracle. All rights reserved. + * -+ * Then we use the tree search ioctl to scan all the root items for a -+ * given root id and spit out the latest generation we can find ++ * 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. + */ -+static u64 find_root_gen(int fd) -+{ -+ struct btrfs_ioctl_ino_lookup_args ino_args; -+ int ret; -+ struct btrfs_ioctl_search_args args; -+ struct btrfs_ioctl_search_key *sk = &args.key; -+ struct btrfs_ioctl_search_header *sh; -+ unsigned long off = 0; -+ u64 max_found = 0; -+ int i; + -+ memset(&ino_args, 0, sizeof(ino_args)); -+ ino_args.objectid = BTRFS_FIRST_FREE_OBJECTID; ++#ifndef __CHECKER__ ++#include ++#include ++#include "ioctl.h" ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "kerncompat.h" ++#include "ctree.h" ++#include "transaction.h" ++#include "utils.h" ++#include "version.h" + -+ /* this ioctl fills in ino_args->treeid */ -+ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args); -+ if (ret) { -+ fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu\n", -+ (unsigned long long)BTRFS_FIRST_FREE_OBJECTID); -+ return 0; -+ } +diff --git a/btrfs-list.c b/btrfs-list.c +new file mode 100644 +index 0000000..5f4a9be +--- /dev/null ++++ b/btrfs-list.c +@@ -0,0 +1,936 @@ ++/* ++ * Copyright (C) 2010 Oracle. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public ++ * License v2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public ++ * License along with this program; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ * Boston, MA 021110-1307, USA. ++ */ + -+ memset(&args, 0, sizeof(args)); ++#define _GNU_SOURCE ++#ifndef __CHECKER__ ++#include ++#include ++#include "ioctl.h" ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "kerncompat.h" ++#include "ctree.h" ++#include "transaction.h" ++#include "utils.h" + -+ sk->tree_id = 1; ++/* we store all the roots we find in an rbtree so that we can ++ * search for them later. ++ */ ++struct root_lookup { ++ struct rb_root root; ++}; + -+ /* -+ * there may be more than one ROOT_ITEM key if there are -+ * snapshots pending deletion, we have to loop through -+ * them. -+ */ -+ sk->min_objectid = ino_args.treeid; -+ sk->max_objectid = ino_args.treeid; -+ sk->max_type = BTRFS_ROOT_ITEM_KEY; -+ sk->min_type = BTRFS_ROOT_ITEM_KEY; -+ sk->max_offset = (u64)-1; -+ sk->max_transid = (u64)-1; -+ sk->nr_items = 4096; ++/* ++ * one of these for each root we find. ++ */ ++struct root_info { ++ struct rb_node rb_node; + -+ while (1) { -+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); -+ if (ret < 0) { -+ fprintf(stderr, "ERROR: can't perform the search\n"); -+ return 0; -+ } -+ /* the ioctl returns the number of item it found in nr_items */ -+ if (sk->nr_items == 0) -+ break; ++ /* this root's id */ ++ u64 root_id; + -+ off = 0; -+ for (i = 0; i < sk->nr_items; i++) { -+ struct btrfs_root_item *item; -+ sh = (struct btrfs_ioctl_search_header *)(args.buf + -+ off); ++ /* the id of the root that references this one */ ++ u64 ref_tree; + -+ off += sizeof(*sh); -+ item = (struct btrfs_root_item *)(args.buf + off); -+ off += sh->len; ++ /* the dir id we're in from ref_tree */ ++ u64 dir_id; + -+ sk->min_objectid = sh->objectid; -+ sk->min_type = sh->type; -+ sk->min_offset = sh->offset; ++ /* path from the subvol we live in to this root, including the ++ * root's name. This is null until we do the extra lookup ioctl. ++ */ ++ char *path; + -+ if (sh->objectid > ino_args.treeid) -+ break; ++ /* the name of this root in the directory it lives in */ ++ char name[]; ++}; + -+ if (sh->objectid == ino_args.treeid && -+ sh->type == BTRFS_ROOT_ITEM_KEY) { -+ max_found = max(max_found, -+ btrfs_root_generation(item)); -+ } -+ } -+ if (sk->min_offset < (u64)-1) -+ sk->min_offset++; -+ else -+ break; ++static void root_lookup_init(struct root_lookup *tree) ++{ ++ tree->root.rb_node = NULL; ++} + -+ if (sk->min_type != BTRFS_ROOT_ITEM_KEY) -+ break; -+ if (sk->min_objectid != BTRFS_ROOT_ITEM_KEY) -+ break; -+ } -+ return max_found; ++static int comp_entry(struct root_info *entry, u64 root_id, u64 ref_tree) ++{ ++ if (entry->root_id > root_id) ++ return 1; ++ if (entry->root_id < root_id) ++ return -1; ++ if (entry->ref_tree > ref_tree) ++ return 1; ++ if (entry->ref_tree < ref_tree) ++ return -1; ++ return 0; +} + -+/* pass in a directory id and this will return -+ * the full path of the parent directory inside its -+ * subvolume root. -+ * -+ * It may return NULL if it is in the root, or an ERR_PTR if things -+ * go badly. ++/* ++ * insert a new root into the tree. returns the existing root entry ++ * if one is already there. Both root_id and ref_tree are used ++ * as the key + */ -+static char *__ino_resolve(int fd, u64 dirid) ++static struct rb_node *tree_insert(struct rb_root *root, u64 root_id, ++ u64 ref_tree, struct rb_node *node) +{ -+ struct btrfs_ioctl_ino_lookup_args args; -+ int ret; -+ char *full; ++ struct rb_node ** p = &root->rb_node; ++ struct rb_node * parent = NULL; ++ struct root_info *entry; ++ int comp; + -+ memset(&args, 0, sizeof(args)); -+ args.objectid = dirid; ++ while(*p) { ++ parent = *p; ++ entry = rb_entry(parent, struct root_info, rb_node); + -+ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); -+ if (ret) { -+ fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu\n", -+ (unsigned long long)dirid); -+ return ERR_PTR(ret); -+ } ++ comp = comp_entry(entry, root_id, ref_tree); + -+ if (args.name[0]) { -+ /* -+ * we're in a subdirectory of ref_tree, the kernel ioctl -+ * puts a / in there for us -+ */ -+ full = strdup(args.name); -+ if (!full) { -+ perror("malloc failed"); -+ return ERR_PTR(-ENOMEM); -+ } -+ } else { -+ /* we're at the root of ref_tree */ -+ full = NULL; ++ if (comp < 0) ++ p = &(*p)->rb_left; ++ else if (comp > 0) ++ p = &(*p)->rb_right; ++ else ++ return parent; + } -+ return full; ++ ++ entry = rb_entry(parent, struct root_info, rb_node); ++ rb_link_node(node, parent, p); ++ rb_insert_color(node, root); ++ return NULL; +} + +/* -+ * simple string builder, returning a new string with both -+ * dirid and name ++ * find a given root id in the tree. We return the smallest one, ++ * rb_next can be used to move forward looking for more if required + */ -+char *build_name(char *dirid, char *name) ++static struct root_info *tree_search(struct rb_root *root, u64 root_id) +{ -+ char *full; -+ if (!dirid) -+ return strdup(name); ++ struct rb_node * n = root->rb_node; ++ struct root_info *entry; + -+ full = malloc(strlen(dirid) + strlen(name) + 1); -+ if (!full) -+ return NULL; -+ strcpy(full, dirid); -+ strcat(full, name); -+ return full; ++ while(n) { ++ entry = rb_entry(n, struct root_info, rb_node); ++ ++ if (entry->root_id < root_id) ++ n = n->rb_left; ++ else if (entry->root_id > root_id) ++ n = n->rb_right; ++ else { ++ struct root_info *prev; ++ struct rb_node *prev_n; ++ while (1) { ++ prev_n = rb_prev(n); ++ if (!prev_n) ++ break; ++ prev = rb_entry(prev_n, struct root_info, ++ rb_node); ++ if (prev->root_id != root_id) ++ break; ++ entry = prev; ++ n = prev_n; ++ } ++ return entry; ++ } ++ } ++ return NULL; +} + +/* -+ * given an inode number, this returns the full path name inside the subvolume -+ * to that file/directory. cache_dirid and cache_name are used to -+ * cache the results so we can avoid tree searches if a later call goes -+ * to the same directory or file name ++ * this allocates a new root in the lookup tree. ++ * ++ * root_id should be the object id of the root ++ * ++ * ref_tree is the objectid of the referring root. ++ * ++ * dir_id is the directory in ref_tree where this root_id can be found. ++ * ++ * name is the name of root_id in that directory ++ * ++ * name_len is the length of name + */ -+static char *ino_resolve(int fd, u64 ino, u64 *cache_dirid, char **cache_name) -+ ++static int add_root(struct root_lookup *root_lookup, ++ u64 root_id, u64 ref_tree, u64 dir_id, char *name, ++ int name_len) +{ -+ u64 dirid; -+ char *dirname; -+ char *name; -+ char *full; -+ int ret; -+ struct btrfs_ioctl_search_args args; -+ struct btrfs_ioctl_search_key *sk = &args.key; -+ struct btrfs_ioctl_search_header *sh; -+ unsigned long off = 0; -+ int namelen; ++ struct root_info *ri; ++ struct rb_node *ret; ++ ri = malloc(sizeof(*ri) + name_len + 1); ++ if (!ri) { ++ printf("memory allocation failed\n"); ++ exit(1); ++ } ++ memset(ri, 0, sizeof(*ri) + name_len + 1); ++ ri->path = NULL; ++ ri->dir_id = dir_id; ++ ri->root_id = root_id; ++ ri->ref_tree = ref_tree; ++ strncpy(ri->name, name, name_len); + -+ memset(&args, 0, sizeof(args)); ++ ret = tree_insert(&root_lookup->root, root_id, ref_tree, &ri->rb_node); ++ if (ret) { ++ printf("failed to insert tree %llu\n", (unsigned long long)root_id); ++ exit(1); ++ } ++ return 0; ++} + -+ sk->tree_id = 0; ++/* ++ * for a given root_info, search through the root_lookup tree to construct ++ * the full path name to it. ++ * ++ * This can't be called until all the root_info->path fields are filled ++ * in by lookup_ino_path ++ */ ++static int resolve_root(struct root_lookup *rl, struct root_info *ri, ++ u64 *root_id, u64 *parent_id, u64 *top_id, char **path) ++{ ++ char *full_path = NULL; ++ int len = 0; ++ struct root_info *found; + + /* -+ * step one, we search for the inode back ref. We just use the first -+ * one ++ * we go backwards from the root_info object and add pathnames ++ * from parent directories as we go. + */ -+ sk->min_objectid = ino; -+ sk->max_objectid = ino; -+ sk->max_type = BTRFS_INODE_REF_KEY; -+ sk->max_offset = (u64)-1; -+ sk->min_type = BTRFS_INODE_REF_KEY; -+ sk->max_transid = (u64)-1; -+ sk->nr_items = 1; ++ *parent_id = 0; ++ found = ri; ++ while (1) { ++ char *tmp; ++ u64 next; ++ int add_len = strlen(found->path); + -+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); -+ if (ret < 0) { -+ fprintf(stderr, "ERROR: can't perform the search\n"); -+ return NULL; ++ /* room for / and for null */ ++ tmp = malloc(add_len + 2 + len); ++ if (full_path) { ++ memcpy(tmp + add_len + 1, full_path, len); ++ tmp[add_len] = '/'; ++ memcpy(tmp, found->path, add_len); ++ tmp [add_len + len + 1] = '\0'; ++ free(full_path); ++ full_path = tmp; ++ len += add_len + 1; ++ } else { ++ full_path = strdup(found->path); ++ len = add_len; ++ } ++ ++ next = found->ref_tree; ++ /* record the first parent */ ++ if (*parent_id == 0) ++ *parent_id = next; ++ ++ /* if the ref_tree refers to ourselves, we're at the top */ ++ if (next == found->root_id) { ++ *top_id = next; ++ break; ++ } ++ ++ /* ++ * if the ref_tree wasn't in our tree of roots, we're ++ * at the top ++ */ ++ found = tree_search(&rl->root, next); ++ if (!found) { ++ *top_id = next; ++ break; ++ } + } -+ /* the ioctl returns the number of item it found in nr_items */ -+ if (sk->nr_items == 0) -+ return NULL; + -+ off = 0; -+ sh = (struct btrfs_ioctl_search_header *)(args.buf + off); ++ *root_id = ri->root_id; ++ *path = full_path; + -+ if (sh->type == BTRFS_INODE_REF_KEY) { -+ struct btrfs_inode_ref *ref; -+ dirid = sh->offset; ++ return 0; ++} + -+ ref = (struct btrfs_inode_ref *)(sh + 1); -+ namelen = btrfs_stack_inode_ref_name_len(ref); ++/* ++ * for a single root_info, ask the kernel to give us a path name ++ * inside it's ref_root for the dir_id where it lives. ++ * ++ * This fills in root_info->path with the path to the directory and and ++ * appends this root's name. ++ */ ++static int lookup_ino_path(int fd, struct root_info *ri) ++{ ++ struct btrfs_ioctl_ino_lookup_args args; ++ int ret, e; + -+ name = (char *)(ref + 1); -+ name = strndup(name, namelen); ++ if (ri->path) ++ return 0; + -+ /* use our cached value */ -+ if (dirid == *cache_dirid && *cache_name) { -+ dirname = *cache_name; -+ goto build; ++ memset(&args, 0, sizeof(args)); ++ args.treeid = ri->ref_tree; ++ args.objectid = ri->dir_id; ++ ++ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); ++ e = errno; ++ if (ret) { ++ fprintf(stderr, "ERROR: Failed to lookup path for root %llu - %s\n", ++ (unsigned long long)ri->ref_tree, ++ strerror(e)); ++ return ret; ++ } ++ ++ if (args.name[0]) { ++ /* ++ * we're in a subdirectory of ref_tree, the kernel ioctl ++ * puts a / in there for us ++ */ ++ ri->path = malloc(strlen(ri->name) + strlen(args.name) + 1); ++ if (!ri->path) { ++ perror("malloc failed"); ++ exit(1); + } ++ strcpy(ri->path, args.name); ++ strcat(ri->path, ri->name); + } else { -+ return NULL; ++ /* we're at the root of ref_tree */ ++ ri->path = strdup(ri->name); ++ if (!ri->path) { ++ perror("strdup failed"); ++ exit(1); ++ } + } -+ /* -+ * the inode backref gives us the file name and the parent directory id. -+ * From here we use __ino_resolve to get the path to the parent -+ */ -+ dirname = __ino_resolve(fd, dirid); -+build: -+ full = build_name(dirname, name); -+ if (*cache_name && dirname != *cache_name) -+ free(*cache_name); -+ -+ *cache_name = dirname; -+ *cache_dirid = dirid; -+ free(name); -+ -+ return full; ++ return 0; +} + -+int list_subvols(int fd) ++/* finding the generation for a given path is a two step process. ++ * First we use the inode loookup routine to find out the root id ++ * ++ * Then we use the tree search ioctl to scan all the root items for a ++ * given root id and spit out the latest generation we can find ++ */ ++static u64 find_root_gen(int fd) +{ -+ struct root_lookup root_lookup; -+ struct rb_node *n; ++ struct btrfs_ioctl_ino_lookup_args ino_args; + int ret; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + struct btrfs_ioctl_search_header *sh; -+ struct btrfs_root_ref *ref; + unsigned long off = 0; -+ int name_len; -+ char *name; -+ u64 dir_id; ++ u64 max_found = 0; + int i; ++ int e; + -+ root_lookup_init(&root_lookup); ++ memset(&ino_args, 0, sizeof(ino_args)); ++ ino_args.objectid = BTRFS_FIRST_FREE_OBJECTID; ++ ++ /* this ioctl fills in ino_args->treeid */ ++ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args); ++ e = errno; ++ if (ret) { ++ fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu - %s\n", ++ (unsigned long long)BTRFS_FIRST_FREE_OBJECTID, ++ strerror(e)); ++ return 0; ++ } + + memset(&args, 0, sizeof(args)); + -+ /* search in the tree of tree roots */ + sk->tree_id = 1; + + /* -+ * set the min and max to backref keys. The search will -+ * only send back this type of key now. -+ */ -+ sk->max_type = BTRFS_ROOT_BACKREF_KEY; -+ sk->min_type = BTRFS_ROOT_BACKREF_KEY; -+ -+ /* -+ * set all the other params to the max, we'll take any objectid -+ * and any trans ++ * there may be more than one ROOT_ITEM key if there are ++ * snapshots pending deletion, we have to loop through ++ * them. + */ -+ sk->max_objectid = (u64)-1; ++ sk->min_objectid = ino_args.treeid; ++ sk->max_objectid = ino_args.treeid; ++ sk->max_type = BTRFS_ROOT_ITEM_KEY; ++ sk->min_type = BTRFS_ROOT_ITEM_KEY; + sk->max_offset = (u64)-1; + sk->max_transid = (u64)-1; -+ -+ /* just a big number, doesn't matter much */ + sk->nr_items = 4096; + -+ while(1) { ++ while (1) { + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); ++ e = errno; + if (ret < 0) { -+ fprintf(stderr, "ERROR: can't perform the search\n"); -+ return ret; ++ fprintf(stderr, "ERROR: can't perform the search - %s\n", ++ strerror(e)); ++ return 0; + } + /* the ioctl returns the number of item it found in nr_items */ + if (sk->nr_items == 0) + break; + + off = 0; -+ -+ /* -+ * for each item, pull the key out of the header and then -+ * read the root_ref item it contains -+ */ + for (i = 0; i < sk->nr_items; i++) { ++ struct btrfs_root_item *item; + sh = (struct btrfs_ioctl_search_header *)(args.buf + + off); -+ off += sizeof(*sh); -+ if (sh->type == BTRFS_ROOT_BACKREF_KEY) { -+ ref = (struct btrfs_root_ref *)(args.buf + off); -+ name_len = btrfs_stack_root_ref_name_len(ref); -+ name = (char *)(ref + 1); -+ dir_id = btrfs_stack_root_ref_dirid(ref); -+ -+ add_root(&root_lookup, sh->objectid, sh->offset, -+ dir_id, name, name_len); -+ } + ++ off += sizeof(*sh); ++ item = (struct btrfs_root_item *)(args.buf + off); + off += sh->len; + -+ /* -+ * record the mins in sk so we can make sure the -+ * next search doesn't repeat this root -+ */ + sk->min_objectid = sh->objectid; + sk->min_type = sh->type; + sk->min_offset = sh->offset; -+ } -+ sk->nr_items = 4096; -+ /* this iteration is done, step forward one root for the next -+ * ioctl -+ */ -+ if (sk->min_objectid < (u64)-1) { -+ sk->min_objectid++; -+ sk->min_type = BTRFS_ROOT_BACKREF_KEY; -+ sk->min_offset = 0; -+ } else ++ ++ if (sh->objectid > ino_args.treeid) ++ break; ++ ++ if (sh->objectid == ino_args.treeid && ++ sh->type == BTRFS_ROOT_ITEM_KEY) { ++ max_found = max(max_found, ++ btrfs_root_generation(item)); ++ } ++ } ++ if (sk->min_offset < (u64)-1) ++ sk->min_offset++; ++ else + break; -+ } -+ /* -+ * now we have an rbtree full of root_info objects, but we need to fill -+ * in their path names within the subvol that is referencing each one. -+ */ -+ n = rb_first(&root_lookup.root); -+ while (n) { -+ struct root_info *entry; -+ int ret; -+ entry = rb_entry(n, struct root_info, rb_node); -+ ret = lookup_ino_path(fd, entry); -+ if(ret < 0) -+ return ret; -+ n = rb_next(n); -+ } + -+ /* now that we have all the subvol-relative paths filled in, -+ * we have to string the subvols together so that we can get -+ * a path all the way back to the FS root -+ */ -+ n = rb_last(&root_lookup.root); -+ while (n) { -+ struct root_info *entry; -+ entry = rb_entry(n, struct root_info, rb_node); -+ resolve_root(&root_lookup, entry); -+ n = rb_prev(n); ++ if (sk->min_type != BTRFS_ROOT_ITEM_KEY) ++ break; ++ if (sk->min_objectid != BTRFS_ROOT_ITEM_KEY) ++ break; + } -+ -+ return ret; ++ return max_found; +} + -+static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh, -+ struct btrfs_file_extent_item *item, -+ u64 found_gen, u64 *cache_dirid, -+ char **cache_dir_name, u64 *cache_ino, -+ char **cache_full_name) ++/* pass in a directory id and this will return ++ * the full path of the parent directory inside its ++ * subvolume root. ++ * ++ * It may return NULL if it is in the root, or an ERR_PTR if things ++ * go badly. ++ */ ++static char *__ino_resolve(int fd, u64 dirid) +{ -+ u64 len = 0; -+ u64 disk_start = 0; -+ u64 disk_offset = 0; -+ u8 type; -+ int compressed = 0; -+ int flags = 0; -+ char *name = NULL; ++ struct btrfs_ioctl_ino_lookup_args args; ++ int ret; ++ char *full; ++ int e; + -+ if (sh->objectid == *cache_ino) { -+ name = *cache_full_name; -+ } else if (*cache_full_name) { -+ free(*cache_full_name); -+ *cache_full_name = NULL; -+ } -+ if (!name) { -+ name = ino_resolve(fd, sh->objectid, cache_dirid, -+ cache_dir_name); -+ *cache_full_name = name; -+ *cache_ino = sh->objectid; -+ } -+ if (!name) -+ return -EIO; ++ memset(&args, 0, sizeof(args)); ++ args.objectid = dirid; + -+ type = btrfs_stack_file_extent_type(item); -+ compressed = btrfs_stack_file_extent_compression(item); ++ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); ++ e = errno; ++ if (ret) { ++ fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu - %s\n", ++ (unsigned long long)dirid, strerror(e) ); ++ return ERR_PTR(ret); ++ } + -+ if (type == BTRFS_FILE_EXTENT_REG || -+ type == BTRFS_FILE_EXTENT_PREALLOC) { -+ disk_start = btrfs_stack_file_extent_disk_bytenr(item); -+ disk_offset = btrfs_stack_file_extent_offset(item); -+ len = btrfs_stack_file_extent_num_bytes(item); -+ } else if (type == BTRFS_FILE_EXTENT_INLINE) { -+ disk_start = 0; -+ disk_offset = 0; -+ len = btrfs_stack_file_extent_ram_bytes(item); ++ if (args.name[0]) { ++ /* ++ * we're in a subdirectory of ref_tree, the kernel ioctl ++ * puts a / in there for us ++ */ ++ full = strdup(args.name); ++ if (!full) { ++ perror("malloc failed"); ++ return ERR_PTR(-ENOMEM); ++ } + } else { -+ printf("unhandled extent type %d for inode %llu " -+ "file offset %llu gen %llu\n", -+ type, -+ (unsigned long long)sh->objectid, -+ (unsigned long long)sh->offset, -+ (unsigned long long)found_gen); -+ -+ return -EIO; ++ /* we're at the root of ref_tree */ ++ full = NULL; + } -+ printf("inode %llu file offset %llu len %llu disk start %llu " -+ "offset %llu gen %llu flags ", -+ (unsigned long long)sh->objectid, -+ (unsigned long long)sh->offset, -+ (unsigned long long)len, -+ (unsigned long long)disk_start, -+ (unsigned long long)disk_offset, -+ (unsigned long long)found_gen); ++ return full; ++} + -+ if (compressed) { -+ printf("COMPRESS"); -+ flags++; -+ } -+ if (type == BTRFS_FILE_EXTENT_PREALLOC) { -+ printf("%sPREALLOC", flags ? "|" : ""); -+ flags++; -+ } -+ if (type == BTRFS_FILE_EXTENT_INLINE) { -+ printf("%sINLINE", flags ? "|" : ""); -+ flags++; -+ } -+ if (!flags) -+ printf("NONE"); ++/* ++ * simple string builder, returning a new string with both ++ * dirid and name ++ */ ++char *build_name(char *dirid, char *name) ++{ ++ char *full; ++ if (!dirid) ++ return strdup(name); + -+ printf(" %s\n", name); -+ return 0; ++ full = malloc(strlen(dirid) + strlen(name) + 1); ++ if (!full) ++ return NULL; ++ strcpy(full, dirid); ++ strcat(full, name); ++ return full; +} + -+int find_updated_files(int fd, u64 root_id, u64 oldest_gen) ++/* ++ * given an inode number, this returns the full path name inside the subvolume ++ * to that file/directory. cache_dirid and cache_name are used to ++ * cache the results so we can avoid tree searches if a later call goes ++ * to the same directory or file name ++ */ ++static char *ino_resolve(int fd, u64 ino, u64 *cache_dirid, char **cache_name) ++ +{ ++ u64 dirid; ++ char *dirname; ++ char *name; ++ char *full; + int ret; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + struct btrfs_ioctl_search_header *sh; -+ struct btrfs_file_extent_item *item; + unsigned long off = 0; -+ u64 found_gen; -+ u64 max_found = 0; -+ int i; -+ u64 cache_dirid = 0; -+ u64 cache_ino = 0; -+ char *cache_dir_name = NULL; -+ char *cache_full_name = NULL; -+ struct btrfs_file_extent_item backup; ++ int namelen; ++ int e; + -+ memset(&backup, 0, sizeof(backup)); + memset(&args, 0, sizeof(args)); + -+ sk->tree_id = root_id; ++ sk->tree_id = 0; + + /* -+ * set all the other params to the max, we'll take any objectid -+ * and any trans ++ * step one, we search for the inode back ref. We just use the first ++ * one + */ -+ sk->max_objectid = (u64)-1; ++ sk->min_objectid = ino; ++ sk->max_objectid = ino; ++ sk->max_type = BTRFS_INODE_REF_KEY; + sk->max_offset = (u64)-1; ++ sk->min_type = BTRFS_INODE_REF_KEY; + sk->max_transid = (u64)-1; -+ sk->max_type = BTRFS_EXTENT_DATA_KEY; -+ sk->min_transid = oldest_gen; -+ /* just a big number, doesn't matter much */ -+ sk->nr_items = 4096; ++ sk->nr_items = 1; + -+ max_found = find_root_gen(fd); -+ while(1) { -+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); -+ if (ret < 0) { -+ fprintf(stderr, "ERROR: can't perform the search\n"); -+ return ret; -+ } -+ /* the ioctl returns the number of item it found in nr_items */ -+ if (sk->nr_items == 0) -+ break; ++ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); ++ e = errno; ++ if (ret < 0) { ++ fprintf(stderr, "ERROR: can't perform the search - %s\n", ++ strerror(e)); ++ return NULL; ++ } ++ /* the ioctl returns the number of item it found in nr_items */ ++ if (sk->nr_items == 0) ++ return NULL; + -+ off = 0; ++ off = 0; ++ sh = (struct btrfs_ioctl_search_header *)(args.buf + off); + -+ /* -+ * for each item, pull the key out of the header and then -+ * read the root_ref item it contains -+ */ -+ for (i = 0; i < sk->nr_items; i++) { -+ sh = (struct btrfs_ioctl_search_header *)(args.buf + -+ off); -+ off += sizeof(*sh); ++ if (sh->type == BTRFS_INODE_REF_KEY) { ++ struct btrfs_inode_ref *ref; ++ dirid = sh->offset; + -+ /* -+ * just in case the item was too big, pass something other -+ * than garbage -+ */ -+ if (sh->len == 0) -+ item = &backup; -+ else -+ item = (struct btrfs_file_extent_item *)(args.buf + -+ off); -+ found_gen = btrfs_stack_file_extent_generation(item); -+ if (sh->type == BTRFS_EXTENT_DATA_KEY && -+ found_gen >= oldest_gen) { -+ print_one_extent(fd, sh, item, found_gen, -+ &cache_dirid, &cache_dir_name, -+ &cache_ino, &cache_full_name); -+ } -+ off += sh->len; ++ ref = (struct btrfs_inode_ref *)(sh + 1); ++ namelen = btrfs_stack_inode_ref_name_len(ref); + -+ /* -+ * record the mins in sk so we can make sure the -+ * next search doesn't repeat this root -+ */ -+ sk->min_objectid = sh->objectid; -+ sk->min_offset = sh->offset; -+ sk->min_type = sh->type; ++ name = (char *)(ref + 1); ++ name = strndup(name, namelen); ++ ++ /* use our cached value */ ++ if (dirid == *cache_dirid && *cache_name) { ++ dirname = *cache_name; ++ goto build; + } -+ sk->nr_items = 4096; -+ if (sk->min_offset < (u64)-1) -+ sk->min_offset++; -+ else if (sk->min_objectid < (u64)-1) { -+ sk->min_objectid++; -+ sk->min_offset = 0; -+ sk->min_type = 0; -+ } else -+ break; ++ } else { ++ return NULL; + } -+ free(cache_dir_name); -+ free(cache_full_name); -+ printf("transid marker was %llu\n", (unsigned long long)max_found); -+ return ret; -+} -diff --git a/btrfs-map-logical.c b/btrfs-map-logical.c -new file mode 100644 -index 0000000..a109c6a ---- /dev/null -+++ b/btrfs-map-logical.c -@@ -0,0 +1,221 @@ -+/* -+ * Copyright (C) 2009 Oracle. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public -+ * License v2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public -+ * License along with this program; if not, write to the -+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, -+ * Boston, MA 021110-1307, USA. -+ */ ++ /* ++ * the inode backref gives us the file name and the parent directory id. ++ * From here we use __ino_resolve to get the path to the parent ++ */ ++ dirname = __ino_resolve(fd, dirid); ++build: ++ full = build_name(dirname, name); ++ if (*cache_name && dirname != *cache_name) ++ free(*cache_name); + -+#define _XOPEN_SOURCE 500 -+#define _GNU_SOURCE 1 -+#include -+#include -+#include -+#include -+#include -+#include "kerncompat.h" -+#include "ctree.h" -+#include "volumes.h" -+#include "disk-io.h" -+#include "print-tree.h" -+#include "transaction.h" -+#include "list.h" -+#include "version.h" ++ *cache_name = dirname; ++ *cache_dirid = dirid; ++ free(name); + -+/* we write the mirror info to stdout unless they are dumping the data -+ * to stdout -+ * */ -+static FILE *info_file; ++ return full; ++} + -+struct extent_buffer *debug_read_block(struct btrfs_root *root, u64 bytenr, -+ u32 blocksize, int copy) ++static int __list_subvol_search(int fd, struct root_lookup *root_lookup) +{ + int ret; -+ int dev_nr; -+ struct extent_buffer *eb; -+ u64 length; -+ struct btrfs_multi_bio *multi = NULL; -+ struct btrfs_device *device; -+ int num_copies; -+ int mirror_num = 1; ++ struct btrfs_ioctl_search_args args; ++ struct btrfs_ioctl_search_key *sk = &args.key; ++ struct btrfs_ioctl_search_header *sh; ++ struct btrfs_root_ref *ref; ++ unsigned long off = 0; ++ int name_len; ++ char *name; ++ u64 dir_id; ++ int i; + -+ eb = btrfs_find_create_tree_block(root, bytenr, blocksize); -+ if (!eb) -+ return NULL; ++ root_lookup_init(root_lookup); ++ memset(&args, 0, sizeof(args)); + -+ dev_nr = 0; -+ length = blocksize; -+ while (1) { -+ ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, -+ eb->start, &length, &multi, mirror_num); -+ BUG_ON(ret); -+ device = multi->stripes[0].dev; -+ eb->fd = device->fd; -+ device->total_ios++; -+ eb->dev_bytenr = multi->stripes[0].physical; ++ root_lookup_init(root_lookup); + -+ fprintf(info_file, "mirror %d logical %Lu physical %Lu " -+ "device %s\n", mirror_num, bytenr, eb->dev_bytenr, -+ device->name); -+ kfree(multi); ++ memset(&args, 0, sizeof(args)); + -+ if (!copy || mirror_num == copy) -+ ret = read_extent_from_disk(eb); ++ /* search in the tree of tree roots */ ++ sk->tree_id = 1; + -+ num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, -+ eb->start, eb->len); -+ if (num_copies == 1) ++ /* ++ * set the min and max to backref keys. The search will ++ * only send back this type of key now. ++ */ ++ sk->max_type = BTRFS_ROOT_BACKREF_KEY; ++ sk->min_type = BTRFS_ROOT_BACKREF_KEY; ++ ++ /* ++ * set all the other params to the max, we'll take any objectid ++ * and any trans ++ */ ++ sk->max_objectid = (u64)-1; ++ sk->max_offset = (u64)-1; ++ sk->max_transid = (u64)-1; ++ ++ /* just a big number, doesn't matter much */ ++ sk->nr_items = 4096; ++ ++ while(1) { ++ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); ++ if (ret < 0) ++ return ret; ++ /* the ioctl returns the number of item it found in nr_items */ ++ if (sk->nr_items == 0) + break; + -+ mirror_num++; -+ if (mirror_num > num_copies) ++ off = 0; ++ ++ /* ++ * for each item, pull the key out of the header and then ++ * read the root_ref item it contains ++ */ ++ for (i = 0; i < sk->nr_items; i++) { ++ sh = (struct btrfs_ioctl_search_header *)(args.buf + ++ off); ++ off += sizeof(*sh); ++ if (sh->type == BTRFS_ROOT_BACKREF_KEY) { ++ ref = (struct btrfs_root_ref *)(args.buf + off); ++ name_len = btrfs_stack_root_ref_name_len(ref); ++ name = (char *)(ref + 1); ++ dir_id = btrfs_stack_root_ref_dirid(ref); ++ ++ add_root(root_lookup, sh->objectid, sh->offset, ++ dir_id, name, name_len); ++ } ++ ++ off += sh->len; ++ ++ /* ++ * record the mins in sk so we can make sure the ++ * next search doesn't repeat this root ++ */ ++ sk->min_objectid = sh->objectid; ++ sk->min_type = sh->type; ++ sk->min_offset = sh->offset; ++ } ++ sk->nr_items = 4096; ++ /* this iteration is done, step forward one root for the next ++ * ioctl ++ */ ++ if (sk->min_type < BTRFS_ROOT_BACKREF_KEY) { ++ sk->min_type = BTRFS_ROOT_BACKREF_KEY; ++ sk->min_offset = 0; ++ } else if (sk->min_objectid < (u64)-1) { ++ sk->min_objectid++; ++ sk->min_type = BTRFS_ROOT_BACKREF_KEY; ++ sk->min_offset = 0; ++ } else + break; + } -+ return eb; ++ ++ return 0; +} + -+static void print_usage(void) ++static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup) +{ -+ fprintf(stderr, "usage: btrfs-map-logical [options] mount_point\n"); -+ fprintf(stderr, "\t-l Logical extent to map\n"); -+ fprintf(stderr, "\t-c Copy of the extent to read (usually 1 or 2)\n"); -+ fprintf(stderr, "\t-o Output file to hold the extent\n"); -+ fprintf(stderr, "\t-s Number of bytes to read\n"); -+ exit(1); -+} ++ struct rb_node *n; + -+static struct option long_options[] = { -+ /* { "byte-count", 1, NULL, 'b' }, */ -+ { "logical", 1, NULL, 'l' }, -+ { "copy", 1, NULL, 'c' }, -+ { "output", 1, NULL, 'c' }, -+ { "bytes", 1, NULL, 'b' }, -+ { 0, 0, 0, 0} -+}; ++ n = rb_first(&root_lookup->root); ++ while (n) { ++ struct root_info *entry; ++ int ret; ++ entry = rb_entry(n, struct root_info, rb_node); ++ ret = lookup_ino_path(fd, entry); ++ if(ret < 0) ++ return ret; ++ n = rb_next(n); ++ } + -+int main(int ac, char **av) ++ return 0; ++} ++ ++int list_subvols(int fd, int print_parent) +{ -+ struct cache_tree root_cache; -+ struct btrfs_root *root; -+ struct extent_buffer *eb; -+ char *dev; -+ char *output_file = NULL; -+ u64 logical = 0; -+ int ret = 0; -+ int option_index = 0; -+ int copy = 0; -+ u64 bytes = 0; -+ int out_fd = 0; -+ int err; ++ struct root_lookup root_lookup; ++ struct rb_node *n; ++ int ret; + -+ while(1) { -+ int c; -+ c = getopt_long(ac, av, "l:c:o:b:", long_options, -+ &option_index); -+ if (c < 0) -+ break; -+ switch(c) { -+ case 'l': -+ logical = atoll(optarg); -+ if (logical == 0) { -+ fprintf(stderr, -+ "invalid extent number\n"); -+ print_usage(); -+ } -+ break; -+ case 'c': -+ copy = atoi(optarg); -+ if (copy == 0) { -+ fprintf(stderr, -+ "invalid copy number\n"); -+ print_usage(); -+ } -+ break; -+ case 'b': -+ bytes = atoll(optarg); -+ if (bytes == 0) { -+ fprintf(stderr, -+ "invalid byte count\n"); -+ print_usage(); -+ } -+ break; -+ case 'o': -+ output_file = strdup(optarg); -+ break; -+ default: -+ print_usage(); -+ } ++ ret = __list_subvol_search(fd, &root_lookup); ++ if (ret) { ++ fprintf(stderr, "ERROR: can't perform the search - %s\n", ++ strerror(errno)); ++ return ret; + } -+ ac = ac - optind; -+ if (ac == 0) -+ print_usage(); -+ if (logical == 0) -+ print_usage(); -+ if (copy < 0) -+ print_usage(); -+ -+ dev = av[optind]; + -+ radix_tree_init(); -+ cache_tree_init(&root_cache); -+ -+ root = open_ctree(dev, 0, 0); -+ if (!root) { -+ fprintf(stderr, "Open ctree failed\n"); -+ exit(1); -+ } ++ /* ++ * now we have an rbtree full of root_info objects, but we need to fill ++ * in their path names within the subvol that is referencing each one. ++ */ ++ ret = __list_subvol_fill_paths(fd, &root_lookup); ++ if (ret < 0) ++ return ret; + -+ if (output_file) { -+ if (strcmp(output_file, "-") == 0) { -+ out_fd = 1; -+ info_file = stderr; ++ /* now that we have all the subvol-relative paths filled in, ++ * we have to string the subvols together so that we can get ++ * a path all the way back to the FS root ++ */ ++ n = rb_last(&root_lookup.root); ++ while (n) { ++ struct root_info *entry; ++ u64 root_id; ++ u64 level; ++ u64 parent_id; ++ char *path; ++ entry = rb_entry(n, struct root_info, rb_node); ++ resolve_root(&root_lookup, entry, &root_id, &parent_id, ++ &level, &path); ++ if (print_parent) { ++ printf("ID %llu parent %llu top level %llu path %s\n", ++ (unsigned long long)root_id, ++ (unsigned long long)parent_id, ++ (unsigned long long)level, path); + } else { -+ out_fd = open(output_file, O_RDWR | O_CREAT, 0600); -+ if (out_fd < 0) -+ goto close; -+ err = ftruncate(out_fd, 0); -+ if (err) { -+ close(out_fd); -+ goto close; -+ } -+ info_file = stdout; ++ printf("ID %llu top level %llu path %s\n", ++ (unsigned long long)root_id, ++ (unsigned long long)level, path); + } ++ free(path); ++ n = rb_prev(n); + } + -+ if (bytes == 0) -+ bytes = root->sectorsize; ++ return ret; ++} + -+ bytes = (bytes + root->sectorsize - 1) / root->sectorsize; -+ bytes *= root->sectorsize; ++static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh, ++ struct btrfs_file_extent_item *item, ++ u64 found_gen, u64 *cache_dirid, ++ char **cache_dir_name, u64 *cache_ino, ++ char **cache_full_name) ++{ ++ u64 len = 0; ++ u64 disk_start = 0; ++ u64 disk_offset = 0; ++ u8 type; ++ int compressed = 0; ++ int flags = 0; ++ char *name = NULL; + -+ while (bytes > 0) { -+ eb = debug_read_block(root, logical, root->sectorsize, copy); -+ if (eb && output_file) { -+ err = write(out_fd, eb->data, eb->len); -+ if (err < 0 || err != eb->len) { -+ fprintf(stderr, "output file write failed\n"); -+ goto out_close_fd; -+ } -+ } -+ free_extent_buffer(eb); -+ logical += root->sectorsize; -+ bytes -= root->sectorsize; ++ if (sh->objectid == *cache_ino) { ++ name = *cache_full_name; ++ } else if (*cache_full_name) { ++ free(*cache_full_name); ++ *cache_full_name = NULL; ++ } ++ if (!name) { ++ name = ino_resolve(fd, sh->objectid, cache_dirid, ++ cache_dir_name); ++ *cache_full_name = name; ++ *cache_ino = sh->objectid; + } ++ if (!name) ++ return -EIO; + -+out_close_fd: -+ if (output_file && out_fd != 1) -+ close(out_fd); -+close: -+ close_ctree(root); -+ return ret; -+} -diff --git a/btrfs-vol.c b/btrfs-vol.c -index 8069778..4ed799d 100644 ---- a/btrfs-vol.c -+++ b/btrfs-vol.c -@@ -108,10 +108,24 @@ int main(int ac, char **av) - if (device && strcmp(device, "missing") == 0 && - cmd == BTRFS_IOC_RM_DEV) { - fprintf(stderr, "removing missing devices from %s\n", mnt); -- } else if (device) { -+ } else if (cmd != BTRFS_IOC_BALANCE) { -+ if (cmd == BTRFS_IOC_ADD_DEV) { -+ ret = check_mounted(device); -+ if (ret < 0) { -+ fprintf(stderr, -+ "error checking %s mount status\n", -+ device); -+ exit(1); -+ } -+ if (ret == 1) { -+ fprintf(stderr, "%s is mounted\n", device); -+ exit(1); -+ } -+ } - devfd = open(device, O_RDWR); -- if (!devfd) { -+ if (devfd < 0) { - fprintf(stderr, "Unable to open device %s\n", device); -+ exit(1); - } - ret = fstat(devfd, &st); - if (ret) { -diff --git a/btrfs-zero-log.c b/btrfs-zero-log.c -new file mode 100644 -index 0000000..f10438b ---- /dev/null -+++ b/btrfs-zero-log.c -@@ -0,0 +1,69 @@ -+/* -+ * Copyright (C) 2007 Oracle. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public -+ * License v2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public -+ * License along with this program; if not, write to the -+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, -+ * Boston, MA 021110-1307, USA. -+ */ ++ type = btrfs_stack_file_extent_type(item); ++ compressed = btrfs_stack_file_extent_compression(item); + -+#define _XOPEN_SOURCE 500 -+#define _GNU_SOURCE 1 -+#include -+#include -+#include -+#include -+#include -+#include "kerncompat.h" -+#include "ctree.h" -+#include "disk-io.h" -+#include "print-tree.h" -+#include "transaction.h" -+#include "list.h" -+#include "version.h" -+#include "utils.h" ++ if (type == BTRFS_FILE_EXTENT_REG || ++ type == BTRFS_FILE_EXTENT_PREALLOC) { ++ disk_start = btrfs_stack_file_extent_disk_bytenr(item); ++ disk_offset = btrfs_stack_file_extent_offset(item); ++ len = btrfs_stack_file_extent_num_bytes(item); ++ } else if (type == BTRFS_FILE_EXTENT_INLINE) { ++ disk_start = 0; ++ disk_offset = 0; ++ len = btrfs_stack_file_extent_ram_bytes(item); ++ } else { ++ printf("unhandled extent type %d for inode %llu " ++ "file offset %llu gen %llu\n", ++ type, ++ (unsigned long long)sh->objectid, ++ (unsigned long long)sh->offset, ++ (unsigned long long)found_gen); + -+static void print_usage(void) -+{ -+ fprintf(stderr, "usage: btrfs-zero-log dev\n"); -+ fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION); -+ exit(1); ++ return -EIO; ++ } ++ printf("inode %llu file offset %llu len %llu disk start %llu " ++ "offset %llu gen %llu flags ", ++ (unsigned long long)sh->objectid, ++ (unsigned long long)sh->offset, ++ (unsigned long long)len, ++ (unsigned long long)disk_start, ++ (unsigned long long)disk_offset, ++ (unsigned long long)found_gen); ++ ++ if (compressed) { ++ printf("COMPRESS"); ++ flags++; ++ } ++ if (type == BTRFS_FILE_EXTENT_PREALLOC) { ++ printf("%sPREALLOC", flags ? "|" : ""); ++ flags++; ++ } ++ if (type == BTRFS_FILE_EXTENT_INLINE) { ++ printf("%sINLINE", flags ? "|" : ""); ++ flags++; ++ } ++ if (!flags) ++ printf("NONE"); ++ ++ printf(" %s\n", name); ++ return 0; +} + -+int main(int ac, char **av) ++int find_updated_files(int fd, u64 root_id, u64 oldest_gen) +{ -+ struct btrfs_root *root; + int ret; ++ struct btrfs_ioctl_search_args args; ++ struct btrfs_ioctl_search_key *sk = &args.key; ++ struct btrfs_ioctl_search_header *sh; ++ struct btrfs_file_extent_item *item; ++ unsigned long off = 0; ++ u64 found_gen; ++ u64 max_found = 0; ++ int i; ++ int e; ++ u64 cache_dirid = 0; ++ u64 cache_ino = 0; ++ char *cache_dir_name = NULL; ++ char *cache_full_name = NULL; ++ struct btrfs_file_extent_item backup; + -+ if (ac != 2) -+ print_usage(); -+ -+ radix_tree_init(); -+ -+ if((ret = check_mounted(av[1])) < 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", av[1]); -+ return -EBUSY; -+ } -+ -+ root = open_ctree(av[1], 0, 1); ++ memset(&backup, 0, sizeof(backup)); ++ memset(&args, 0, sizeof(args)); + -+ if (root == NULL) -+ return 1; -+ -+ btrfs_set_super_log_root(&root->fs_info->super_copy, 0); -+ btrfs_set_super_log_root_level(&root->fs_info->super_copy, 0); -+ close_ctree(root); -+ return ret; -+} -diff --git a/btrfs.c b/btrfs.c -new file mode 100644 -index 0000000..46314cf ---- /dev/null -+++ b/btrfs.c -@@ -0,0 +1,387 @@ -+/* -+ * 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 _GNU_SOURCE -+#include -+#include -+#include -+ -+#include "kerncompat.h" -+#include "btrfs_cmds.h" -+#include "version.h" -+ -+typedef int (*CommandFunction)(int argc, char **argv); -+ -+struct Command { -+ CommandFunction func; /* function which implements the command */ -+ int nargs; /* if == 999, any number of arguments -+ if >= 0, number of arguments, -+ if < 0, _minimum_ number of arguments */ -+ char *verb; /* verb */ -+ char *help; /* help lines; form the 2nd onward they are -+ indented */ -+ -+ /* the following fields are run-time filled by the program */ -+ char **cmds; /* array of subcommands */ -+ int ncmds; /* number of subcommand */ -+}; -+ -+static struct Command commands[] = { ++ sk->tree_id = root_id; + + /* -+ avoid short commands different for the case only -+ */ -+ { do_clone, 2, -+ "subvolume snapshot", " [/]\n" -+ "Create a writable snapshot of the subvolume with\n" -+ "the name in the directory." -+ }, -+ { do_delete_subvolume, 1, -+ "subvolume delete", "\n" -+ "Delete the subvolume ." -+ }, -+ { do_create_subvol, 1, -+ "subvolume create", "[/]\n" -+ "Create a subvolume in (or the current directory if\n" -+ "not passed)." -+ }, -+ { do_subvol_list, 1, "subvolume list", "\n" -+ "List the snapshot/subvolume of a filesystem." -+ }, -+ { do_find_newer, 2, "subvolume find-new", " \n" -+ "List the recently modified files in a filesystem." -+ }, -+ { do_defrag, -1, -+ "filesystem defragment", "[-vcf] [-s start] [-l len] [-t size] | [|...]\n" -+ "Defragment a file or a directory." -+ }, -+ { do_set_default_subvol, 2, -+ "subvolume set-default", " \n" -+ "Set the subvolume of the filesystem which will be mounted\n" -+ "as default." -+ }, -+ { do_fssync, 1, -+ "filesystem sync", "\n" -+ "Force a sync on the filesystem ." -+ }, -+ { do_resize, 2, -+ "filesystem resize", "[+/-][gkm]|max \n" -+ "Resize the file system. If 'max' is passed, the filesystem\n" -+ "will occupe all available space on the device." -+ }, -+ { do_show_filesystem, 999, -+ "filesystem show", "[|