diff --git a/.gitignore b/.gitignore
index b606ca1..902d3b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,16 @@
-btrfs-progs-0.19.tar.bz2
+*.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
+/btrfs-progs-0.19.20120817git043a639.tar.bz2
diff --git a/Btrfs-progs-add-btrfs-device-ready-command.patch b/Btrfs-progs-add-btrfs-device-ready-command.patch
new file mode 100644
index 0000000..b59b9d1
--- /dev/null
+++ b/Btrfs-progs-add-btrfs-device-ready-command.patch
@@ -0,0 +1,84 @@
+From 01077c81987011d8a50999824f96032c10b2733b Mon Sep 17 00:00:00 2001
+From: Josef Bacik <jbacik@fusionio.com>
+Date: Thu, 21 Jun 2012 16:02:10 -0400
+Subject: [PATCH 1/3] Btrfs-progs: add btrfs device ready command
+
+This command will be used by things like dracut that wish to know very
+simply if all of the devices have been added to the kernel cache yet for the
+device to be fully mounted.  This keeps initrd's from constantly having to
+try to mount the file system until it succeeds every time a device is added
+to the system.  Thanks,
+
+Signed-off-by: Josef Bacik <jbacik@fusionio.com>
+---
+ cmds-device.c |   35 +++++++++++++++++++++++++++++++++++
+ ioctl.h       |    2 ++
+ 2 files changed, 37 insertions(+), 0 deletions(-)
+
+diff --git a/cmds-device.c b/cmds-device.c
+index 771856b..b24e2a3 100644
+--- a/cmds-device.c
++++ b/cmds-device.c
+@@ -249,11 +249,46 @@ static int cmd_scan_dev(int argc, char **argv)
+ 	return 0;
+ }
+ 
++static const char * const cmd_ready_dev_usage[] = {
++	"btrfs device ready <device>",
++	"Check device to see if it has all of it's devices in cache for mounting",
++	NULL
++};
++
++static int cmd_ready_dev(int argc, char **argv)
++{
++	struct	btrfs_ioctl_vol_args args;
++	int	fd;
++	int	ret;
++
++	if (check_argc_min(argc, 2))
++		usage(cmd_ready_dev_usage);
++
++	fd = open("/dev/btrfs-control", O_RDWR);
++	if (fd < 0) {
++		perror("failed to open /dev/btrfs-control");
++		return 10;
++	}
++
++	strncpy(args.name, argv[argc - 1], BTRFS_PATH_NAME_MAX);
++	ret = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args);
++	if (ret < 0) {
++		fprintf(stderr, "ERROR: unable to determine if the device '%s'"
++			" is ready for mounting - %s\n", argv[argc - 1],
++			strerror(errno));
++		ret = 1;
++	}
++
++	close(fd);
++	return ret;
++}
++
+ const struct cmd_group device_cmd_group = {
+ 	device_cmd_group_usage, NULL, {
+ 		{ "add", cmd_add_dev, cmd_add_dev_usage, NULL, 0 },
+ 		{ "delete", cmd_rm_dev, cmd_rm_dev_usage, NULL, 0 },
+ 		{ "scan", cmd_scan_dev, cmd_scan_dev_usage, NULL, 0 },
++		{ "ready", cmd_ready_dev, cmd_ready_dev_usage, NULL, 0 },
+ 		{ 0, 0, 0, 0, 0 }
+ 	}
+ };
+diff --git a/ioctl.h b/ioctl.h
+index d6f3d07..30220ad 100644
+--- a/ioctl.h
++++ b/ioctl.h
+@@ -365,6 +365,8 @@ struct btrfs_ioctl_clone_range_args {
+ 					struct btrfs_ioctl_ino_path_args)
+ #define BTRFS_IOC_LOGICAL_INO _IOWR(BTRFS_IOCTL_MAGIC, 36, \
+ 					struct btrfs_ioctl_ino_path_args)
++#define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \
++				     struct btrfs_ioctl_vol_args)
+ 
+ #define BTRFS_IOC_SET_RECEIVED_SUBVOL _IOWR(BTRFS_IOCTL_MAGIC, 37, \
+ 				struct btrfs_ioctl_received_subvol_args)
+-- 
+1.7.7.6
+
diff --git a/Btrfs-progs-detect-if-the-disk-we-are-formatting-is-.patch b/Btrfs-progs-detect-if-the-disk-we-are-formatting-is-.patch
new file mode 100644
index 0000000..ef8528d
--- /dev/null
+++ b/Btrfs-progs-detect-if-the-disk-we-are-formatting-is-.patch
@@ -0,0 +1,163 @@
+From f507119002c6bb972df09a47edfa8998c0fc8172 Mon Sep 17 00:00:00 2001
+From: Josef Bacik <jbacik@fusionio.com>
+Date: Fri, 20 Jul 2012 11:09:43 -0400
+Subject: [PATCH 2/3] Btrfs-progs: detect if the disk we are formatting is a
+ ssd
+
+SSD's do not gain anything by having metadata DUP turned on.  The underlying
+file system that is a part of all SSD's could easily map duplicate metadat
+blocks into the same erase block which effectively eliminates the benefit of
+duplicating the metadata on disk.  So detect if we are formatting a single
+SSD drive and if we are do not use DUP.  Thanks,
+
+Signed-off-by: Josef Bacik <jbacik@fusionio.com>
+---
+ Makefile            |    2 +-
+ man/mkfs.btrfs.8.in |    5 +++-
+ mkfs.c              |   58 ++++++++++++++++++++++++++++++++++++++++++++++++--
+ 3 files changed, 60 insertions(+), 5 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index c0aaa3d..1afef49 100644
+--- a/Makefile
++++ b/Makefile
+@@ -66,7 +66,7 @@ btrfsck: $(objects) btrfsck.o
+ 	$(CC) $(CFLAGS) -o btrfsck btrfsck.o $(objects) $(LDFLAGS) $(LIBS)
+ 
+ mkfs.btrfs: $(objects) mkfs.o
+-	$(CC) $(CFLAGS) -o mkfs.btrfs $(objects) mkfs.o $(LDFLAGS) $(LIBS)
++	$(CC) $(CFLAGS) -o mkfs.btrfs $(objects) mkfs.o $(LDFLAGS) $(LIBS) -lblkid
+ 
+ btrfs-debug-tree: $(objects) debug-tree.o
+ 	$(CC) $(CFLAGS) -o btrfs-debug-tree $(objects) debug-tree.o $(LDFLAGS) $(LIBS)
+diff --git a/man/mkfs.btrfs.8.in b/man/mkfs.btrfs.8.in
+index fc2e1d2..dfa7996 100644
+--- a/man/mkfs.btrfs.8.in
++++ b/man/mkfs.btrfs.8.in
+@@ -46,7 +46,10 @@ Specify a label for the filesystem.
+ .TP
+ \fB\-m\fR, \fB\-\-metadata \fIprofile\fR
+ Specify how metadata must be spanned across the devices specified. Valid
+-values are raid0, raid1, raid10 or single.
++values are raid0, raid1, raid10, single or dup.  Single device will have dup
++set by default except in the case of SSDs which will default to single.  This is
++because SSDs can remap blocks internally so duplicate blocks could end up in the
++same erase block which negates the benefits of doing metadata duplication.
+ .TP
+ \fB\-M\fR, \fB\-\-mixed\fR
+ Mix data and metadata chunks together for more efficient space 
+diff --git a/mkfs.c b/mkfs.c
+index dff5eb8..8816db8 100644
+--- a/mkfs.c
++++ b/mkfs.c
+@@ -37,6 +37,7 @@
+ #include <linux/fs.h>
+ #include <ctype.h>
+ #include <attr/xattr.h>
++#include <blkid/blkid.h>
+ #include "kerncompat.h"
+ #include "ctree.h"
+ #include "disk-io.h"
+@@ -234,7 +235,7 @@ static int create_one_raid_group(struct btrfs_trans_handle *trans,
+ static int create_raid_groups(struct btrfs_trans_handle *trans,
+ 			      struct btrfs_root *root, u64 data_profile,
+ 			      int data_profile_opt, u64 metadata_profile,
+-			      int metadata_profile_opt, int mixed)
++			      int metadata_profile_opt, int mixed, int ssd)
+ {
+ 	u64 num_devices = btrfs_super_num_devices(&root->fs_info->super_copy);
+ 	u64 allowed;
+@@ -245,8 +246,12 @@ static int create_raid_groups(struct btrfs_trans_handle *trans,
+ 	 * For mixed groups defaults are single/single.
+ 	 */
+ 	if (!metadata_profile_opt && !mixed) {
++		if (num_devices == 1 && ssd)
++			printf("Detected a SSD, turning off metadata "
++			       "duplication.  Mkfs with -m dup if you want to "
++			       "force metadata duplication.\n");
+ 		metadata_profile = (num_devices > 1) ?
+-			BTRFS_BLOCK_GROUP_RAID1 : BTRFS_BLOCK_GROUP_DUP;
++			BTRFS_BLOCK_GROUP_RAID1 : (ssd) ? 0: BTRFS_BLOCK_GROUP_DUP;
+ 	}
+ 	if (!data_profile_opt && !mixed) {
+ 		data_profile = (num_devices > 1) ?
+@@ -1201,6 +1206,49 @@ static int zero_output_file(int out_fd, u64 size, u32 sectorsize)
+ 	return ret;
+ }
+ 
++static int is_ssd(const char *file)
++{
++	char *devname;
++	blkid_probe probe;
++	char *dev;
++	char path[PATH_MAX];
++	dev_t disk;
++	int fd;
++	char rotational;
++
++	probe = blkid_new_probe_from_filename(file);
++	if (!probe)
++		return 0;
++
++	/*
++	 * We want to use blkid_devno_to_wholedisk() but it's broken for some
++	 * reason on F17 at least so we'll do this trickery
++	 */
++	disk = blkid_probe_get_wholedisk_devno(probe);
++	devname = blkid_devno_to_devname(disk);
++
++	dev = strrchr(devname, '/');
++	dev++;
++
++	snprintf(path, PATH_MAX, "/sys/block/%s/queue/rotational", dev);
++
++	free(devname);
++	blkid_free_probe(probe);
++
++	fd = open(path, O_RDONLY);
++	if (fd < 0) {
++		return 0;
++	}
++
++	if (read(fd, &rotational, sizeof(char)) < sizeof(char)) {
++		close(fd);
++		return 0;
++	}
++	close(fd);
++
++	return !atoi((const char *)&rotational);
++}
++
+ int main(int ac, char **av)
+ {
+ 	char *file;
+@@ -1227,6 +1275,7 @@ int main(int ac, char **av)
+ 	int data_profile_opt = 0;
+ 	int metadata_profile_opt = 0;
+ 	int nodiscard = 0;
++	int ssd = 0;
+ 
+ 	char *source_dir = NULL;
+ 	int source_dir_set = 0;
+@@ -1352,6 +1401,9 @@ int main(int ac, char **av)
+ 			exit(1);
+ 		}
+ 	}
++
++	ssd = is_ssd(file);
++
+ 	if (mixed) {
+ 		if (metadata_profile != data_profile) {
+ 			fprintf(stderr, "With mixed block groups data and metadata "
+@@ -1438,7 +1490,7 @@ raid_groups:
+ 	if (!source_dir_set) {
+ 		ret = create_raid_groups(trans, root, data_profile,
+ 				 data_profile_opt, metadata_profile,
+-				 metadata_profile_opt, mixed);
++				 metadata_profile_opt, mixed, ssd);
+ 		BUG_ON(ret);
+ 	}
+ 
+-- 
+1.7.7.6
+
diff --git a/Btrfs-progs-only-enforce-a-maximum-size-if-we-specif.patch b/Btrfs-progs-only-enforce-a-maximum-size-if-we-specif.patch
new file mode 100644
index 0000000..93aa5e0
--- /dev/null
+++ b/Btrfs-progs-only-enforce-a-maximum-size-if-we-specif.patch
@@ -0,0 +1,143 @@
+From cbe7ca77431c40bab80135c7b8ee6a5dece56e03 Mon Sep 17 00:00:00 2001
+From: Josef Bacik <jbacik@fusionio.com>
+Date: Fri, 27 Jul 2012 08:24:37 -0400
+Subject: [PATCH 3/3] Btrfs-progs: only enforce a maximum size if we specify
+ one
+
+My patch
+
+04609add88ef8428d725de6ef60f46a3ff0dbc8e
+
+introduced a regression where if you mkfs'ed a group of disks with different
+sizes it limited the disks to the size of the first one that is specified.
+This was not the intent of my patch, I only want it to limit the size based
+on the -b option, so I've reworked the code to pass in a max block count and
+that fixes the issue.  Thanks,
+
+Signed-off-by: Josef Bacik <jbacik@fusionio.com>
+---
+ btrfs-vol.c   |    3 ++-
+ cmds-device.c |    3 ++-
+ mkfs.c        |   15 ++++++---------
+ utils.c       |   12 +++---------
+ utils.h       |    6 ++----
+ 5 files changed, 15 insertions(+), 24 deletions(-)
+
+diff --git a/btrfs-vol.c b/btrfs-vol.c
+index 0efdbc1..ad824bd 100644
+--- a/btrfs-vol.c
++++ b/btrfs-vol.c
+@@ -150,7 +150,8 @@ int main(int ac, char **av)
+ 	if (cmd == BTRFS_IOC_ADD_DEV) {
+ 		int mixed = 0;
+ 
+-		ret = btrfs_prepare_device(devfd, device, 1, &dev_block_count, &mixed);
++		ret = btrfs_prepare_device(devfd, device, 1, &dev_block_count,
++					   0, &mixed, 0);
+ 		if (ret) {
+ 			fprintf(stderr, "Unable to init %s\n", device);
+ 			exit(1);
+diff --git a/cmds-device.c b/cmds-device.c
+index b24e2a3..75ee293 100644
+--- a/cmds-device.c
++++ b/cmds-device.c
+@@ -107,7 +107,8 @@ static int cmd_add_dev(int argc, char **argv)
+ 			continue;
+ 		}
+ 
+-		res = btrfs_prepare_device(devfd, argv[i], 1, &dev_block_count, &mixed);
++		res = btrfs_prepare_device(devfd, argv[i], 1, &dev_block_count,
++					   0, &mixed, 0);
+ 		if (res) {
+ 			fprintf(stderr, "ERROR: Unable to init '%s'\n", argv[i]);
+ 			close(devfd);
+diff --git a/mkfs.c b/mkfs.c
+index 8816db8..93cd83c 100644
+--- a/mkfs.c
++++ b/mkfs.c
+@@ -1373,11 +1373,9 @@ int main(int ac, char **av)
+ 			exit(1);
+ 		}
+ 		first_file = file;
+-		ret = __btrfs_prepare_device(fd, file, zero_end,
+-				&dev_block_count, &mixed, nodiscard);
+-		if (block_count == 0)
+-			block_count = dev_block_count;
+-		else if (block_count > dev_block_count) {
++		ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count,
++					   block_count, &mixed, nodiscard);
++		if (block_count && block_count > dev_block_count) {
+ 			fprintf(stderr, "%s is smaller than requested size\n", file);
+ 			exit(1);
+ 		}
+@@ -1418,7 +1416,7 @@ int main(int ac, char **av)
+ 			leafsize * i;
+ 	}
+ 
+-	ret = make_btrfs(fd, file, label, blocks, block_count,
++	ret = make_btrfs(fd, file, label, blocks, dev_block_count,
+ 			 nodesize, leafsize,
+ 			 sectorsize, stripesize);
+ 	if (ret) {
+@@ -1474,9 +1472,8 @@ int main(int ac, char **av)
+ 			close(fd);
+ 			continue;
+ 		}
+-		dev_block_count = block_count;
+-		ret = __btrfs_prepare_device(fd, file, zero_end,
+-					   &dev_block_count, &mixed, nodiscard);
++		ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count,
++					   block_count, &mixed, nodiscard);
+ 		mixed = old_mixed;
+ 		BUG_ON(ret);
+ 
+diff --git a/utils.c b/utils.c
+index aade9e2..a5ffb62 100644
+--- a/utils.c
++++ b/utils.c
+@@ -537,13 +537,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
+ }
+ 
+ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
+-			 int *mixed)
+-{
+-	/* discard by default when called from 'device add' */
+-	return __btrfs_prepare_device(fd, file, zero_end, block_count_ret, mixed, 0);
+-}
+-int __btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
+-			 int *mixed, int nodiscard)
++			   u64 max_block_count, int *mixed, int nodiscard)
+ {
+ 	u64 block_count;
+ 	u64 bytenr;
+@@ -561,8 +555,8 @@ int __btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_re
+ 		fprintf(stderr, "unable to find %s size\n", file);
+ 		exit(1);
+ 	}
+-	if (*block_count_ret)
+-		block_count = min(block_count, *block_count_ret);
++	if (max_block_count)
++		block_count = min(block_count, max_block_count);
+ 	zero_end = 1;
+ 
+ 	if (block_count < 1024 * 1024 * 1024 && !(*mixed)) {
+diff --git a/utils.h b/utils.h
+index c147c12..3a0368b 100644
+--- a/utils.h
++++ b/utils.h
+@@ -26,10 +26,8 @@ int make_btrfs(int fd, const char *device, const char *label,
+ 	       u32 leafsize, u32 sectorsize, u32 stripesize);
+ int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
+ 			struct btrfs_root *root, u64 objectid);
+-int btrfs_prepare_device(int fd, char *file, int zero_end,
+-			 u64 *block_count_ret, int *mixed);
+-int __btrfs_prepare_device(int fd, char *file, int zero_end,
+-			 u64 *block_count_ret, int *mixed, int nodiscard);
++int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
++			 u64 max_block_count, int *mixed, int nodiscard);
+ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
+ 		      struct btrfs_root *root, int fd, char *path,
+ 		      u64 block_count, u32 io_width, u32 io_align,
+-- 
+1.7.7.6
+
diff --git a/btrfs-init-dev-list.patch b/btrfs-init-dev-list.patch
new file mode 100644
index 0000000..80c0959
--- /dev/null
+++ b/btrfs-init-dev-list.patch
@@ -0,0 +1,21 @@
+diff --git a/utils.c b/utils.c
+index a5ffb62..f6686c6 100644
+--- a/utils.c
++++ b/utils.c
+@@ -476,7 +476,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
+ 	u64 num_devs;
+ 	int ret;
+ 
+-	device = kmalloc(sizeof(*device), GFP_NOFS);
++	device = kzalloc(sizeof(*device), GFP_NOFS);
+ 	if (!device)
+ 		return -ENOMEM;
+ 	buf = kmalloc(sectorsize, GFP_NOFS);
+@@ -502,6 +502,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
+ 	device->bytes_used = 0;
+ 	device->total_ios = 0;
+ 	device->dev_root = root->fs_info->dev_root;
++	INIT_LIST_HEAD(&device->dev_list);
+ 
+ 	ret = btrfs_add_device(trans, root, device);
+ 	BUG_ON(ret);
diff --git a/btrfs-progs-upstream.patch b/btrfs-progs-upstream.patch
deleted file mode 100644
index 83e06a0..0000000
--- a/btrfs-progs-upstream.patch
+++ /dev/null
@@ -1,16713 +0,0 @@
-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.</li>
- 
--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..79818e6 100644
---- a/Makefile
-+++ b/Makefile
-@@ -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 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 \
--		-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-image btrfs-zero-log btrfs-convert \
-+	btrfs-find-root btrfs-restore btrfstune
- 
- # make C=1 to enable sparse
- ifdef C
--	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 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)
-+	$(CC) $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(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
-+	$(CC) $(CFLAGS) -o btrfs-zero-log $(objects) btrfs-zero-log.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
-+	$(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)
-+	$(CC) $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(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)
- 
- 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
-+	$(CC) $(CFLAGS) -o ioctl-test $(objects) ioctl-test.o $(LDFLAGS) $(LIBS)
- 
- manpages:
- 	cd man; make
-@@ -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..7051e99
---- /dev/null
-+++ b/btrfs-corrupt-block.c
-@@ -0,0 +1,396 @@
-+/*
-+ * 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.
-+ */
-+
-+#define _XOPEN_SOURCE 500
-+#define _GNU_SOURCE 1
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <fcntl.h>
-+#include <unistd.h>
-+#include <getopt.h>
-+#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"
-+
-+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;
-+
-+	eb = btrfs_find_create_tree_block(root, bytenr, blocksize);
-+	if (!eb)
-+		return NULL;
-+
-+	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;
-+
-+		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);
-+
-+		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);
-+		}
-+
-+		num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
-+					      eb->start, eb->len);
-+		if (num_copies == 1)
-+			break;
-+
-+		mirror_num++;
-+		if (mirror_num > num_copies)
-+			break;
-+	}
-+	return eb;
-+}
-+
-+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 corrupt_keys(struct btrfs_trans_handle *trans,
-+			 struct btrfs_root *root,
-+			 struct extent_buffer *eb)
-+{
-+	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 corrupt_keys_in_block(struct btrfs_root *root, u64 bytenr)
-+{
-+	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;
-+}
-+
-+static int corrupt_extent(struct btrfs_trans_handle *trans,
-+			  struct btrfs_root *root, u64 bytenr, int copy)
-+{
-+	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;
-+
-+	path = btrfs_alloc_path();
-+
-+	key.objectid = bytenr;
-+	key.type = (u8)-1;
-+	key.offset = (u64)-1;
-+
-+	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;
-+	}
-+
-+	btrfs_free_path(path);
-+	return 0;
-+}
-+
-+static void btrfs_corrupt_extent_leaf(struct btrfs_trans_handle *trans,
-+				      struct btrfs_root *root, struct extent_buffer *eb)
-+{
-+	u32 nr = btrfs_header_nritems(eb);
-+	u32 victim = rand() % nr;
-+	u64 objectid;
-+	struct btrfs_key key;
-+
-+	btrfs_item_key_to_cpu(eb, &key, victim);
-+	objectid = key.objectid;
-+	corrupt_extent(trans, root, objectid, 1);
-+}
-+
-+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;
-+	}
-+
-+	if (btrfs_header_level(eb) == 1 && eb != root->node) {
-+		if (rand() % 5)
-+			return;
-+	}
-+
-+	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);
-+	}
-+}
-+
-+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}
-+};
-+
-+
-+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;
-+
-+	srand(128);
-+
-+	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();
-+		}
-+	}
-+	ac = ac - optind;
-+	if (ac == 0)
-+		print_usage();
-+	if (logical == 0 && !extent_tree)
-+		print_usage();
-+	if (copy < 0)
-+		print_usage();
-+
-+	dev = av[optind];
-+
-+	radix_tree_init();
-+	cache_tree_init(&root_cache);
-+
-+	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 (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 ret;
-+out_close:
-+	close_ctree(root);
-+	return ret;
-+}
-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.
-+ *
-+ * 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.
-+ */
-+
-+#ifndef __CHECKER__
-+#include <sys/ioctl.h>
-+#include <sys/mount.h>
-+#include "ioctl.h"
-+#endif
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <sys/types.h>
-+#include <sys/stat.h>
-+#include <fcntl.h>
-+#include <ctype.h>
-+#include <unistd.h>
-+#include <dirent.h>
-+#include <libgen.h>
-+#include <getopt.h>
-+#include "kerncompat.h"
-+#include "ctree.h"
-+#include "transaction.h"
-+#include "utils.h"
-+#include "version.h"
-+
-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.
-+ */
-+
-+#define _GNU_SOURCE
-+#ifndef __CHECKER__
-+#include <sys/ioctl.h>
-+#include <sys/mount.h>
-+#include "ioctl.h"
-+#endif
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <sys/types.h>
-+#include <sys/stat.h>
-+#include <fcntl.h>
-+#include <unistd.h>
-+#include <dirent.h>
-+#include <libgen.h>
-+#include "kerncompat.h"
-+#include "ctree.h"
-+#include "transaction.h"
-+#include "utils.h"
-+
-+/* 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;
-+};
-+
-+/*
-+ * one of these for each root we find.
-+ */
-+struct root_info {
-+	struct rb_node rb_node;
-+
-+	/* this root's id */
-+	u64 root_id;
-+
-+	/* the id of the root that references this one */
-+	u64 ref_tree;
-+
-+	/* the dir id we're in from ref_tree */
-+	u64 dir_id;
-+
-+	/* 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;
-+
-+	/* the name of this root in the directory it lives in */
-+	char name[];
-+};
-+
-+static void root_lookup_init(struct root_lookup *tree)
-+{
-+	tree->root.rb_node = NULL;
-+}
-+
-+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;
-+}
-+
-+/*
-+ * 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)
-+{
-+	struct rb_node ** p = &root->rb_node;
-+	struct rb_node * parent = NULL;
-+	struct root_info *entry;
-+	int comp;
-+
-+	while(*p) {
-+		parent = *p;
-+		entry = rb_entry(parent, struct root_info, rb_node);
-+
-+		comp = comp_entry(entry, root_id, ref_tree);
-+
-+		if (comp < 0)
-+			p = &(*p)->rb_left;
-+		else if (comp > 0)
-+			p = &(*p)->rb_right;
-+		else
-+			return parent;
-+	}
-+
-+	entry = rb_entry(parent, struct root_info, rb_node);
-+	rb_link_node(node, parent, p);
-+	rb_insert_color(node, root);
-+	return NULL;
-+}
-+
-+/*
-+ * 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)
-+{
-+	struct rb_node * n = root->rb_node;
-+	struct root_info *entry;
-+
-+	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;
-+}
-+
-+/*
-+ * 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);
-+	}
-+	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);
-+	}
-+	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 *root_id, u64 *parent_id, u64 *top_id, char **path)
-+{
-+	char *full_path = NULL;
-+	int len = 0;
-+	struct root_info *found;
-+
-+	/*
-+	 * we go backwards from the root_info object and add pathnames
-+	 * from parent directories as we go.
-+	 */
-+	*parent_id = 0;
-+	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;
-+		}
-+
-+		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;
-+		}
-+	}
-+
-+	*root_id = ri->root_id;
-+	*path = 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, e;
-+
-+	if (ri->path)
-+		return 0;
-+
-+	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 {
-+		/* we're at the root of ref_tree */
-+		ri->path = strdup(ri->name);
-+		if (!ri->path) {
-+			perror("strdup failed");
-+			exit(1);
-+		}
-+	}
-+	return 0;
-+}
-+
-+/* 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 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;
-+	int e;
-+
-+	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));
-+
-+	sk->tree_id = 1;
-+
-+	/*
-+	 * 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;
-+
-+	while (1) {
-+		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 0;
-+		}
-+		/* the ioctl returns the number of item it found in nr_items */
-+		if (sk->nr_items == 0)
-+			break;
-+
-+		off = 0;
-+		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);
-+			item = (struct btrfs_root_item *)(args.buf + off);
-+			off += sh->len;
-+
-+			sk->min_objectid = sh->objectid;
-+			sk->min_type = sh->type;
-+			sk->min_offset = sh->offset;
-+
-+			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;
-+
-+		if (sk->min_type != BTRFS_ROOT_ITEM_KEY)
-+			break;
-+		if (sk->min_objectid != BTRFS_ROOT_ITEM_KEY)
-+			break;
-+	}
-+	return max_found;
-+}
-+
-+/* 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)
-+{
-+	struct btrfs_ioctl_ino_lookup_args args;
-+	int ret;
-+	char *full;
-+	int e;
-+
-+	memset(&args, 0, sizeof(args));
-+	args.objectid = dirid;
-+
-+	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 (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;
-+	}
-+	return full;
-+}
-+
-+/*
-+ * 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);
-+
-+	full = malloc(strlen(dirid) + strlen(name) + 1);
-+	if (!full)
-+		return NULL;
-+	strcpy(full, dirid);
-+	strcat(full, name);
-+	return full;
-+}
-+
-+/*
-+ * 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;
-+	unsigned long off = 0;
-+	int namelen;
-+	int e;
-+
-+	memset(&args, 0, sizeof(args));
-+
-+	sk->tree_id = 0;
-+
-+	/*
-+	 * step one, we search for the inode back ref.  We just use the first
-+	 * one
-+	 */
-+	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;
-+
-+	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;
-+	sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
-+
-+	if (sh->type == BTRFS_INODE_REF_KEY) {
-+		struct btrfs_inode_ref *ref;
-+		dirid = sh->offset;
-+
-+		ref = (struct btrfs_inode_ref *)(sh + 1);
-+		namelen = btrfs_stack_inode_ref_name_len(ref);
-+
-+		name = (char *)(ref + 1);
-+		name = strndup(name, namelen);
-+
-+		/* use our cached value */
-+		if (dirid == *cache_dirid && *cache_name) {
-+			dirname = *cache_name;
-+			goto build;
-+		}
-+	} else {
-+		return NULL;
-+	}
-+	/*
-+	 * 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;
-+}
-+
-+static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
-+{
-+	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;
-+	int i;
-+
-+	root_lookup_init(root_lookup);
-+	memset(&args, 0, sizeof(args));
-+
-+	root_lookup_init(root_lookup);
-+
-+	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
-+	 */
-+	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;
-+
-+		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 0;
-+}
-+
-+static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
-+{
-+	struct rb_node *n;
-+
-+	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);
-+	}
-+
-+	return 0;
-+}
-+
-+int list_subvols(int fd, int print_parent)
-+{
-+	struct root_lookup root_lookup;
-+	struct rb_node *n;
-+	int ret;
-+
-+	ret = __list_subvol_search(fd, &root_lookup);
-+	if (ret) {
-+		fprintf(stderr, "ERROR: can't perform the search - %s\n",
-+				strerror(errno));
-+		return ret;
-+	}
-+
-+	/*
-+	 * 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;
-+
-+	/* 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 {
-+			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);
-+	}
-+
-+	return ret;
-+}
-+
-+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;
-+
-+	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;
-+
-+	type = btrfs_stack_file_extent_type(item);
-+	compressed = btrfs_stack_file_extent_compression(item);
-+
-+	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);
-+
-+		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 find_updated_files(int fd, u64 root_id, u64 oldest_gen)
-+{
-+	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;
-+
-+	memset(&backup, 0, sizeof(backup));
-+	memset(&args, 0, sizeof(args));
-+
-+	sk->tree_id = root_id;
-+
-+	/*
-+	 * 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;
-+	sk->max_type = BTRFS_EXTENT_DATA_KEY;
-+	sk->min_transid = oldest_gen;
-+	/* just a big number, doesn't matter much */
-+	sk->nr_items = 4096;
-+
-+	max_found = find_root_gen(fd);
-+	while(1) {
-+		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 ret;
-+		}
-+		/* 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++) {
-+			sh = (struct btrfs_ioctl_search_header *)(args.buf +
-+								  off);
-+			off += sizeof(*sh);
-+
-+			/*
-+			 * 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;
-+
-+			/*
-+			 * 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;
-+		}
-+		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;
-+	}
-+	free(cache_dir_name);
-+	free(cache_full_name);
-+	printf("transid marker was %llu\n", (unsigned long long)max_found);
-+	return ret;
-+}
-+
-+char *path_for_root(int fd, u64 root)
-+{
-+	struct root_lookup root_lookup;
-+	struct rb_node *n;
-+	char *ret_path = NULL;
-+	int ret;
-+
-+	ret = __list_subvol_search(fd, &root_lookup);
-+	if (ret < 0)
-+		return ERR_PTR(ret);
-+
-+	ret = __list_subvol_fill_paths(fd, &root_lookup);
-+	if (ret < 0)
-+		return ERR_PTR(ret);
-+
-+	n = rb_last(&root_lookup.root);
-+	while (n) {
-+		struct root_info *entry;
-+		u64 root_id;
-+		u64 parent_id;
-+		u64 level;
-+		char *path;
-+		entry = rb_entry(n, struct root_info, rb_node);
-+		resolve_root(&root_lookup, entry, &root_id, &parent_id, &level,
-+				&path);
-+		if (root_id == root)
-+			ret_path = path;
-+		else
-+			free(path);
-+		n = rb_prev(n);
-+	}
-+
-+	return ret_path;
-+}
-diff --git a/btrfs-map-logical.c b/btrfs-map-logical.c
-new file mode 100644
-index 0000000..d79a73a
---- /dev/null
-+++ b/btrfs-map-logical.c
-@@ -0,0 +1,220 @@
-+/*
-+ * 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.
-+ */
-+
-+#define _XOPEN_SOURCE 500
-+#define _GNU_SOURCE 1
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <fcntl.h>
-+#include <unistd.h>
-+#include <getopt.h>
-+#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"
-+
-+/* we write the mirror info to stdout unless they are dumping the data
-+ * to stdout
-+ * */
-+static FILE *info_file;
-+
-+struct extent_buffer *debug_read_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;
-+
-+	eb = btrfs_find_create_tree_block(root, bytenr, blocksize);
-+	if (!eb)
-+		return NULL;
-+
-+	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;
-+
-+		fprintf(info_file, "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);
-+
-+		if (!copy || mirror_num == copy)
-+			ret = read_extent_from_disk(eb);
-+
-+		num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
-+					      eb->start, eb->len);
-+		if (num_copies == 1)
-+			break;
-+
-+		mirror_num++;
-+		if (mirror_num > num_copies)
-+			break;
-+	}
-+	return eb;
-+}
-+
-+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 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}
-+};
-+
-+int main(int ac, char **av)
-+{
-+	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;
-+
-+	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();
-+		}
-+	}
-+	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);
-+	}
-+
-+	info_file = stdout;
-+	if (output_file) {
-+		if (strcmp(output_file, "-") == 0) {
-+			out_fd = 1;
-+			info_file = stderr;
-+		} 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;
-+		}
-+	}
-+
-+	if (bytes == 0)
-+		bytes = root->sectorsize;
-+
-+	bytes = (bytes + root->sectorsize - 1) / root->sectorsize;
-+	bytes *= root->sectorsize;
-+
-+	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;
-+	}
-+
-+out_close_fd:
-+	if (output_file && out_fd != 1)
-+		close(out_fd);
-+close:
-+	close_ctree(root);
-+	return ret;
-+}
-diff --git a/btrfs-select-super.c b/btrfs-select-super.c
-new file mode 100644
-index 0000000..51eb9c9
---- /dev/null
-+++ b/btrfs-select-super.c
-@@ -0,0 +1,99 @@
-+/*
-+ * 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.
-+ */
-+
-+#define _XOPEN_SOURCE 500
-+#define _GNU_SOURCE 1
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <fcntl.h>
-+#include <sys/stat.h>
-+#include "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"
-+
-+static void print_usage(void)
-+{
-+	fprintf(stderr, "usage: btrfs-select-super -s number dev\n");
-+	fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION);
-+	exit(1);
-+}
-+
-+int main(int ac, char **av)
-+{
-+	struct btrfs_root *root;
-+	int ret;
-+	int num;
-+	u64 bytenr = 0;
-+
-+	while(1) {
-+		int c;
-+		c = getopt(ac, av, "s:");
-+		if (c < 0)
-+			break;
-+		switch(c) {
-+			case 's':
-+				num = atol(optarg);
-+				bytenr = btrfs_sb_offset(num);
-+				printf("using SB copy %d, bytenr %llu\n", num,
-+				       (unsigned long long)bytenr);
-+				break;
-+			default:
-+				print_usage();
-+		}
-+	}
-+	ac = ac - optind;
-+
-+	if (ac != 1)
-+		print_usage();
-+
-+	if (bytenr == 0) {
-+		fprintf(stderr, "Please select the super copy with -s\n");
-+		print_usage();
-+	}
-+
-+	radix_tree_init();
-+
-+	if((ret = check_mounted(av[optind])) < 0) {
-+		fprintf(stderr, "Could not check mount status: %s\n", strerror(-ret));
-+		return ret;
-+	} else if(ret) {
-+		fprintf(stderr, "%s is currently mounted. Aborting.\n", av[optind]);
-+		return -EBUSY;
-+	}
-+
-+	root = open_ctree(av[optind], bytenr, 1);
-+
-+	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);
-+
-+	/* 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
-+	 * disk to overwrite all the other copies
-+	 */ 
-+	return ret;
-+}
-diff --git a/btrfs-show.c b/btrfs-show.c
-index c49626c..8210fd2 100644
---- a/btrfs-show.c
-+++ b/btrfs-show.c
-@@ -117,6 +117,11 @@ int main(int ac, char **av)
- 	int ret;
- 	int option_index = 0;
- 
-+	printf( "**\n"
-+		"** WARNING: this program is considered deprecated\n"
-+		"** Please consider to switch to the btrfs utility\n"
-+		"**\n");
-+
- 	while(1) {
- 		int c;
- 		c = getopt_long(ac, av, "", long_options,
-diff --git a/btrfs-vol.c b/btrfs-vol.c
-index 8069778..0efdbc1 100644
---- a/btrfs-vol.c
-+++ b/btrfs-vol.c
-@@ -78,6 +78,11 @@ int main(int ac, char **av)
- 	struct btrfs_ioctl_vol_args args;
- 	u64 dev_block_count = 0;
- 
-+	printf( "**\n"
-+		"** WARNING: this program is considered deprecated\n"
-+		"** Please consider to switch to the btrfs utility\n"
-+		"**\n");
-+
- 	while(1) {
- 		int c;
- 		c = getopt_long(ac, av, "a:br:", long_options,
-@@ -108,10 +113,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) {
-@@ -129,7 +148,9 @@ int main(int ac, char **av)
- 		exit(1);
- 	}
- 	if (cmd == BTRFS_IOC_ADD_DEV) {
--		ret = btrfs_prepare_device(devfd, device, 1, &dev_block_count);
-+		int mixed = 0;
-+
-+		ret = btrfs_prepare_device(devfd, device, 1, &dev_block_count, &mixed);
- 		if (ret) {
- 			fprintf(stderr, "Unable to init %s\n", device);
- 			exit(1);
-diff --git a/btrfs-zero-log.c b/btrfs-zero-log.c
-new file mode 100644
-index 0000000..1ea867b
---- /dev/null
-+++ b/btrfs-zero-log.c
-@@ -0,0 +1,72 @@
-+/*
-+ * 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.
-+ */
-+
-+#define _XOPEN_SOURCE 500
-+#define _GNU_SOURCE 1
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <fcntl.h>
-+#include <sys/stat.h>
-+#include "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"
-+
-+static void print_usage(void)
-+{
-+	fprintf(stderr, "usage: btrfs-zero-log dev\n");
-+	fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION);
-+	exit(1);
-+}
-+
-+int main(int ac, char **av)
-+{
-+	struct btrfs_root *root;
-+	struct btrfs_trans_handle *trans;
-+	int ret;
-+
-+	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);
-+
-+	if (root == NULL)
-+		return 1;
-+
-+	trans = btrfs_start_transaction(root, 1);
-+	btrfs_set_super_log_root(&root->fs_info->super_copy, 0);
-+	btrfs_set_super_log_root_level(&root->fs_info->super_copy, 0);
-+	btrfs_commit_transaction(trans, root);
-+	close_ctree(root);
-+	return ret;
-+}
-diff --git a/btrfs.c b/btrfs.c
-new file mode 100644
-index 0000000..88238d6
---- /dev/null
-+++ b/btrfs.c
-@@ -0,0 +1,276 @@
-+/*
-+ * 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 <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+
-+#include "commands.h"
-+#include "version.h"
-+
-+static const char * const btrfs_cmd_group_usage[] = {
-+	"btrfs [--help] [--version] <group> [<group>...] <command> [<args>]",
-+	NULL
-+};
-+
-+static const char btrfs_cmd_group_info[] =
-+	"Use --help as an argument for information on a specific group or command.";
-+
-+char argv0_buf[ARGV0_BUF_SIZE] = "btrfs";
-+
-+static inline const char *skip_prefix(const char *str, const char *prefix)
-+{
-+	size_t len = strlen(prefix);
-+	return strncmp(str, prefix, len) ? NULL : str + len;
-+}
-+
-+int prefixcmp(const char *str, const char *prefix)
-+{
-+	for (; ; str++, prefix++)
-+		if (!*prefix)
-+			return 0;
-+		else if (*str != *prefix)
-+			return (unsigned char)*prefix - (unsigned char)*str;
-+}
-+
-+static int parse_one_token(const char *arg, const struct cmd_group *grp,
-+			   const struct cmd_struct **cmd_ret)
-+{
-+	const struct cmd_struct *cmd = grp->commands;
-+	const struct cmd_struct *abbrev_cmd = NULL, *ambiguous_cmd = NULL;
-+
-+	for (; cmd->token; cmd++) {
-+		const char *rest;
-+
-+		rest = skip_prefix(arg, cmd->token);
-+		if (!rest) {
-+			if (!prefixcmp(cmd->token, arg)) {
-+				if (abbrev_cmd) {
-+					/*
-+					 * If this is abbreviated, it is
-+					 * ambiguous. So when there is no
-+					 * exact match later, we need to
-+					 * error out.
-+					 */
-+					ambiguous_cmd = abbrev_cmd;
-+				}
-+				abbrev_cmd = cmd;
-+			}
-+			continue;
-+		}
-+		if (*rest)
-+			continue;
-+
-+		*cmd_ret = cmd;
-+		return 0;
-+	}
-+
-+	if (ambiguous_cmd)
-+		return -2;
-+
-+	if (abbrev_cmd) {
-+		*cmd_ret = abbrev_cmd;
-+		return 0;
-+	}
-+
-+	return -1;
-+}
-+
-+static const struct cmd_struct *
-+parse_command_token(const char *arg, const struct cmd_group *grp)
-+{
-+	const struct cmd_struct *cmd;
-+
-+	switch(parse_one_token(arg, grp, &cmd)) {
-+	case -1:
-+		help_unknown_token(arg, grp);
-+	case -2:
-+		help_ambiguous_token(arg, grp);
-+	}
-+
-+	return cmd;
-+}
-+
-+void handle_help_options_next_level(const struct cmd_struct *cmd,
-+				    int argc, char **argv)
-+{
-+	if (argc < 2)
-+		return;
-+
-+	if (!strcmp(argv[1], "--help")) {
-+		if (cmd->next) {
-+			argc--;
-+			argv++;
-+			help_command_group(cmd->next, argc, argv);
-+		} else {
-+			usage_command(cmd, 1, 0);
-+		}
-+
-+		exit(0);
-+	}
-+}
-+
-+static void fixup_argv0(char **argv, const char *token)
-+{
-+	int len = strlen(argv0_buf);
-+
-+	snprintf(argv0_buf + len, sizeof(argv0_buf) - len, " %s", token);
-+	argv[0] = argv0_buf;
-+}
-+
-+int handle_command_group(const struct cmd_group *grp, int argc,
-+			 char **argv)
-+
-+{
-+	const struct cmd_struct *cmd;
-+
-+	argc--;
-+	argv++;
-+	if (argc < 1) {
-+		usage_command_group(grp, 0, 0);
-+		exit(1);
-+	}
-+
-+	cmd = parse_command_token(argv[0], grp);
-+
-+	handle_help_options_next_level(cmd, argc, argv);
-+
-+	fixup_argv0(argv, cmd->token);
-+	return cmd->fn(argc, argv);
-+}
-+
-+int check_argc_exact(int nargs, int expected)
-+{
-+	if (nargs < expected)
-+		fprintf(stderr, "%s: too few arguments\n", argv0_buf);
-+	if (nargs > expected)
-+		fprintf(stderr, "%s: too many arguments\n", argv0_buf);
-+
-+	return nargs != expected;
-+}
-+
-+int check_argc_min(int nargs, int expected)
-+{
-+	if (nargs < expected) {
-+		fprintf(stderr, "%s: too few arguments\n", argv0_buf);
-+		return 1;
-+	}
-+
-+	return 0;
-+}
-+
-+int check_argc_max(int nargs, int expected)
-+{
-+	if (nargs > expected) {
-+		fprintf(stderr, "%s: too many arguments\n", argv0_buf);
-+		return 1;
-+	}
-+
-+	return 0;
-+}
-+
-+const struct cmd_group btrfs_cmd_group;
-+
-+static const char * const cmd_help_usage[] = {
-+	"btrfs help [--full]",
-+	"Dislay help information",
-+	"",
-+	"--full     display detailed help on every command",
-+	NULL
-+};
-+
-+static int cmd_help(int argc, char **argv)
-+{
-+	help_command_group(&btrfs_cmd_group, argc, argv);
-+	return 0;
-+}
-+
-+static const char * const cmd_version_usage[] = {
-+	"btrfs version",
-+	"Display btrfs-progs version",
-+	NULL
-+};
-+
-+static int cmd_version(int argc, char **argv)
-+{
-+	printf("%s\n", BTRFS_BUILD_VERSION);
-+	return 0;
-+}
-+
-+static int handle_options(int *argc, char ***argv)
-+{
-+	char **orig_argv = *argv;
-+
-+	while (*argc > 0) {
-+		const char *arg = (*argv)[0];
-+		if (arg[0] != '-')
-+			break;
-+
-+		if (!strcmp(arg, "--help")) {
-+			break;
-+		} else if (!strcmp(arg, "--version")) {
-+			break;
-+		} else {
-+			fprintf(stderr, "Unknown option: %s\n", arg);
-+			fprintf(stderr, "usage: %s\n",
-+				btrfs_cmd_group.usagestr[0]);
-+			exit(129);
-+		}
-+
-+		(*argv)++;
-+		(*argc)--;
-+	}
-+
-+	return (*argv) - orig_argv;
-+}
-+
-+const struct cmd_group btrfs_cmd_group = {
-+	btrfs_cmd_group_usage, btrfs_cmd_group_info, {
-+		{ "subvolume", cmd_subvolume, NULL, &subvolume_cmd_group, 0 },
-+		{ "filesystem", cmd_filesystem, NULL, &filesystem_cmd_group, 0 },
-+		{ "balance", cmd_balance, NULL, &balance_cmd_group, 0 },
-+		{ "device", cmd_device, NULL, &device_cmd_group, 0 },
-+		{ "scrub", cmd_scrub, NULL, &scrub_cmd_group, 0 },
-+		{ "inspect-internal", cmd_inspect, NULL, &inspect_cmd_group, 0 },
-+		{ "help", cmd_help, cmd_help_usage, NULL, 0 },
-+		{ "version", cmd_version, cmd_version_usage, NULL, 0 },
-+		{ 0, 0, 0, 0, 0 }
-+	},
-+};
-+
-+int main(int argc, char **argv)
-+{
-+	const struct cmd_struct *cmd;
-+
-+	argc--;
-+	argv++;
-+	handle_options(&argc, &argv);
-+	if (argc > 0) {
-+		if (!prefixcmp(argv[0], "--"))
-+			argv[0] += 2;
-+	} else {
-+		usage_command_group(&btrfs_cmd_group, 0, 0);
-+		exit(1);
-+	}
-+
-+	cmd = parse_command_token(argv[0], &btrfs_cmd_group);
-+
-+	handle_help_options_next_level(cmd, argc, argv);
-+
-+	fixup_argv0(argv, cmd->token);
-+	exit(cmd->fn(argc, argv));
-+}
-diff --git a/btrfsck.c b/btrfsck.c
-index 40c90f8..7aac736 100644
---- a/btrfsck.c
-+++ b/btrfsck.c
-@@ -20,14 +20,20 @@
- #define _GNU_SOURCE 1
- #include <stdio.h>
- #include <stdlib.h>
-+#include <unistd.h>
- #include <fcntl.h>
-+#include <sys/stat.h>
-+#include <getopt.h>
- #include "kerncompat.h"
- #include "ctree.h"
-+#include "volumes.h"
-+#include "repair.h"
- #include "disk-io.h"
- #include "print-tree.h"
- #include "transaction.h"
- #include "list.h"
- #include "version.h"
-+#include "utils.h"
- 
- static u64 bytes_used = 0;
- static u64 total_csum_bytes = 0;
-@@ -36,7 +42,7 @@ static u64 total_fs_tree_bytes = 0;
- static u64 btree_space_waste = 0;
- static u64 data_bytes_allocated = 0;
- static u64 data_bytes_referenced = 0;
--int found_old_backref = 0;
-+static int found_old_backref = 0;
- 
- struct extent_backref {
- 	struct list_head list;
-@@ -71,9 +77,13 @@ struct extent_record {
- 	struct cache_extent cache;
- 	struct btrfs_disk_key parent_key;
- 	u64 start;
-+	u64 max_size;
- 	u64 nr;
- 	u64 refs;
- 	u64 extent_item_refs;
-+	u64 generation;
-+	u64 info_objectid;
-+	u8 info_level;
- 	unsigned int content_checked:1;
- 	unsigned int owner_ref_checked:1;
- 	unsigned int is_root:1;
-@@ -100,7 +110,11 @@ struct inode_backref {
- #define REF_ERR_DUP_INODE_REF		(1 << 5)
- #define REF_ERR_INDEX_UNMATCH		(1 << 6)
- #define REF_ERR_FILETYPE_UNMATCH	(1 << 7)
--#define REF_ERR_NAME_TOO_LONG		(1 << 8)
-+#define REF_ERR_NAME_TOO_LONG		(1 << 8) // 100
-+#define REF_ERR_NO_ROOT_REF		(1 << 9)
-+#define REF_ERR_NO_ROOT_BACKREF		(1 << 10)
-+#define REF_ERR_DUP_ROOT_REF		(1 << 11)
-+#define REF_ERR_DUP_ROOT_BACKREF	(1 << 12)
- 
- struct inode_record {
- 	struct list_head backrefs;
-@@ -144,6 +158,29 @@ struct inode_record {
- #define I_ERR_SOME_CSUM_MISSING		(1 << 12)
- #define I_ERR_LINK_COUNT_WRONG		(1 << 13)
- 
-+struct root_backref {
-+	struct list_head list;
-+	unsigned int found_dir_item:1;
-+	unsigned int found_dir_index:1;
-+	unsigned int found_back_ref:1;
-+	unsigned int found_forward_ref:1;
-+	unsigned int reachable:1;
-+	int errors;
-+	u64 ref_root;
-+	u64 dir;
-+	u64 index;
-+	u16 namelen;
-+	char name[0];
-+};
-+
-+struct root_record {
-+	struct list_head backrefs;
-+	struct cache_extent cache;
-+	unsigned int found_root_item:1;
-+	u64 objectid;
-+	u32 found_ref;
-+};
-+
- struct ptr_node {
- 	struct cache_extent cache;
- 	void *data;
-@@ -151,6 +188,7 @@ struct ptr_node {
- 
- struct shared_node {
- 	struct cache_extent cache;
-+	struct cache_tree root_cache;
- 	struct cache_tree inode_cache;
- 	struct inode_record *current;
- 	u32 refs;
-@@ -258,6 +296,14 @@ static void free_inode_rec(struct inode_record *rec)
- 	free(rec);
- }
- 
-+static int can_free_inode_rec(struct inode_record *rec)
-+{
-+	if (!rec->errors && rec->checked && rec->found_inode_item &&
-+	    rec->nlink == rec->found_link && list_empty(&rec->backrefs))
-+		return 1;
-+	return 0;
-+}
-+
- static void maybe_free_inode_rec(struct cache_tree *inode_cache,
- 				 struct inode_record *rec)
- {
-@@ -309,8 +355,7 @@ static void maybe_free_inode_rec(struct cache_tree *inode_cache,
- 	}
- 
- 	BUG_ON(rec->refs != 1);
--	if (!rec->errors && rec->nlink == rec->found_link &&
--	    list_empty(&rec->backrefs)) {
-+	if (can_free_inode_rec(rec)) {
- 		cache = find_cache_extent(inode_cache, rec->ino, 1);
- 		node = container_of(cache, struct ptr_node, cache);
- 		BUG_ON(node->data != rec);
-@@ -338,14 +383,12 @@ static int check_orphan_item(struct btrfs_root *root, u64 ino)
- 	return ret;
- }
- 
--static int process_inode_item(struct btrfs_root *root,
--			      struct extent_buffer *eb,
-+static int process_inode_item(struct extent_buffer *eb,
- 			      int slot, struct btrfs_key *key,
- 			      struct shared_node *active_node)
- {
- 	struct inode_record *rec;
- 	struct btrfs_inode_item *item;
--	int ret;
- 
- 	rec = active_node->current;
- 	BUG_ON(rec->ino != key->objectid || rec->refs > 1);
-@@ -361,11 +404,8 @@ static int process_inode_item(struct btrfs_root *root,
- 	if (btrfs_inode_flags(eb, item) & BTRFS_INODE_NODATASUM)
- 		rec->nodatasum = 1;
- 	rec->found_inode_item = 1;
--	if (rec->nlink == 0) {
--		ret = check_orphan_item(root, rec->ino);
--		if (ret == -ENOENT)
--			rec->errors |= I_ERR_NO_ORPHAN_ITEM;
--	}
-+	if (rec->nlink == 0)
-+		rec->errors |= I_ERR_NO_ORPHAN_ITEM;
- 	maybe_free_inode_rec(&active_node->inode_cache, rec);
- 	return 0;
- }
-@@ -391,7 +431,6 @@ static struct inode_backref *get_inode_backref(struct inode_record *rec,
- 	memcpy(backref->name, name, namelen);
- 	backref->name[namelen] = '\0';
- 	list_add_tail(&backref->list, &rec->backrefs);
--	rec->found_link++;
- 	return backref;
- }
- 
-@@ -419,6 +458,7 @@ static int add_inode_backref(struct cache_tree *inode_cache,
- 		backref->filetype = filetype;
- 		backref->found_dir_index = 1;
- 	} else if (itemtype == BTRFS_DIR_ITEM_KEY) {
-+		rec->found_link++;
- 		if (backref->found_dir_item)
- 			backref->errors |= REF_ERR_DUP_DIR_ITEM;
- 		if (backref->found_dir_index && backref->filetype != filetype)
-@@ -443,10 +483,10 @@ static int add_inode_backref(struct cache_tree *inode_cache,
- }
- 
- static int merge_inode_recs(struct inode_record *src, struct inode_record *dst,
--			    struct shared_node *dst_node)
-+			    struct cache_tree *dst_cache)
- {
- 	struct inode_backref *backref;
--	struct cache_tree *dst_cache = &dst_node->inode_cache;
-+	u32 dir_count = 0;
- 
- 	dst->merging = 1;
- 	list_for_each_entry(backref, &src->backrefs, list) {
-@@ -457,6 +497,7 @@ static int merge_inode_recs(struct inode_record *src, struct inode_record *dst,
- 					BTRFS_DIR_INDEX_KEY, backref->errors);
- 		}
- 		if (backref->found_dir_item) {
-+			dir_count++;
- 			add_inode_backref(dst_cache, dst->ino,
- 					backref->dir, 0, backref->name,
- 					backref->namelen, backref->filetype,
-@@ -481,6 +522,8 @@ static int merge_inode_recs(struct inode_record *src, struct inode_record *dst,
- 	if (dst->first_extent_gap > src->first_extent_gap)
- 		dst->first_extent_gap = src->first_extent_gap;
- 
-+	BUG_ON(src->found_link < dir_count);
-+	dst->found_link += src->found_link - dir_count;
- 	dst->found_size += src->found_size;
- 	if (src->extent_start != (u64)-1) {
- 		if (dst->extent_start == (u64)-1) {
-@@ -510,14 +553,8 @@ static int merge_inode_recs(struct inode_record *src, struct inode_record *dst,
- 			dst->errors |= I_ERR_DUP_INODE_ITEM;
- 		}
- 	}
--
--	if (src->checked) {
--		dst->checked = 1;
--		if (dst_node->current == dst)
--			dst_node->current = NULL;
--	}
- 	dst->merging = 0;
--	maybe_free_inode_rec(dst_cache, dst);
-+
- 	return 0;
- }
- 
-@@ -537,8 +574,9 @@ static int splice_shared_node(struct shared_node *src_node,
- 	if (src_node->current)
- 		current_ino = src_node->current->ino;
- 
--	src = &src_node->inode_cache;
--	dst = &dst_node->inode_cache;
-+	src = &src_node->root_cache;
-+	dst = &dst_node->root_cache;
-+again:
- 	cache = find_first_cache_extent(src, 0);
- 	while (cache) {
- 		node = container_of(cache, struct ptr_node, cache);
-@@ -558,13 +596,26 @@ static int splice_shared_node(struct shared_node *src_node,
- 		ret = insert_existing_cache_extent(dst, &ins->cache);
- 		if (ret == -EEXIST) {
- 			conflict = get_inode_rec(dst, rec->ino, 1);
--			merge_inode_recs(rec, conflict, dst_node);
-+			merge_inode_recs(rec, conflict, dst);
-+			if (rec->checked) {
-+				conflict->checked = 1;
-+				if (dst_node->current == conflict)
-+					dst_node->current = NULL;
-+			}
-+			maybe_free_inode_rec(dst, conflict);
- 			free_inode_rec(rec);
- 			free(ins);
- 		} else {
- 			BUG_ON(ret);
- 		}
- 	}
-+
-+	if (src == &src_node->root_cache) {
-+		src = &src_node->inode_cache;
-+		dst = &dst_node->inode_cache;
-+		goto again;
-+	}
-+
- 	if (current_ino > 0 && (!dst_node->current ||
- 	    current_ino > dst_node->current->ino)) {
- 		if (dst_node->current) {
-@@ -616,6 +667,7 @@ static int add_shared_node(struct cache_tree *shared, u64 bytenr, u32 refs)
- 	node = calloc(1, sizeof(*node));
- 	node->cache.start = bytenr;
- 	node->cache.size = 1;
-+	cache_tree_init(&node->root_cache);
- 	cache_tree_init(&node->inode_cache);
- 	node->refs = refs;
- 
-@@ -646,6 +698,7 @@ static int enter_shared_node(struct btrfs_root *root, u64 bytenr, u32 refs,
- 	if (wc->root_level == wc->active_node &&
- 	    btrfs_root_refs(&root->root_item) == 0) {
- 		if (--node->refs == 0) {
-+			free_inode_recs(&node->root_cache);
- 			free_inode_recs(&node->inode_cache);
- 			remove_cache_extent(&wc->shared, &node->cache);
- 			free(node);
-@@ -708,10 +761,12 @@ static int process_dir_item(struct extent_buffer *eb,
- 	int filetype;
- 	struct btrfs_dir_item *di;
- 	struct inode_record *rec;
-+	struct cache_tree *root_cache;
- 	struct cache_tree *inode_cache;
- 	struct btrfs_key location;
- 	char namebuf[BTRFS_NAME_LEN];
- 
-+	root_cache = &active_node->root_cache;
- 	inode_cache = &active_node->inode_cache;
- 	rec = active_node->current;
- 	rec->found_dir_item = 1;
-@@ -740,7 +795,9 @@ static int process_dir_item(struct extent_buffer *eb,
- 					  key->objectid, key->offset, namebuf,
- 					  len, filetype, key->type, error);
- 		} else if (location.type == BTRFS_ROOT_ITEM_KEY) {
--			/* fixme: check root back & forward references */
-+			add_inode_backref(root_cache, location.objectid,
-+					  key->objectid, key->offset, namebuf,
-+					  len, filetype, key->type, error);
- 		} else {
- 			fprintf(stderr, "warning line %d\n", __LINE__);
- 		}
-@@ -945,7 +1002,7 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb,
- 	struct btrfs_key key;
- 	u32 nritems;
- 	int i;
--	int ret;
-+	int ret = 0;
- 	struct cache_tree *inode_cache;
- 	struct shared_node *active_node;
- 
-@@ -977,8 +1034,7 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb,
- 			ret = process_inode_ref(eb, i, &key, active_node);
- 			break;
- 		case BTRFS_INODE_ITEM_KEY:
--			ret = process_inode_item(root, eb, i, &key,
--						 active_node);
-+			ret = process_inode_item(eb, i, &key, active_node);
- 			break;
- 		case BTRFS_EXTENT_DATA_KEY:
- 			ret = process_file_extent(root, eb, i, &key,
-@@ -988,7 +1044,7 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb,
- 			break;
- 		};
- 	}
--	return 0;
-+	return ret;
- }
- 
- static void reada_walk_down(struct btrfs_root *root,
-@@ -1033,7 +1089,9 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
- 	ret = btrfs_lookup_extent_info(NULL, root,
- 				       path->nodes[*level]->start,
- 				       path->nodes[*level]->len, &refs, NULL);
--	BUG_ON(ret);
-+	if (ret < 0)
-+		goto out;
-+
- 	if (refs > 1) {
- 		ret = enter_shared_node(root, path->nodes[*level]->start,
- 					refs, wc, *level);
-@@ -1060,7 +1118,8 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
- 		blocksize = btrfs_level_size(root, *level - 1);
- 		ret = btrfs_lookup_extent_info(NULL, root, bytenr, blocksize,
- 					       &refs, NULL);
--		BUG_ON(ret);
-+		if (ret < 0)
-+			refs = 0;
- 
- 		if (refs > 1) {
- 			ret = enter_shared_node(root, bytenr, refs,
-@@ -1120,7 +1179,7 @@ static int check_root_dir(struct inode_record *rec)
- 
- 	if (!rec->found_inode_item || rec->errors)
- 		goto out;
--	if (rec->nlink != 1 || rec->found_link != 1)
-+	if (rec->nlink != 1 || rec->found_link != 0)
- 		goto out;
- 	if (list_empty(&rec->backrefs))
- 		goto out;
-@@ -1176,13 +1235,23 @@ static int check_inode_recs(struct btrfs_root *root,
- 		node = container_of(cache, struct ptr_node, cache);
- 		rec = node->data;
- 		remove_cache_extent(inode_cache, &node->cache);
-+		free(node);
- 		if (rec->ino == root_dirid ||
- 		    rec->ino == BTRFS_ORPHAN_OBJECTID) {
--			free(node);
- 			free_inode_rec(rec);
- 			continue;
- 		}
- 
-+		if (rec->errors & I_ERR_NO_ORPHAN_ITEM) {
-+			ret = check_orphan_item(root, rec->ino);
-+			if (ret == 0)
-+				rec->errors &= ~I_ERR_NO_ORPHAN_ITEM;
-+			if (can_free_inode_rec(rec)) {
-+				free_inode_rec(rec);
-+				continue;
-+			}
-+		}
-+
- 		error++;
- 		if (!rec->found_inode_item)
- 			rec->errors |= I_ERR_NO_INODE_ITEM;
-@@ -1205,13 +1274,314 @@ static int check_inode_recs(struct btrfs_root *root,
- 				backref->namelen, backref->name,
- 				backref->filetype, backref->errors);
- 		}
--		free(node);
- 		free_inode_rec(rec);
- 	}
- 	return (error > 0) ? -1 : 0;
- }
- 
-+static struct root_record *get_root_rec(struct cache_tree *root_cache,
-+					u64 objectid)
-+{
-+	struct cache_extent *cache;
-+	struct root_record *rec = NULL;
-+	int ret;
-+
-+	cache = find_cache_extent(root_cache, objectid, 1);
-+	if (cache) {
-+		rec = container_of(cache, struct root_record, cache);
-+	} else {
-+		rec = calloc(1, sizeof(*rec));
-+		rec->objectid = objectid;
-+		INIT_LIST_HEAD(&rec->backrefs);
-+		rec->cache.start = objectid;
-+		rec->cache.size = 1;
-+
-+		ret = insert_existing_cache_extent(root_cache, &rec->cache);
-+		BUG_ON(ret);
-+	}
-+	return rec;
-+}
-+
-+static struct root_backref *get_root_backref(struct root_record *rec,
-+					     u64 ref_root, u64 dir, u64 index,
-+					     const char *name, int namelen)
-+{
-+	struct root_backref *backref;
-+
-+	list_for_each_entry(backref, &rec->backrefs, list) {
-+		if (backref->ref_root != ref_root || backref->dir != dir ||
-+		    backref->namelen != namelen)
-+			continue;
-+		if (memcmp(name, backref->name, namelen))
-+			continue;
-+		return backref;
-+	}
-+
-+	backref = malloc(sizeof(*backref) + namelen + 1);
-+	memset(backref, 0, sizeof(*backref));
-+	backref->ref_root = ref_root;
-+	backref->dir = dir;
-+	backref->index = index;
-+	backref->namelen = namelen;
-+	memcpy(backref->name, name, namelen);
-+	backref->name[namelen] = '\0';
-+	list_add_tail(&backref->list, &rec->backrefs);
-+	return backref;
-+}
-+
-+static void free_root_recs(struct cache_tree *root_cache)
-+{
-+	struct cache_extent *cache;
-+	struct root_record *rec;
-+	struct root_backref *backref;
-+
-+	while (1) {
-+		cache = find_first_cache_extent(root_cache, 0);
-+		if (!cache)
-+			break;
-+		rec = container_of(cache, struct root_record, cache);
-+		remove_cache_extent(root_cache, &rec->cache);
-+
-+		while (!list_empty(&rec->backrefs)) {
-+			backref = list_entry(rec->backrefs.next,
-+					     struct root_backref, list);
-+			list_del(&backref->list);
-+			free(backref);
-+		}
-+		kfree(rec);
-+	}
-+}
-+
-+static int add_root_backref(struct cache_tree *root_cache,
-+			    u64 root_id, u64 ref_root, u64 dir, u64 index,
-+			    const char *name, int namelen,
-+			    int item_type, int errors)
-+{
-+	struct root_record *rec;
-+	struct root_backref *backref;
-+
-+	rec = get_root_rec(root_cache, root_id);
-+	backref = get_root_backref(rec, ref_root, dir, index, name, namelen);
-+
-+	backref->errors |= errors;
-+
-+	if (item_type != BTRFS_DIR_ITEM_KEY) {
-+		if (backref->found_dir_index || backref->found_back_ref ||
-+		    backref->found_forward_ref) {
-+			if (backref->index != index)
-+				backref->errors |= REF_ERR_INDEX_UNMATCH;
-+		} else {
-+			backref->index = index;
-+		}
-+	}
-+
-+	if (item_type == BTRFS_DIR_ITEM_KEY) {
-+		backref->found_dir_item = 1;
-+		backref->reachable = 1;
-+		rec->found_ref++;
-+	} else if (item_type == BTRFS_DIR_INDEX_KEY) {
-+		backref->found_dir_index = 1;
-+	} else if (item_type == BTRFS_ROOT_REF_KEY) {
-+		if (backref->found_forward_ref)
-+			backref->errors |= REF_ERR_DUP_ROOT_REF;
-+		backref->found_forward_ref = 1;
-+	} else if (item_type == BTRFS_ROOT_BACKREF_KEY) {
-+		if (backref->found_back_ref)
-+			backref->errors |= REF_ERR_DUP_ROOT_BACKREF;
-+		backref->found_back_ref = 1;
-+	} else {
-+		BUG_ON(1);
-+	}
-+
-+	return 0;
-+}
-+
-+static int merge_root_recs(struct btrfs_root *root,
-+			   struct cache_tree *src_cache,
-+			   struct cache_tree *dst_cache)
-+{
-+	struct cache_extent *cache;
-+	struct ptr_node *node;
-+	struct inode_record *rec;
-+	struct inode_backref *backref;
-+
-+	if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) {
-+		free_inode_recs(src_cache);
-+		return 0;
-+	}
-+
-+	while (1) {
-+		cache = find_first_cache_extent(src_cache, 0);
-+		if (!cache)
-+			break;
-+		node = container_of(cache, struct ptr_node, cache);
-+		rec = node->data;
-+		remove_cache_extent(src_cache, &node->cache);
-+		free(node);
-+
-+		list_for_each_entry(backref, &rec->backrefs, list) {
-+			BUG_ON(backref->found_inode_ref);
-+			if (backref->found_dir_item)
-+				add_root_backref(dst_cache, rec->ino,
-+					root->root_key.objectid, backref->dir,
-+					backref->index, backref->name,
-+					backref->namelen, BTRFS_DIR_ITEM_KEY,
-+					backref->errors);
-+			if (backref->found_dir_index)
-+				add_root_backref(dst_cache, rec->ino,
-+					root->root_key.objectid, backref->dir,
-+					backref->index, backref->name,
-+					backref->namelen, BTRFS_DIR_INDEX_KEY,
-+					backref->errors);
-+		}
-+		free_inode_rec(rec);
-+	}
-+	return 0;
-+}
-+
-+static int check_root_refs(struct btrfs_root *root,
-+			   struct cache_tree *root_cache)
-+{
-+	struct root_record *rec;
-+	struct root_record *ref_root;
-+	struct root_backref *backref;
-+	struct cache_extent *cache;
-+	int loop = 1;
-+	int ret;
-+	int error;
-+	int errors = 0;
-+
-+	rec = get_root_rec(root_cache, BTRFS_FS_TREE_OBJECTID);
-+	rec->found_ref = 1;
-+
-+	/* fixme: this can not detect circular references */
-+	while (loop) {
-+		loop = 0;
-+		cache = find_first_cache_extent(root_cache, 0);
-+		while (1) {
-+			if (!cache)
-+				break;
-+			rec = container_of(cache, struct root_record, cache);
-+			cache = next_cache_extent(cache);
-+
-+			if (rec->found_ref == 0)
-+				continue;
-+
-+			list_for_each_entry(backref, &rec->backrefs, list) {
-+				if (!backref->reachable)
-+					continue;
-+
-+				ref_root = get_root_rec(root_cache,
-+							backref->ref_root);
-+				if (ref_root->found_ref > 0)
-+					continue;
-+
-+				backref->reachable = 0;
-+				rec->found_ref--;
-+				if (rec->found_ref == 0)
-+					loop = 1;
-+			}
-+		}
-+	}
-+
-+	cache = find_first_cache_extent(root_cache, 0);
-+	while (1) {
-+		if (!cache)
-+			break;
-+		rec = container_of(cache, struct root_record, cache);
-+		cache = next_cache_extent(cache);
-+
-+		if (rec->found_ref == 0 &&
-+		    rec->objectid >= BTRFS_FIRST_FREE_OBJECTID &&
-+		    rec->objectid <= BTRFS_LAST_FREE_OBJECTID) {
-+			ret = check_orphan_item(root->fs_info->tree_root,
-+						rec->objectid);
-+			if (ret == 0)
-+				continue;
-+			errors++;
-+			fprintf(stderr, "fs tree %llu not referenced\n",
-+				(unsigned long long)rec->objectid);
-+		}
-+
-+		error = 0;
-+		if (rec->found_ref > 0 && !rec->found_root_item)
-+			error = 1;
-+		list_for_each_entry(backref, &rec->backrefs, list) {
-+			if (!backref->found_dir_item)
-+				backref->errors |= REF_ERR_NO_DIR_ITEM;
-+			if (!backref->found_dir_index)
-+				backref->errors |= REF_ERR_NO_DIR_INDEX;
-+			if (!backref->found_back_ref)
-+				backref->errors |= REF_ERR_NO_ROOT_BACKREF;
-+			if (!backref->found_forward_ref)
-+				backref->errors |= REF_ERR_NO_ROOT_REF;
-+			if (backref->reachable && backref->errors)
-+				error = 1;
-+		}
-+		if (!error)
-+			continue;
-+
-+		errors++;
-+		fprintf(stderr, "fs tree %llu refs %u %s\n",
-+			(unsigned long long)rec->objectid, rec->found_ref,
-+			 rec->found_root_item ? "" : "not found");
-+
-+		list_for_each_entry(backref, &rec->backrefs, list) {
-+			if (!backref->reachable)
-+				continue;
-+			if (!backref->errors && rec->found_root_item)
-+				continue;
-+			fprintf(stderr, "\tunresolved ref root %llu dir %llu"
-+				" index %llu namelen %u name %s error %x\n",
-+				(unsigned long long)backref->ref_root,
-+				(unsigned long long)backref->dir,
-+				(unsigned long long)backref->index,
-+				backref->namelen, backref->name,
-+				backref->errors);
-+		}
-+	}
-+	return errors > 0 ? 1 : 0;
-+}
-+
-+static int process_root_ref(struct extent_buffer *eb, int slot,
-+			    struct btrfs_key *key,
-+			    struct cache_tree *root_cache)
-+{
-+	u64 dirid;
-+	u64 index;
-+	u32 len;
-+	u32 name_len;
-+	struct btrfs_root_ref *ref;
-+	char namebuf[BTRFS_NAME_LEN];
-+	int error;
-+
-+	ref = btrfs_item_ptr(eb, slot, struct btrfs_root_ref);
-+
-+	dirid = btrfs_root_ref_dirid(eb, ref);
-+	index = btrfs_root_ref_sequence(eb, ref);
-+	name_len = btrfs_root_ref_name_len(eb, ref);
-+
-+	if (name_len <= BTRFS_NAME_LEN) {
-+		len = name_len;
-+		error = 0;
-+	} else {
-+		len = BTRFS_NAME_LEN;
-+		error = REF_ERR_NAME_TOO_LONG;
-+	}
-+	read_extent_buffer(eb, namebuf, (unsigned long)(ref + 1), len);
-+
-+	if (key->type == BTRFS_ROOT_REF_KEY) {
-+		add_root_backref(root_cache, key->offset, key->objectid, dirid,
-+				 index, namebuf, len, key->type, error);
-+	} else {
-+		add_root_backref(root_cache, key->objectid, key->offset, dirid,
-+				 index, namebuf, len, key->type, error);
-+	}
-+	return 0;
-+}
-+
- static int check_fs_root(struct btrfs_root *root,
-+			 struct cache_tree *root_cache,
- 			 struct walk_control *wc)
- {
- 	int ret = 0;
-@@ -1219,10 +1589,18 @@ static int check_fs_root(struct btrfs_root *root,
- 	int level;
- 	struct btrfs_path path;
- 	struct shared_node root_node;
-+	struct root_record *rec;
- 	struct btrfs_root_item *root_item = &root->root_item;
- 
-+	if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
-+		rec = get_root_rec(root_cache, root->root_key.objectid);
-+		if (btrfs_root_refs(root_item) > 0)
-+			rec->found_root_item = 1;
-+	}
-+
- 	btrfs_init_path(&path);
- 	memset(&root_node, 0, sizeof(root_node));
-+	cache_tree_init(&root_node.root_cache);
- 	cache_tree_init(&root_node.inode_cache);
- 
- 	level = btrfs_header_level(root->node);
-@@ -1266,6 +1644,8 @@ static int check_fs_root(struct btrfs_root *root,
- 	}
- 	btrfs_release_path(root, &path);
- 
-+	merge_root_recs(root, &root_node.root_cache, root_cache);
-+
- 	if (root_node.current) {
- 		root_node.current->checked = 1;
- 		maybe_free_inode_rec(&root_node.inode_cache,
-@@ -1280,13 +1660,15 @@ static int fs_root_objectid(u64 objectid)
- {
- 	if (objectid == BTRFS_FS_TREE_OBJECTID ||
- 	    objectid == BTRFS_TREE_RELOC_OBJECTID ||
-+	    objectid == BTRFS_DATA_RELOC_TREE_OBJECTID ||
- 	    (objectid >= BTRFS_FIRST_FREE_OBJECTID &&
--	     objectid < BTRFS_LAST_FREE_OBJECTID))
-+	     objectid <= BTRFS_LAST_FREE_OBJECTID))
- 		return 1;
- 	return 0;
- }
- 
--static int check_fs_roots(struct btrfs_root *root)
-+static int check_fs_roots(struct btrfs_root *root,
-+			  struct cache_tree *root_cache)
- {
- 	struct btrfs_path path;
- 	struct btrfs_key key;
-@@ -1319,10 +1701,14 @@ static int check_fs_roots(struct btrfs_root *root)
- 		    fs_root_objectid(key.objectid)) {
- 			tmp_root = btrfs_read_fs_root_no_cache(root->fs_info,
- 							       &key);
--			ret = check_fs_root(tmp_root, &wc);
-+			ret = check_fs_root(tmp_root, root_cache, &wc);
- 			if (ret)
- 				err = 1;
- 			btrfs_free_fs_root(root->fs_info, tmp_root);
-+		} else if (key.type == BTRFS_ROOT_REF_KEY ||
-+			   key.type == BTRFS_ROOT_BACKREF_KEY) {
-+			process_root_ref(leaf, path.slots[0], &key,
-+					 root_cache);
- 		}
- 		path.slots[0]++;
- 	}
-@@ -1334,86 +1720,6 @@ static int check_fs_roots(struct btrfs_root *root)
- 	return err;
- }
- 
--static int check_node(struct btrfs_root *root,
--		      struct btrfs_disk_key *parent_key,
--		      struct extent_buffer *buf)
--{
--	int i;
--	struct btrfs_key cpukey;
--	struct btrfs_disk_key key;
--	u32 nritems = btrfs_header_nritems(buf);
--
--	if (nritems == 0 || nritems > BTRFS_NODEPTRS_PER_BLOCK(root))
--		return 1;
--	if (parent_key->type) {
--		btrfs_node_key(buf, &key, 0);
--		if (memcmp(parent_key, &key, sizeof(key)))
--			return 1;
--	}
--	for (i = 0; nritems > 1 && i < nritems - 2; i++) {
--		btrfs_node_key(buf, &key, i);
--		btrfs_node_key_to_cpu(buf, &cpukey, i + 1);
--		if (btrfs_comp_keys(&key, &cpukey) >= 0)
--			return 1;
--	}
--	return 0;
--}
--
--static int check_leaf(struct btrfs_root *root,
--		      struct btrfs_disk_key *parent_key,
--		      struct extent_buffer *buf)
--{
--	int i;
--	struct btrfs_key cpukey;
--	struct btrfs_disk_key key;
--	u32 nritems = btrfs_header_nritems(buf);
--
--	if (btrfs_header_level(buf) != 0) {
--		fprintf(stderr, "leaf is not a leaf %llu\n",
--		       (unsigned long long)btrfs_header_bytenr(buf));
--		return 1;
--	}
--	if (btrfs_leaf_free_space(root, buf) < 0) {
--		fprintf(stderr, "leaf free space incorrect %llu %d\n",
--			(unsigned long long)btrfs_header_bytenr(buf),
--			btrfs_leaf_free_space(root, buf));
--		return 1;
--	}
--
--	if (nritems == 0)
--		return 0;
--
--	btrfs_item_key(buf, &key, 0);
--	if (parent_key->type && memcmp(parent_key, &key, sizeof(key))) {
--		fprintf(stderr, "leaf parent key incorrect %llu\n",
--		       (unsigned long long)btrfs_header_bytenr(buf));
--		return 1;
--	}
--	for (i = 0; nritems > 1 && i < nritems - 2; i++) {
--		btrfs_item_key(buf, &key, i);
--		btrfs_item_key_to_cpu(buf, &cpukey, i + 1);
--		if (btrfs_comp_keys(&key, &cpukey) >= 0) {
--			fprintf(stderr, "bad key ordering %d %d\n", i, i+1);
--			return 1;
--		}
--		if (btrfs_item_offset_nr(buf, i) !=
--			btrfs_item_end_nr(buf, i + 1)) {
--			fprintf(stderr, "incorrect offsets %u %u\n",
--				btrfs_item_offset_nr(buf, i),
--				btrfs_item_end_nr(buf, i + 1));
--			return 1;
--		}
--		if (i == 0 && btrfs_item_end_nr(buf, i) !=
--		    BTRFS_LEAF_DATA_SIZE(root)) {
--			fprintf(stderr, "bad item end %u wanted %u\n",
--				btrfs_item_end_nr(buf, i),
--				(unsigned)BTRFS_LEAF_DATA_SIZE(root));
--			return 1;
--		}
--	}
--	return 0;
--}
--
- static int all_backpointers_checked(struct extent_record *rec, int print_errs)
- {
- 	struct list_head *cur = rec->backrefs.next;
-@@ -1458,12 +1764,12 @@ static int all_backpointers_checked(struct extent_record *rec, int print_errs)
- 			if (!print_errs)
- 				goto out;
- 			tback = (struct tree_backref *)back;
--			fprintf(stderr, "Backref %llu %s %llu not referenced\n",
-+			fprintf(stderr, "Backref %llu %s %llu not referenced back %p\n",
- 				(unsigned long long)rec->start,
- 				back->full_backref ? "parent" : "root",
- 				back->full_backref ?
- 				(unsigned long long)tback->parent :
--				(unsigned long long)tback->root);
-+				(unsigned long long)tback->root, back);
- 		}
- 		if (back->is_data) {
- 			dback = (struct data_backref *)back;
-@@ -1473,7 +1779,7 @@ static int all_backpointers_checked(struct extent_record *rec, int print_errs)
- 					goto out;
- 				fprintf(stderr, "Incorrect local backref count"
- 					" on %llu %s %llu owner %llu"
--					" offset %llu found %u wanted %u\n",
-+					" offset %llu found %u wanted %u back %p\n",
- 					(unsigned long long)rec->start,
- 					back->full_backref ?
- 					"parent" : "root",
-@@ -1482,7 +1788,7 @@ static int all_backpointers_checked(struct extent_record *rec, int print_errs)
- 					(unsigned long long)dback->root,
- 					(unsigned long long)dback->owner,
- 					(unsigned long long)dback->offset,
--					dback->found_ref, dback->num_refs);
-+					dback->found_ref, dback->num_refs, back);
- 			}
- 		}
- 		if (!back->is_data) {
-@@ -1541,7 +1847,6 @@ static int check_owner_ref(struct btrfs_root *root,
- 	struct btrfs_root *ref_root;
- 	struct btrfs_key key;
- 	struct btrfs_path path;
--	int ret;
- 	int level;
- 	int found = 0;
- 
-@@ -1571,10 +1876,10 @@ static int check_owner_ref(struct btrfs_root *root,
- 		btrfs_item_key_to_cpu(buf, &key, 0);
- 	else
- 		btrfs_node_key_to_cpu(buf, &key, 0);
--	
-+
- 	btrfs_init_path(&path);
- 	path.lowest_level = level + 1;
--	ret = btrfs_search_slot(NULL, ref_root, &key, &path, 0, 0);
-+	btrfs_search_slot(NULL, ref_root, &key, &path, 0, 0);
- 
- 	if (buf->start == btrfs_node_blockptr(path.nodes[level + 1],
- 					      path.slots[level + 1]))
-@@ -1584,23 +1889,81 @@ static int check_owner_ref(struct btrfs_root *root,
- 	return found ? 0 : 1;
- }
- 
-+static int is_extent_tree_record(struct extent_record *rec)
-+{
-+	struct list_head *cur = rec->backrefs.next;
-+	struct extent_backref *node;
-+	struct tree_backref *back;
-+	int is_extent = 0;
-+
-+	while(cur != &rec->backrefs) {
-+		node = list_entry(cur, struct extent_backref, list);
-+		cur = cur->next;
-+		if (node->is_data)
-+			return 0;
-+		back = (struct tree_backref *)node;
-+		if (node->full_backref)
-+			return 0;
-+		if (back->root == BTRFS_EXTENT_TREE_OBJECTID)
-+			is_extent = 1;
-+	}
-+	return is_extent;
-+}
-+
-+
-+static int record_bad_block_io(struct btrfs_fs_info *info,
-+			       struct cache_tree *extent_cache,
-+			       u64 start, u64 len)
-+{
-+	struct extent_record *rec;
-+	struct cache_extent *cache;
-+	struct btrfs_key key;
-+
-+	cache = find_cache_extent(extent_cache, start, len);
-+	if (!cache)
-+		return 0;
-+
-+	rec = container_of(cache, struct extent_record, cache);
-+	if (!is_extent_tree_record(rec))
-+		return 0;
-+
-+	btrfs_disk_key_to_cpu(&key, &rec->parent_key);
-+	return btrfs_add_corrupt_extent_record(info, &key, start, len, 0);
-+}
-+
- static int check_block(struct btrfs_root *root,
- 		       struct cache_tree *extent_cache,
- 		       struct extent_buffer *buf, u64 flags)
- {
- 	struct extent_record *rec;
- 	struct cache_extent *cache;
-+	struct btrfs_key key;
- 	int ret = 1;
-+	int level;
- 
- 	cache = find_cache_extent(extent_cache, buf->start, buf->len);
- 	if (!cache)
- 		return 1;
- 	rec = container_of(cache, struct extent_record, cache);
--	if (btrfs_is_leaf(buf)) {
--		ret = check_leaf(root, &rec->parent_key, buf);
--	} else {
--		ret = check_node(root, &rec->parent_key, buf);
-+	rec->generation = btrfs_header_generation(buf);
-+
-+	level = btrfs_header_level(buf);
-+	if (btrfs_header_nritems(buf) > 0) {
-+
-+		if (level == 0)
-+			btrfs_item_key_to_cpu(buf, &key, 0);
-+		else
-+			btrfs_node_key_to_cpu(buf, &key, 0);
-+
-+		rec->info_objectid = key.objectid;
- 	}
-+	rec->info_level = level;
-+
-+	if (btrfs_is_leaf(buf))
-+		ret = btrfs_check_leaf(root, &rec->parent_key, buf);
-+	else
-+		ret = btrfs_check_node(root, &rec->parent_key, buf);
-+
- 	if (ret) {
- 		fprintf(stderr, "bad block %llu\n",
- 			(unsigned long long)buf->start);
-@@ -1660,6 +2023,7 @@ static struct tree_backref *alloc_tree_backref(struct extent_record *rec,
- 		ref->node.full_backref = 0;
- 	}
- 	list_add_tail(&ref->node.list, &rec->backrefs);
-+
- 	return ref;
- }
- 
-@@ -1677,7 +2041,7 @@ static struct data_backref *find_data_backref(struct extent_record *rec,
- 		if (!node->is_data)
- 			continue;
- 		back = (struct data_backref *)node;
--		if (parent > 0) { 
-+		if (parent > 0) {
- 			if (!node->full_backref)
- 				continue;
- 			if (parent == back->parent)
-@@ -1695,11 +2059,13 @@ static struct data_backref *find_data_backref(struct extent_record *rec,
- 
- static struct data_backref *alloc_data_backref(struct extent_record *rec,
- 						u64 parent, u64 root,
--						u64 owner, u64 offset)
-+						u64 owner, u64 offset,
-+						u64 max_size)
- {
- 	struct data_backref *ref = malloc(sizeof(*ref));
- 	memset(&ref->node, 0, sizeof(ref->node));
- 	ref->node.is_data = 1;
-+
- 	if (parent > 0) {
- 		ref->parent = parent;
- 		ref->owner = 0;
-@@ -1714,13 +2080,16 @@ static struct data_backref *alloc_data_backref(struct extent_record *rec,
- 	ref->found_ref = 0;
- 	ref->num_refs = 0;
- 	list_add_tail(&ref->node.list, &rec->backrefs);
-+	if (max_size > rec->max_size)
-+		rec->max_size = max_size;
- 	return ref;
- }
- 
- static int add_extent_rec(struct cache_tree *extent_cache,
- 			  struct btrfs_key *parent_key,
- 			  u64 start, u64 nr, u64 extent_item_refs,
--			  int is_root, int inc_ref, int set_checked)
-+			  int is_root, int inc_ref, int set_checked,
-+			  u64 max_size)
- {
- 	struct extent_record *rec;
- 	struct cache_extent *cache;
-@@ -1732,7 +2101,7 @@ static int add_extent_rec(struct cache_tree *extent_cache,
- 		if (inc_ref)
- 			rec->refs++;
- 		if (rec->nr == 1)
--			rec->nr = nr;
-+			rec->nr = max(nr, max_size);
- 
- 		if (start != rec->start) {
- 			fprintf(stderr, "warning, start mismatch %llu %llu\n",
-@@ -1761,12 +2130,16 @@ static int add_extent_rec(struct cache_tree *extent_cache,
- 		if (parent_key)
- 			btrfs_cpu_key_to_disk(&rec->parent_key, parent_key);
- 
-+		if (rec->max_size < max_size)
-+			rec->max_size = max_size;
-+
- 		maybe_free_extent_rec(extent_cache, rec);
- 		return ret;
- 	}
- 	rec = malloc(sizeof(*rec));
- 	rec->start = start;
--	rec->nr = nr;
-+	rec->max_size = max_size;
-+	rec->nr = max(nr, max_size);
- 	rec->content_checked = 0;
- 	rec->owner_ref_checked = 0;
- 	INIT_LIST_HEAD(&rec->backrefs);
-@@ -1812,7 +2185,7 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
- 
- 	cache = find_cache_extent(extent_cache, bytenr, 1);
- 	if (!cache) {
--		add_extent_rec(extent_cache, NULL, bytenr, 1, 0, 0, 0, 0);
-+		add_extent_rec(extent_cache, NULL, bytenr, 1, 0, 0, 0, 0, 0);
- 		cache = find_cache_extent(extent_cache, bytenr, 1);
- 		if (!cache)
- 			abort();
-@@ -1851,7 +2224,7 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
- 
- static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
- 			    u64 parent, u64 root, u64 owner, u64 offset,
--			    u32 num_refs, int found_ref)
-+			    u32 num_refs, int found_ref, u64 max_size)
- {
- 	struct extent_record *rec;
- 	struct data_backref *back;
-@@ -1859,7 +2232,8 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
- 
- 	cache = find_cache_extent(extent_cache, bytenr, 1);
- 	if (!cache) {
--		add_extent_rec(extent_cache, NULL, bytenr, 1, 0, 0, 0, 0);
-+		add_extent_rec(extent_cache, NULL, bytenr, 1, 0, 0, 0, 0,
-+			       max_size);
- 		cache = find_cache_extent(extent_cache, bytenr, 1);
- 		if (!cache)
- 			abort();
-@@ -1869,9 +2243,13 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
- 	if (rec->start != bytenr) {
- 		abort();
- 	}
-+	if (rec->max_size < max_size)
-+		rec->max_size = max_size;
-+
- 	back = find_data_backref(rec, parent, root, owner, offset);
- 	if (!back)
--		back = alloc_data_backref(rec, parent, root, owner, offset);
-+		back = alloc_data_backref(rec, parent, root, owner, offset,
-+					  max_size);
- 
- 	if (found_ref) {
- 		BUG_ON(num_refs != 1);
-@@ -1895,7 +2273,6 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
- 	return 0;
- }
- 
--
- static int add_pending(struct cache_tree *pending,
- 		       struct cache_tree *seen, u64 bytenr, u32 size)
- {
-@@ -1985,11 +2362,10 @@ static int process_extent_ref_v0(struct cache_tree *extent_cache,
- 	btrfs_item_key_to_cpu(leaf, &key, slot);
- 	ref0 = btrfs_item_ptr(leaf, slot, struct btrfs_extent_ref_v0);
- 	if (btrfs_ref_objectid_v0(leaf, ref0) < BTRFS_FIRST_FREE_OBJECTID) {
--		add_tree_backref(extent_cache, key.objectid, key.offset,
--				 0, 0);
-+		add_tree_backref(extent_cache, key.objectid, key.offset, 0, 0);
- 	} else {
- 		add_data_backref(extent_cache, key.objectid, key.offset, 0,
--				 0, 0, btrfs_ref_count_v0(leaf, ref0), 0);
-+				 0, 0, btrfs_ref_count_v0(leaf, ref0), 0, 0);
- 	}
- 	return 0;
- }
-@@ -2022,14 +2398,14 @@ static int process_extent_item(struct cache_tree *extent_cache,
- 		BUG();
- #endif
- 		return add_extent_rec(extent_cache, NULL, key.objectid,
--				      key.offset, refs, 0, 0, 0);
-+				      key.offset, refs, 0, 0, 0, key.offset);
- 	}
- 
- 	ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item);
- 	refs = btrfs_extent_refs(eb, ei);
- 
- 	add_extent_rec(extent_cache, NULL, key.objectid, key.offset,
--		       refs, 0, 0, 0);
-+		       refs, 0, 0, 0, key.offset);
- 
- 	ptr = (unsigned long)(ei + 1);
- 	if (btrfs_extent_flags(eb, ei) & BTRFS_EXTENT_FLAG_TREE_BLOCK)
-@@ -2057,21 +2433,24 @@ static int process_extent_item(struct cache_tree *extent_cache,
- 								       dref),
- 					btrfs_extent_data_ref_offset(eb, dref),
- 					btrfs_extent_data_ref_count(eb, dref),
--					0);
-+					0, key.offset);
- 			break;
- 		case BTRFS_SHARED_DATA_REF_KEY:
- 			sref = (struct btrfs_shared_data_ref *)(iref + 1);
- 			add_data_backref(extent_cache, key.objectid, offset,
- 					0, 0, 0,
- 					btrfs_shared_data_ref_count(eb, sref),
--					0);
-+					0, key.offset);
- 			break;
- 		default:
--			BUG();
-+			fprintf(stderr, "corrupt extent record: key %Lu %u %Lu\n",
-+				key.objectid, key.type, key.offset);
-+			goto out;
- 		}
- 		ptr += btrfs_extent_inline_ref_size(type);
- 	}
- 	WARN_ON(ptr > end);
-+out:
- 	return 0;
- }
- 
-@@ -2135,9 +2514,18 @@ static int run_next_block(struct btrfs_root *root,
- 
- 	/* fixme, get the real parent transid */
- 	buf = read_tree_block(root, bytenr, size, 0);
-+	if (!extent_buffer_uptodate(buf)) {
-+		record_bad_block_io(root->fs_info,
-+				    extent_cache, bytenr, size);
-+		free_extent_buffer(buf);
-+		goto out;
-+	}
-+
- 	nritems = btrfs_header_nritems(buf);
- 
- 	ret = btrfs_lookup_extent_info(NULL, root, bytenr, size, NULL, &flags);
-+	if (ret < 0)
-+		flags = BTRFS_BLOCK_FLAG_FULL_BACKREF;
- 
- 	if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) {
- 		parent = bytenr;
-@@ -2148,6 +2536,8 @@ static int run_next_block(struct btrfs_root *root,
- 	}
- 
- 	ret = check_block(root, extent_cache, buf, flags);
-+	if (ret)
-+		goto out;
- 
- 	if (btrfs_is_leaf(buf)) {
- 		btree_space_waste += btrfs_leaf_free_space(root, buf);
-@@ -2164,16 +2554,6 @@ static int run_next_block(struct btrfs_root *root,
- 				continue;
- 			}
- 			if (key.type == BTRFS_BLOCK_GROUP_ITEM_KEY) {
--				struct btrfs_block_group_item *bi;
--				bi = btrfs_item_ptr(buf, i,
--					    struct btrfs_block_group_item);
--#if 0
--				fprintf(stderr,"block group %Lu %Lu used %Lu ",
--					btrfs_disk_key_objectid(disk_key),
--					btrfs_disk_key_offset(disk_key),
--					btrfs_block_group_used(bi));
--				fprintf(stderr, "flags %x\n", bi->flags);
--#endif
- 				continue;
- 			}
- 			if (key.type == BTRFS_EXTENT_REF_V0_KEY) {
-@@ -2206,7 +2586,7 @@ static int run_next_block(struct btrfs_root *root,
- 								       ref),
- 					btrfs_extent_data_ref_offset(buf, ref),
- 					btrfs_extent_data_ref_count(buf, ref),
--					0);
-+					0, root->sectorsize);
- 				continue;
- 			}
- 			if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
-@@ -2216,7 +2596,7 @@ static int run_next_block(struct btrfs_root *root,
- 				add_data_backref(extent_cache,
- 					key.objectid, key.offset, 0, 0, 0, 
- 					btrfs_shared_data_ref_count(buf, ref),
--					0);
-+					0, root->sectorsize);
- 				continue;
- 			}
- 			if (key.type != BTRFS_EXTENT_DATA_KEY)
-@@ -2239,26 +2619,33 @@ static int run_next_block(struct btrfs_root *root,
- 			ret = add_extent_rec(extent_cache, NULL,
- 				   btrfs_file_extent_disk_bytenr(buf, fi),
- 				   btrfs_file_extent_disk_num_bytes(buf, fi),
--				   0, 0, 1, 1);
-+				   0, 0, 1, 1,
-+				   btrfs_file_extent_disk_num_bytes(buf, fi));
- 			add_data_backref(extent_cache,
- 				btrfs_file_extent_disk_bytenr(buf, fi),
- 				parent, owner, key.objectid, key.offset -
--				btrfs_file_extent_offset(buf, fi), 1, 1);
-+				btrfs_file_extent_offset(buf, fi), 1, 1,
-+				btrfs_file_extent_disk_num_bytes(buf, fi));
- 			BUG_ON(ret);
- 		}
- 	} else {
- 		int level;
-+		struct btrfs_key first_key;
-+
-+		first_key.objectid = 0;
-+
-+		if (nritems > 0)
-+			btrfs_item_key_to_cpu(buf, &first_key, 0);
- 		level = btrfs_header_level(buf);
- 		for (i = 0; i < nritems; i++) {
- 			u64 ptr = btrfs_node_blockptr(buf, i);
- 			u32 size = btrfs_level_size(root, level - 1);
- 			btrfs_node_key_to_cpu(buf, &key, i);
- 			ret = add_extent_rec(extent_cache, &key,
--					     ptr, size, 0, 0, 1, 0);
-+					     ptr, size, 0, 0, 1, 0, size);
- 			BUG_ON(ret);
- 
--			add_tree_backref(extent_cache, ptr, parent, 
--					 owner, 1);
-+			add_tree_backref(extent_cache, ptr, parent, owner, 1);
- 
- 			if (level > 1) {
- 				add_pending(nodes, seen, ptr, size);
-@@ -2277,6 +2664,7 @@ static int run_next_block(struct btrfs_root *root,
- 	    btrfs_header_backref_rev(buf) == BTRFS_MIXED_BACKREF_REV &&
- 	    !btrfs_header_flag(buf, BTRFS_HEADER_FLAG_RELOC))
- 		found_old_backref = 1;
-+out:
- 	free_extent_buffer(buf);
- 	return 0;
- }
-@@ -2296,25 +2684,553 @@ static int add_root_to_pending(struct extent_buffer *buf,
- 	else
- 		add_pending(pending, seen, buf->start, buf->len);
- 	add_extent_rec(extent_cache, NULL, buf->start, buf->len,
--		       0, 1, 1, 0);
-+		       0, 1, 1, 0, buf->len);
- 
- 	if (root_key->objectid == BTRFS_TREE_RELOC_OBJECTID ||
- 	    btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV)
--		add_tree_backref(extent_cache, buf->start, buf->start, 0, 1);
-+		add_tree_backref(extent_cache, buf->start, buf->start,
-+				 0, 1);
- 	else
- 		add_tree_backref(extent_cache, buf->start, 0,
- 				 root_key->objectid, 1);
- 	return 0;
- }
- 
--static int check_extent_refs(struct btrfs_root *root,
--		      struct cache_tree *extent_cache)
-+/* as we fix the tree, we might be deleting blocks that
-+ * we're tracking for repair.  This hook makes sure we
-+ * remove any backrefs for blocks as we are fixing them.
-+ */
-+static int free_extent_hook(struct btrfs_trans_handle *trans,
-+			    struct btrfs_root *root,
-+			    u64 bytenr, u64 num_bytes, u64 parent,
-+			    u64 root_objectid, u64 owner, u64 offset,
-+			    int refs_to_drop)
-+{
-+	struct extent_record *rec;
-+	struct cache_extent *cache;
-+	int is_data;
-+	struct cache_tree *extent_cache = root->fs_info->fsck_extent_cache;
-+
-+	is_data = owner >= BTRFS_FIRST_FREE_OBJECTID;
-+	cache = find_cache_extent(extent_cache, bytenr, num_bytes);
-+	if (!cache)
-+		return 0;
-+
-+	rec = container_of(cache, struct extent_record, cache);
-+	if (is_data) {
-+		struct data_backref *back;
-+		back = find_data_backref(rec, parent, root_objectid, owner,
-+					 offset);
-+		if (!back)
-+			goto out;
-+		if (back->node.found_ref) {
-+			back->found_ref -= refs_to_drop;
-+			if (rec->refs)
-+				rec->refs -= refs_to_drop;
-+		}
-+		if (back->node.found_extent_tree) {
-+			back->num_refs -= refs_to_drop;
-+			if (rec->extent_item_refs)
-+				rec->extent_item_refs -= refs_to_drop;
-+		}
-+		if (back->found_ref == 0)
-+			back->node.found_ref = 0;
-+		if (back->num_refs == 0)
-+			back->node.found_extent_tree = 0;
-+
-+		if (!back->node.found_extent_tree && back->node.found_ref) {
-+			list_del(&back->node.list);
-+			free(back);
-+		}
-+	} else {
-+		struct tree_backref *back;
-+		back = find_tree_backref(rec, parent, root_objectid);
-+		if (!back)
-+			goto out;
-+		if (back->node.found_ref) {
-+			if (rec->refs)
-+				rec->refs--;
-+			back->node.found_ref = 0;
-+		}
-+		if (back->node.found_extent_tree) {
-+			if (rec->extent_item_refs)
-+				rec->extent_item_refs--;
-+			back->node.found_extent_tree = 0;
-+		}
-+		if (!back->node.found_extent_tree && back->node.found_ref) {
-+			list_del(&back->node.list);
-+			free(back);
-+		}
-+	}
-+	maybe_free_extent_rec(extent_cache, rec);
-+out:
-+	return 0;
-+}
-+
-+static int delete_extent_records(struct btrfs_trans_handle *trans,
-+				 struct btrfs_root *root,
-+				 struct btrfs_path *path,
-+				 u64 bytenr, u64 new_len)
-+{
-+	struct btrfs_key key;
-+	struct btrfs_key found_key;
-+	struct extent_buffer *leaf;
-+	int ret;
-+	int slot;
-+
-+
-+	key.objectid = bytenr;
-+	key.type = (u8)-1;
-+	key.offset = (u64)-1;
-+
-+	while(1) {
-+		ret = btrfs_search_slot(trans, root->fs_info->extent_root,
-+					&key, path, 0, 1);
-+		if (ret < 0)
-+			break;
-+
-+		if (ret > 0) {
-+			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, &found_key, slot);
-+		if (found_key.objectid != bytenr)
-+			break;
-+
-+		if (found_key.type != BTRFS_EXTENT_ITEM_KEY &&
-+		    found_key.type != BTRFS_TREE_BLOCK_REF_KEY &&
-+		    found_key.type != BTRFS_EXTENT_DATA_REF_KEY &&
-+		    found_key.type != BTRFS_EXTENT_REF_V0_KEY &&
-+		    found_key.type != BTRFS_SHARED_BLOCK_REF_KEY &&
-+		    found_key.type != BTRFS_SHARED_DATA_REF_KEY) {
-+			btrfs_release_path(NULL, path);
-+			if (found_key.type == 0) {
-+				if (found_key.offset == 0)
-+					break;
-+				key.offset = found_key.offset - 1;
-+				key.type = found_key.type;
-+			}
-+			key.type = found_key.type - 1;
-+			key.offset = (u64)-1;
-+			continue;
-+		}
-+
-+		fprintf(stderr, "repair deleting extent record: key %Lu %u %Lu\n",
-+			found_key.objectid, found_key.type, found_key.offset);
-+
-+		ret = btrfs_del_item(trans, root->fs_info->extent_root, path);
-+		if (ret)
-+			break;
-+		btrfs_release_path(NULL, path);
-+
-+		if (found_key.type == BTRFS_EXTENT_ITEM_KEY) {
-+			ret = btrfs_update_block_group(trans, root, bytenr,
-+						       found_key.offset, 0, 0);
-+			if (ret)
-+				break;
-+		}
-+	}
-+
-+	btrfs_release_path(NULL, path);
-+	return ret;
-+}
-+
-+/*
-+ * for a single backref, this will allocate a new extent
-+ * and add the backref to it.
-+ */
-+static int record_extent(struct btrfs_trans_handle *trans,
-+			 struct btrfs_fs_info *info,
-+			 struct btrfs_path *path,
-+			 struct extent_record *rec,
-+			 struct extent_backref *back,
-+			 int allocated, u64 flags)
-+{
-+	int ret;
-+	struct btrfs_root *extent_root = info->extent_root;
-+	struct extent_buffer *leaf;
-+	struct btrfs_key ins_key;
-+	struct btrfs_extent_item *ei;
-+	struct tree_backref *tback;
-+	struct data_backref *dback;
-+	struct btrfs_tree_block_info *bi;
-+
-+	if (!back->is_data)
-+		rec->max_size = max_t(u64, rec->max_size,
-+				    info->extent_root->leafsize);
-+
-+	if (!allocated) {
-+		u32 item_size = sizeof(*ei);
-+
-+		if (!back->is_data)
-+			item_size += sizeof(*bi);
-+
-+		ins_key.objectid = rec->start;
-+		ins_key.offset = rec->max_size;
-+		ins_key.type = BTRFS_EXTENT_ITEM_KEY;
-+
-+		ret = btrfs_insert_empty_item(trans, extent_root, path,
-+					&ins_key, item_size);
-+		if (ret)
-+			goto fail;
-+
-+		leaf = path->nodes[0];
-+		ei = btrfs_item_ptr(leaf, path->slots[0],
-+				    struct btrfs_extent_item);
-+
-+		btrfs_set_extent_refs(leaf, ei, 0);
-+		btrfs_set_extent_generation(leaf, ei, rec->generation);
-+
-+		if (back->is_data) {
-+			btrfs_set_extent_flags(leaf, ei,
-+					       BTRFS_EXTENT_FLAG_DATA);
-+		} else {
-+			struct btrfs_disk_key copy_key;;
-+
-+			tback = (struct tree_backref *)back;
-+			bi = (struct btrfs_tree_block_info *)(ei + 1);
-+			memset_extent_buffer(leaf, 0, (unsigned long)bi,
-+					     sizeof(*bi));
-+			memset(&copy_key, 0, sizeof(copy_key));
-+
-+			copy_key.objectid = le64_to_cpu(rec->info_objectid);
-+			btrfs_set_tree_block_level(leaf, bi, rec->info_level);
-+			btrfs_set_tree_block_key(leaf, bi, &copy_key);
-+
-+			btrfs_set_extent_flags(leaf, ei,
-+					       BTRFS_EXTENT_FLAG_TREE_BLOCK | flags);
-+		}
-+
-+		btrfs_mark_buffer_dirty(leaf);
-+		ret = btrfs_update_block_group(trans, extent_root, rec->start,
-+					       rec->max_size, 1, 0);
-+		if (ret)
-+			goto fail;
-+		btrfs_release_path(NULL, path);
-+	}
-+
-+	if (back->is_data) {
-+		u64 parent;
-+		int i;
-+
-+		dback = (struct data_backref *)back;
-+		if (back->full_backref)
-+			parent = dback->parent;
-+		else
-+			parent = 0;
-+
-+		for (i = 0; i < dback->found_ref; i++) {
-+			/* if parent != 0, we're doing a full backref
-+			 * passing BTRFS_FIRST_FREE_OBJECTID as the owner
-+			 * just makes the backref allocator create a data
-+			 * backref
-+			 */
-+			ret = btrfs_inc_extent_ref(trans, info->extent_root,
-+						   rec->start, rec->max_size,
-+						   parent,
-+						   dback->root,
-+						   parent ?
-+						   BTRFS_FIRST_FREE_OBJECTID :
-+						   dback->owner,
-+						   dback->offset);
-+			if (ret)
-+				break;
-+		}
-+		fprintf(stderr, "adding new data backref"
-+				" on %llu %s %llu owner %llu"
-+				" offset %llu found %d\n",
-+				(unsigned long long)rec->start,
-+				back->full_backref ?
-+				"parent" : "root",
-+				back->full_backref ?
-+				(unsigned long long)parent :
-+				(unsigned long long)dback->root,
-+				(unsigned long long)dback->owner,
-+				(unsigned long long)dback->offset,
-+				dback->found_ref);
-+	} else {
-+		u64 parent;
-+
-+		tback = (struct tree_backref *)back;
-+		if (back->full_backref)
-+			parent = tback->parent;
-+		else
-+			parent = 0;
-+
-+		ret = btrfs_inc_extent_ref(trans, info->extent_root,
-+					   rec->start, rec->max_size,
-+					   parent, tback->root, 0, 0);
-+		fprintf(stderr, "adding new tree backref on "
-+			"start %llu len %llu parent %llu root %llu\n",
-+			rec->start, rec->max_size, tback->parent, tback->root);
-+	}
-+	if (ret)
-+		goto fail;
-+fail:
-+	btrfs_release_path(NULL, path);
-+	return ret;
-+}
-+
-+/*
-+ * when an incorrect extent item is found, this will delete
-+ * all of the existing entries for it and recreate them
-+ * based on what the tree scan found.
-+ */
-+static int fixup_extent_refs(struct btrfs_trans_handle *trans,
-+			     struct btrfs_fs_info *info,
-+			     struct extent_record *rec)
-+{
-+	int ret;
-+	struct btrfs_path *path;
-+	struct list_head *cur = rec->backrefs.next;
-+	struct cache_extent *cache;
-+	struct extent_backref *back;
-+	int allocated = 0;
-+	u64 flags = 0;
-+
-+	/* remember our flags for recreating the extent */
-+	ret = btrfs_lookup_extent_info(NULL, info->extent_root, rec->start,
-+				       rec->max_size, NULL, &flags);
-+	if (ret < 0)
-+		flags = BTRFS_BLOCK_FLAG_FULL_BACKREF;
-+
-+	path = btrfs_alloc_path();
-+
-+	/* step one, delete all the existing records */
-+	ret = delete_extent_records(trans, info->extent_root, path,
-+				    rec->start, rec->max_size);
-+
-+	if (ret < 0)
-+		goto out;
-+
-+	/* was this block corrupt?  If so, don't add references to it */
-+	cache = find_cache_extent(info->corrupt_blocks, rec->start, rec->max_size);
-+	if (cache) {
-+		ret = 0;
-+		goto out;
-+	}
-+
-+	/* step two, recreate all the refs we did find */
-+	while(cur != &rec->backrefs) {
-+		back = list_entry(cur, struct extent_backref, list);
-+		cur = cur->next;
-+
-+		/*
-+		 * if we didn't find any references, don't create a
-+		 * new extent record
-+		 */
-+		if (!back->found_ref)
-+			continue;
-+
-+		ret = record_extent(trans, info, path, rec, back, allocated, flags);
-+		allocated = 1;
-+
-+		if (ret)
-+			goto out;
-+	}
-+out:
-+	btrfs_free_path(path);
-+	return ret;
-+}
-+
-+/* right now we only prune from the extent allocation tree */
-+static int prune_one_block(struct btrfs_trans_handle *trans,
-+			   struct btrfs_fs_info *info,
-+			   struct btrfs_corrupt_block *corrupt)
-+{
-+	int ret;
-+	struct btrfs_path path;
-+	struct extent_buffer *eb;
-+	u64 found;
-+	int slot;
-+	int nritems;
-+	int level = corrupt->level + 1;
-+
-+	btrfs_init_path(&path);
-+again:
-+	/* we want to stop at the parent to our busted block */
-+	path.lowest_level = level;
-+
-+	ret = btrfs_search_slot(trans, info->extent_root,
-+				&corrupt->key, &path, -1, 1);
-+
-+	if (ret < 0)
-+		goto out;
-+
-+	eb = path.nodes[level];
-+	if (!eb) {
-+		ret = -ENOENT;
-+		goto out;
-+	}
-+
-+	/*
-+	 * hopefully the search gave us the block we want to prune,
-+	 * lets try that first
-+	 */
-+	slot = path.slots[level];
-+	found =  btrfs_node_blockptr(eb, slot);
-+	if (found == corrupt->cache.start)
-+		goto del_ptr;
-+
-+	nritems = btrfs_header_nritems(eb);
-+
-+	/* the search failed, lets scan this node and hope we find it */
-+	for (slot = 0; slot < nritems; slot++) {
-+		found =  btrfs_node_blockptr(eb, slot);
-+		if (found == corrupt->cache.start)
-+			goto del_ptr;
-+	}
-+	/*
-+	 * we couldn't find the bad block.  TODO, search all the nodes for pointers
-+	 * to this block
-+	 */
-+	if (eb == info->extent_root->node) {
-+		ret = -ENOENT;
-+		goto out;
-+	} else {
-+		level++;
-+		btrfs_release_path(NULL, &path);
-+		goto again;
-+	}
-+
-+del_ptr:
-+	printk("deleting pointer to block %Lu\n", corrupt->cache.start);
-+	ret = btrfs_del_ptr(trans, info->extent_root, &path, level, slot);
-+
-+out:
-+	btrfs_release_path(NULL, &path);
-+	return ret;
-+}
-+
-+static int prune_corrupt_blocks(struct btrfs_trans_handle *trans,
-+				struct btrfs_fs_info *info)
-+{
-+	struct cache_extent *cache;
-+	struct btrfs_corrupt_block *corrupt;
-+
-+	cache = find_first_cache_extent(info->corrupt_blocks, 0);
-+	while (1) {
-+		if (!cache)
-+			break;
-+		corrupt = container_of(cache, struct btrfs_corrupt_block, cache);
-+		prune_one_block(trans, info, corrupt);
-+		cache = next_cache_extent(cache);
-+	}
-+	return 0;
-+}
-+
-+static void free_corrupt_blocks(struct btrfs_fs_info *info)
-+{
-+	struct cache_extent *cache;
-+	struct btrfs_corrupt_block *corrupt;
-+
-+	while (1) {
-+		cache = find_first_cache_extent(info->corrupt_blocks, 0);
-+		if (!cache)
-+			break;
-+		corrupt = container_of(cache, struct btrfs_corrupt_block, cache);
-+		remove_cache_extent(info->corrupt_blocks, cache);
-+		free(corrupt);
-+	}
-+}
-+
-+static int check_block_group(struct btrfs_trans_handle *trans,
-+			      struct btrfs_fs_info *info,
-+			      struct map_lookup *map,
-+			      int *reinit)
-+{
-+	struct btrfs_key key;
-+	struct btrfs_path path;
-+	int ret;
-+
-+	key.objectid = map->ce.start;
-+	key.offset = map->ce.size;
-+	key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
-+
-+	btrfs_init_path(&path);
-+	ret = btrfs_search_slot(NULL, info->extent_root,
-+				&key, &path, 0, 0);
-+	btrfs_release_path(NULL, &path);
-+	if (ret <= 0)
-+		goto out;
-+
-+	ret = btrfs_make_block_group(trans, info->extent_root, 0, map->type,
-+			       BTRFS_FIRST_CHUNK_TREE_OBJECTID,
-+			       key.objectid, key.offset);
-+	*reinit = 1;
-+out:
-+	return ret;
-+}
-+
-+static int check_block_groups(struct btrfs_trans_handle *trans,
-+			      struct btrfs_fs_info *info, int *reinit)
-+{
-+	struct cache_extent *ce;
-+	struct map_lookup *map;
-+	struct btrfs_mapping_tree *map_tree = &info->mapping_tree;
-+
-+	/* this isn't quite working */
-+	return 0;
-+
-+	ce = find_first_cache_extent(&map_tree->cache_tree, 0);
-+	while (1) {
-+		if (!ce)
-+			break;
-+		map = container_of(ce, struct map_lookup, ce);
-+		check_block_group(trans, info, map, reinit);
-+		ce = next_cache_extent(ce);
-+	}
-+	return 0;
-+}
-+
-+static int check_extent_refs(struct btrfs_trans_handle *trans,
-+			     struct btrfs_root *root,
-+			     struct cache_tree *extent_cache, int repair)
- {
- 	struct extent_record *rec;
- 	struct cache_extent *cache;
- 	int err = 0;
-+	int ret = 0;
-+	int fixed = 0;
-+	int reinit = 0;
-+
-+	if (repair) {
-+		/*
-+		 * if we're doing a repair, we have to make sure
-+		 * we don't allocate from the problem extents.
-+		 * In the worst case, this will be all the
-+		 * extents in the FS
-+		 */
-+		cache = find_first_cache_extent(extent_cache, 0);
-+		while(cache) {
-+			rec = container_of(cache, struct extent_record, cache);
-+			btrfs_pin_extent(root->fs_info,
-+					 rec->start, rec->max_size);
-+			cache = next_cache_extent(cache);
-+		}
- 
-+		/* pin down all the corrupted blocks too */
-+		cache = find_first_cache_extent(root->fs_info->corrupt_blocks, 0);
-+		while(cache) {
-+			rec = container_of(cache, struct extent_record, cache);
-+			btrfs_pin_extent(root->fs_info,
-+					 rec->start, rec->max_size);
-+			cache = next_cache_extent(cache);
-+		}
-+		prune_corrupt_blocks(trans, root->fs_info);
-+		check_block_groups(trans, root->fs_info, &reinit);
-+		if (reinit)
-+			btrfs_read_block_groups(root->fs_info->extent_root);
-+	}
- 	while(1) {
-+		fixed = 0;
- 		cache = find_first_cache_extent(extent_cache, 0);
- 		if (!cache)
- 			break;
-@@ -2326,19 +3242,39 @@ static int check_extent_refs(struct btrfs_root *root,
- 			fprintf(stderr, "extent item %llu, found %llu\n",
- 				(unsigned long long)rec->extent_item_refs,
- 				(unsigned long long)rec->refs);
-+			if (!fixed && repair) {
-+				ret = fixup_extent_refs(trans, root->fs_info, rec);
-+				if (ret)
-+					goto repair_abort;
-+				fixed = 1;
-+			}
- 			err = 1;
-+
- 		}
- 		if (all_backpointers_checked(rec, 1)) {
- 			fprintf(stderr, "backpointer mismatch on [%llu %llu]\n",
- 				(unsigned long long)rec->start,
- 				(unsigned long long)rec->nr);
- 
-+			if (!fixed && repair) {
-+				ret = fixup_extent_refs(trans, root->fs_info, rec);
-+				if (ret)
-+					goto repair_abort;
-+				fixed = 1;
-+			}
-+
- 			err = 1;
- 		}
- 		if (!rec->owner_ref_checked) {
- 			fprintf(stderr, "owner ref check failed [%llu %llu]\n",
- 				(unsigned long long)rec->start,
- 				(unsigned long long)rec->nr);
-+			if (!fixed && repair) {
-+				ret = fixup_extent_refs(trans, root->fs_info, rec);
-+				if (ret)
-+					goto repair_abort;
-+				fixed = 1;
-+			}
- 			err = 1;
- 		}
- 
-@@ -2346,16 +3282,30 @@ static int check_extent_refs(struct btrfs_root *root,
- 		free_all_extent_backrefs(rec);
- 		free(rec);
- 	}
-+repair_abort:
-+	if (repair) {
-+		if (ret) {
-+			fprintf(stderr, "failed to repair damaged filesystem, aborting\n");
-+			exit(1);
-+		} else {
-+			btrfs_fix_block_accounting(trans, root);
-+		}
-+		if (err)
-+			fprintf(stderr, "repaired damaged extent references\n");
-+		return ret;
-+	}
- 	return err;
- }
- 
--static int check_extents(struct btrfs_root *root)
-+static int check_extents(struct btrfs_trans_handle *trans,
-+			 struct btrfs_root *root, int repair)
- {
- 	struct cache_tree extent_cache;
- 	struct cache_tree seen;
- 	struct cache_tree pending;
- 	struct cache_tree reada;
- 	struct cache_tree nodes;
-+	struct cache_tree corrupt_blocks;
- 	struct btrfs_path path;
- 	struct btrfs_key key;
- 	struct btrfs_key found_key;
-@@ -2372,6 +3322,13 @@ static int check_extents(struct btrfs_root *root)
- 	cache_tree_init(&pending);
- 	cache_tree_init(&nodes);
- 	cache_tree_init(&reada);
-+	cache_tree_init(&corrupt_blocks);
-+
-+	if (repair) {
-+		root->fs_info->fsck_extent_cache = &extent_cache;
-+		root->fs_info->free_extent_hook = free_extent_hook;
-+		root->fs_info->corrupt_blocks = &corrupt_blocks;
-+	}
- 
- 	bits_nr = 1024;
- 	bits = malloc(bits_nr * sizeof(struct block_info));
-@@ -2430,7 +3387,15 @@ static int check_extents(struct btrfs_root *root)
- 		if (ret != 0)
- 			break;
- 	}
--	ret = check_extent_refs(root, &extent_cache);
-+	ret = check_extent_refs(trans, root, &extent_cache, repair);
-+
-+	if (repair) {
-+		free_corrupt_blocks(root->fs_info);
-+		root->fs_info->fsck_extent_cache = NULL;
-+		root->fs_info->free_extent_hook = NULL;
-+		root->fs_info->corrupt_blocks = NULL;
-+	}
-+
- 	return ret;
- }
- 
-@@ -2441,29 +3406,120 @@ static void print_usage(void)
- 	exit(1);
- }
- 
-+static struct option long_options[] = {
-+	{ "super", 1, NULL, 's' },
-+	{ "repair", 0, NULL, 0 },
-+	{ "init-csum-tree", 0, NULL, 0 },
-+	{ "init-extent-tree", 0, NULL, 0 },
-+	{ 0, 0, 0, 0}
-+};
-+
- int main(int ac, char **av)
- {
-+	struct cache_tree root_cache;
- 	struct btrfs_root *root;
-+	struct btrfs_fs_info *info;
-+	struct btrfs_trans_handle *trans = NULL;
-+	u64 bytenr = 0;
- 	int ret;
-+	int num;
-+	int repair = 0;
-+	int option_index = 0;
-+	int init_csum_tree = 0;
-+	int rw = 0;
-+
-+	while(1) {
-+		int c;
-+		c = getopt_long(ac, av, "", long_options,
-+				&option_index);
-+		if (c < 0)
-+			break;
-+		switch(c) {
-+			case 's':
-+				num = atol(optarg);
-+				bytenr = btrfs_sb_offset(num);
-+				printf("using SB copy %d, bytenr %llu\n", num,
-+				       (unsigned long long)bytenr);
-+				break;
-+			case '?':
-+				print_usage();
-+		}
-+		if (option_index == 1) {
-+			printf("enabling repair mode\n");
-+			repair = 1;
-+			rw = 1;
-+		} else if (option_index == 2) {
-+			printf("Creating a new CRC tree\n");
-+			init_csum_tree = 1;
-+			rw = 1;
-+		}
-+
-+	}
-+	ac = ac - optind;
- 
--	if (ac < 2)
-+	if (ac != 1)
- 		print_usage();
- 
- 	radix_tree_init();
--	root = open_ctree(av[1], 0, 0);
-+	cache_tree_init(&root_cache);
- 
--	if (root == NULL)
-+	if((ret = check_mounted(av[optind])) < 0) {
-+		fprintf(stderr, "Could not check mount status: %s\n", strerror(-ret));
-+		return ret;
-+	} else if(ret) {
-+		fprintf(stderr, "%s is currently mounted. Aborting.\n", av[optind]);
-+		return -EBUSY;
-+	}
-+
-+	info = open_ctree_fs_info(av[optind], bytenr, rw, 1);
-+
-+	if (info == NULL)
- 		return 1;
- 
--	ret = check_extents(root);
-+	if (!extent_buffer_uptodate(info->tree_root->node) ||
-+	    !extent_buffer_uptodate(info->dev_root->node) ||
-+	    !extent_buffer_uptodate(info->extent_root->node) ||
-+	    !extent_buffer_uptodate(info->chunk_root->node)) {
-+		fprintf(stderr, "Critical roots corrupted, unable to fsck the FS\n");
-+		return -EIO;
-+	}
-+
-+	root = info->fs_root;
-+
-+	fprintf(stderr, "checking extents\n");
-+	if (rw)
-+		trans = btrfs_start_transaction(root, 1);
-+
-+	if (init_csum_tree) {
-+		fprintf(stderr, "Reinit crc root\n");
-+		ret = btrfs_fsck_reinit_root(trans, info->csum_root);
-+		if (ret) {
-+			fprintf(stderr, "crc root initialization failed\n");
-+			return -EIO;
-+		}
-+		goto out;
-+	}
-+	ret = check_extents(trans, root, repair);
-+	if (ret)
-+		fprintf(stderr, "Errors found in extent allocation tree\n");
-+
-+	fprintf(stderr, "checking fs roots\n");
-+	ret = check_fs_roots(root, &root_cache);
- 	if (ret)
- 		goto out;
--	ret = check_fs_roots(root);
- 
-+	fprintf(stderr, "checking root refs\n");
-+	ret = check_root_refs(root, &root_cache);
- out:
-+	free_root_recs(&root_cache);
-+	if (rw) {
-+		ret = btrfs_commit_transaction(trans, root);
-+		if (ret)
-+			exit(1);
-+	}
- 	close_ctree(root);
--	if (found_old_backref) {
--		/*
-+
-+	if (found_old_backref) { /*
- 		 * there was a disk format change when mixed
- 		 * backref was in testing tree. The old format
- 		 * existed about one week.
-diff --git a/btrfsctl.c b/btrfsctl.c
-index b323818..d45e2a7 100644
---- a/btrfsctl.c
-+++ b/btrfsctl.c
-@@ -29,6 +29,7 @@
- #include <unistd.h>
- #include <dirent.h>
- #include <libgen.h>
-+#include <stdlib.h>
- #include "kerncompat.h"
- #include "ctree.h"
- #include "transaction.h"
-@@ -46,7 +47,7 @@ static inline int ioctl(int fd, int define, void *arg) { return 0; }
- static void print_usage(void)
- {
- 	printf("usage: btrfsctl [ -d file|dir] [ -s snap_name subvol|tree ]\n");
--	printf("                [-r size] [-A device] [-a] [-c]\n");
-+	printf("                [-r size] [-A device] [-a] [-c] [-D dir .]\n");
- 	printf("\t-d filename: defragments one file\n");
- 	printf("\t-d directory: defragments the entire Btree\n");
- 	printf("\t-s snap_name dir: creates a new snapshot of dir\n");
-@@ -55,6 +56,9 @@ static void print_usage(void)
- 	printf("\t-A device: scans the device file for a Btrfs filesystem\n");
- 	printf("\t-a: scans all devices for Btrfs filesystems\n");
- 	printf("\t-c: forces a single FS sync\n");
-+	printf("\t-D: delete snapshot\n");
-+	printf("\t-m [tree id] directory: set the default mounted subvolume"
-+	       " to the [tree id] or the directory\n");
- 	printf("%s\n", BTRFS_BUILD_VERSION);
- 	exit(1);
- }
-@@ -99,8 +103,15 @@ int main(int ac, char **av)
- 	int i;
- 	unsigned long command = 0;
- 	int len;
-+	char *pos;
- 	char *fullpath;
-+	u64 objectid = 0;
- 
-+	printf( "**\n"
-+		"** WARNING: this program is considered deprecated\n"
-+		"** Please consider to switch to the btrfs utility\n"
-+		"**\n");
-+	
- 	if (ac == 2 && strcmp(av[1], "-a") == 0) {
- 		fprintf(stderr, "Scanning for Btrfs filesystems\n");
- 		btrfs_scan_one_dir("/dev", 1);
-@@ -158,6 +169,28 @@ int main(int ac, char **av)
- 				print_usage();
- 			}
- 			command = BTRFS_IOC_DEFRAG;
-+		} else if (strcmp(av[i], "-D") == 0) {
-+			if (i >= ac - 1) {
-+				fprintf(stderr, "-D requires an arg\n");
-+				print_usage();
-+			}
-+			command = BTRFS_IOC_SNAP_DESTROY;
-+			name = av[i + 1];
-+			len = strlen(name);
-+			pos = strchr(name, '/');
-+			if (pos) {
-+				if (*(pos + 1) == '\0')
-+					*(pos) = '\0';
-+				else {
-+					fprintf(stderr,
-+						"error: / not allowed in names\n");
-+					exit(1);
-+				}
-+			}
-+			if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
-+				fprintf(stderr, "-D size too long\n");
-+				exit(1);
-+			}
- 		} else if (strcmp(av[i], "-A") == 0) {
- 			if (i >= ac - 1) {
- 				fprintf(stderr, "-A requires an arg\n");
-@@ -178,6 +211,16 @@ int main(int ac, char **av)
- 			command = BTRFS_IOC_RESIZE;
- 		} else if (strcmp(av[i], "-c") == 0) {
- 			command = BTRFS_IOC_SYNC;
-+		} else if (strcmp(av[i], "-m") == 0) {
-+			command = BTRFS_IOC_DEFAULT_SUBVOL;
-+			if (i == ac - 3) {
-+				objectid = (unsigned long long)
-+					    strtoll(av[i + 1], NULL, 0);
-+				if (errno == ERANGE) {
-+					fprintf(stderr, "invalid tree id\n");
-+					exit(1);
-+				}
-+			}
- 		}
- 	}
- 	if (command == 0) {
-@@ -199,13 +242,16 @@ int main(int ac, char **av)
- 	 }
- 
- 	if (name)
--		strcpy(args.name, name);
-+                strncpy(args.name, name, BTRFS_PATH_NAME_MAX + 1);
- 	else
- 		args.name[0] = '\0';
- 
- 	if (command == BTRFS_IOC_SNAP_CREATE) {
- 		args.fd = fd;
- 		ret = ioctl(snap_fd, command, &args);
-+	} else if (command == BTRFS_IOC_DEFAULT_SUBVOL) {
-+		printf("objectid is %llu\n", (unsigned long long)objectid);
-+		ret = ioctl(fd, command, &objectid);
- 	} else
- 		ret = ioctl(fd, command, &args);
- 	if (ret < 0) {
-@@ -219,8 +265,8 @@ int main(int ac, char **av)
- 	}
- 	printf("%s\n", BTRFS_BUILD_VERSION);
- 	if (ret)
--		exit(0);
--	else
- 		exit(1);
-+
-+	return 0;
- }
- 
-diff --git a/btrfslabel.c b/btrfslabel.c
-new file mode 100644
-index 0000000..c9f4684
---- /dev/null
-+++ b/btrfslabel.c
-@@ -0,0 +1,121 @@
-+/*
-+ * Copyright (C) 2008 Morey Roof.   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 <sys/ioctl.h>
-+#include <sys/mount.h>
-+#include "ioctl.h"
-+#endif /* __CHECKER__ */
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <sys/types.h>
-+#include <sys/stat.h>
-+#include <dirent.h>
-+#include <fcntl.h>
-+#include <unistd.h>
-+#include <linux/fs.h>
-+#include <linux/limits.h>
-+#include <ctype.h>
-+#include "kerncompat.h"
-+#include "ctree.h"
-+#include "utils.h"
-+#include "version.h"
-+#include "disk-io.h"
-+#include "transaction.h"
-+
-+#define MOUNTED                        1
-+#define UNMOUNTED                      2
-+#define GET_LABEL                      3
-+#define SET_LABEL                      4
-+
-+static void change_label_unmounted(char *dev, char *nLabel)
-+{
-+       struct btrfs_root *root;
-+       struct btrfs_trans_handle *trans;
-+
-+       /* Open the super_block at the default location
-+        * and as read-write.
-+        */
-+       root = open_ctree(dev, 0, 1);
-+
-+       trans = btrfs_start_transaction(root, 1);
-+       strncpy(root->fs_info->super_copy.label, nLabel, BTRFS_LABEL_SIZE);
-+       btrfs_commit_transaction(trans, root);
-+
-+       /* Now we close it since we are done. */
-+       close_ctree(root);
-+}
-+
-+static void get_label_unmounted(char *dev)
-+{
-+       struct btrfs_root *root;
-+
-+       /* Open the super_block at the default location
-+        * and as read-only.
-+        */
-+       root = open_ctree(dev, 0, 0);
-+
-+       fprintf(stdout, "%s\n", root->fs_info->super_copy.label);
-+
-+       /* Now we close it since we are done. */
-+       close_ctree(root);
-+}
-+
-+int get_label(char *btrfs_dev)
-+{
-+
-+	int ret;
-+	ret = check_mounted(btrfs_dev);
-+	if (ret < 0)
-+	{
-+	       fprintf(stderr, "FATAL: error checking %s mount status\n", btrfs_dev);
-+	       return -1;
-+	}
-+
-+	if(ret != 0)
-+	{
-+	       fprintf(stderr, "FATAL: the filesystem has to be unmounted\n");
-+	       return -2;
-+	}
-+	get_label_unmounted(btrfs_dev);
-+	return 0;
-+}
-+
-+
-+int set_label(char *btrfs_dev, char *nLabel)
-+{
-+
-+	int ret;
-+	ret = check_mounted(btrfs_dev);
-+	if (ret < 0)
-+	{
-+	       fprintf(stderr, "FATAL: error checking %s mount status\n", btrfs_dev);
-+	       return -1;
-+	}
-+
-+	if(ret != 0)
-+	{
-+	       fprintf(stderr, "FATAL: the filesystem has to be unmounted\n");
-+	       return -2;
-+	}
-+	change_label_unmounted(btrfs_dev, nLabel);
-+	return 0;
-+}
-diff --git a/btrfslabel.h b/btrfslabel.h
-new file mode 100644
-index 0000000..abf43ad
---- /dev/null
-+++ b/btrfslabel.h
-@@ -0,0 +1,5 @@
-+/* btrflabel.h */
-+
-+
-+int get_label(char *btrfs_dev);
-+int set_label(char *btrfs_dev, char *nLabel);
-\ No newline at end of file
-diff --git a/calc-size.c b/calc-size.c
-new file mode 100644
-index 0000000..c4adfb0
---- /dev/null
-+++ b/calc-size.c
-@@ -0,0 +1,268 @@
-+/*
-+ * Copyright (C) 2011 Red Hat.  All rights reserved.
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public
-+ * License v2 as published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+ * General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public
-+ * License along with this program; if not, write to the
-+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-+ * Boston, MA 021110-1307, USA.
-+ */
-+
-+#define _XOPEN_SOURCE 500
-+#define _GNU_SOURCE 1
-+#include <ctype.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <fcntl.h>
-+#include <sys/stat.h>
-+#include <zlib.h>
-+#include "kerncompat.h"
-+#include "ctree.h"
-+#include "disk-io.h"
-+#include "print-tree.h"
-+#include "transaction.h"
-+#include "list.h"
-+#include "version.h"
-+#include "volumes.h"
-+#include "utils.h"
-+
-+static int verbose = 0;
-+static int no_pretty = 0;
-+
-+struct root_stats {
-+	u64 total_nodes;
-+	u64 total_leaves;
-+	u64 total_bytes;
-+	u64 total_inline;
-+	int total_levels;
-+};
-+
-+struct fs_root {
-+	struct btrfs_key key;
-+	struct btrfs_key *snaps;
-+};
-+
-+static int walk_leaf(struct btrfs_root *root, struct btrfs_path *path,
-+		     struct root_stats *stat, int find_inline)
-+{
-+	struct extent_buffer *b = path->nodes[0];
-+	struct btrfs_file_extent_item *fi;
-+	struct btrfs_key found_key;
-+	int i;
-+
-+	stat->total_bytes += root->leafsize;
-+	stat->total_leaves++;
-+
-+	if (!find_inline)
-+		return 0;
-+
-+	for (i = 0; i < btrfs_header_nritems(b); i++) {
-+		btrfs_item_key_to_cpu(b, &found_key, i);
-+		if (found_key.type != BTRFS_EXTENT_DATA_KEY)
-+			continue;
-+
-+		fi = btrfs_item_ptr(b, i, struct btrfs_file_extent_item);
-+		if (btrfs_file_extent_type(b, fi) == BTRFS_FILE_EXTENT_INLINE)
-+			stat->total_inline +=
-+				btrfs_file_extent_inline_item_len(b,
-+							btrfs_item_nr(b, i));
-+	}
-+
-+	return 0;
-+}
-+
-+static int walk_nodes(struct btrfs_root *root, struct btrfs_path *path,
-+		      struct root_stats *stat, int level, int find_inline)
-+{
-+	struct extent_buffer *b = path->nodes[level];
-+	int i;
-+	int ret = 0;
-+
-+	stat->total_bytes += root->nodesize;
-+	stat->total_nodes++;
-+
-+	for (i = 0; i < btrfs_header_nritems(b); i++) {
-+		struct extent_buffer *tmp = NULL;
-+
-+		path->slots[level] = i;
-+		if ((level - 1) > 0 || find_inline) {
-+			tmp = read_tree_block(root, btrfs_node_blockptr(b, i),
-+					      btrfs_level_size(root, level - 1),
-+					      btrfs_node_ptr_generation(b, i));
-+			if (!tmp) {
-+				fprintf(stderr, "Failed to read blocknr %Lu\n",
-+					btrfs_node_blockptr(b, i));
-+				continue;
-+			}
-+			path->nodes[level - 1] = tmp;
-+		}
-+		if (level - 1)
-+			ret = walk_nodes(root, path, stat, level - 1,
-+					 find_inline);
-+		else
-+			ret = walk_leaf(root, path, stat, find_inline);
-+		free_extent_buffer(tmp);
-+		if (ret) {
-+			fprintf(stderr, "Error walking down path\n");
-+			break;
-+		}
-+	}
-+
-+	return ret;
-+}
-+
-+static int calc_root_size(struct btrfs_root *tree_root, struct btrfs_key *key,
-+			  int find_inline)
-+{
-+	struct btrfs_root *root;
-+	struct btrfs_path *path;
-+	struct root_stats stat;
-+	int level;
-+	int ret = 0;
-+	int size_fail = 0;
-+
-+	root = btrfs_read_fs_root(tree_root->fs_info, key);
-+	if (!root) {
-+		fprintf(stderr, "Failed to read root %Lu\n", key->objectid);
-+		return 1;
-+	}
-+
-+	path = btrfs_alloc_path();
-+	if (!path) {
-+		fprintf(stderr, "Could not allocate path\n");
-+		return 1;
-+	}
-+
-+	memset(&stat, 0, sizeof(stat));
-+	level = btrfs_header_level(root->node);
-+	path->nodes[level] = root->node;
-+	if (!level) {
-+		ret = walk_leaf(root, path, &stat, find_inline);
-+		if (ret)
-+			goto out;
-+		goto out_print;
-+	}
-+
-+	ret = walk_nodes(root, path, &stat, level, find_inline);
-+	if (ret)
-+		goto out;
-+out_print:
-+	if (no_pretty || size_fail) {
-+		printf("\t%Lu total bytes, %Lu inline data bytes, %Lu nodes, "
-+		       "%Lu leaves, %d levels\n", stat.total_bytes,
-+		       stat.total_inline, stat.total_nodes, stat.total_leaves,
-+		       level + 1);
-+	} else {
-+		char *total_size;
-+		char *inline_size;
-+
-+		total_size = pretty_sizes(stat.total_bytes);
-+		inline_size = pretty_sizes(stat.total_inline);
-+
-+		printf("\t%s total size, %s inline data, %Lu nodes, "
-+		       "%Lu leaves, %d levels\n",
-+		       total_size, inline_size, stat.total_nodes,
-+		       stat.total_leaves, level + 1);
-+		free(total_size);
-+		free(inline_size);
-+	}
-+out:
-+	btrfs_free_path(path);
-+	return ret;
-+}
-+
-+static void usage()
-+{
-+	fprintf(stderr, "Usage: calc-size [-v] [-b] <device>\n");
-+}
-+
-+int main(int argc, char **argv)
-+{
-+	struct btrfs_key key;
-+	struct fs_root *roots;
-+	struct btrfs_root *root;
-+	size_t fs_roots_size = sizeof(struct fs_root);
-+	int opt;
-+	int ret = 0;
-+
-+	while ((opt = getopt(argc, argv, "vb")) != -1) {
-+		switch (opt) {
-+			case 'v':
-+				verbose++;
-+				break;
-+			case 'b':
-+				no_pretty = 1;
-+				break;
-+			default:
-+				usage();
-+				exit(1);
-+		}
-+	}
-+
-+	if (optind >= argc) {
-+		usage();
-+		exit(1);
-+	}
-+
-+	/*
-+	if ((ret = check_mounted(argv[optind])) < 0) {
-+		fprintf(stderr, "Could not check mount status: %d\n", ret);
-+		if (ret == -EACCES)
-+			fprintf(stderr, "Maybe you need to run as root?\n");
-+		return ret;
-+	} else if (ret) {
-+		fprintf(stderr, "%s is currently mounted.  Aborting.\n",
-+			argv[optind]);
-+		return -EBUSY;
-+	}
-+	*/
-+
-+	root = open_ctree(argv[optind], 0, 0);
-+	if (!root) {
-+		fprintf(stderr, "Couldn't open ctree\n");
-+		exit(1);
-+	}
-+
-+	roots = malloc(fs_roots_size);
-+	if (!roots) {
-+		fprintf(stderr, "No memory\n");
-+		goto out;
-+	}
-+
-+	printf("Calculating size of root tree\n");
-+	key.objectid = BTRFS_ROOT_TREE_OBJECTID;
-+	ret = calc_root_size(root, &key, 0);
-+	if (ret)
-+		goto out;
-+
-+	printf("Calculating size of extent tree\n");
-+	key.objectid = BTRFS_EXTENT_TREE_OBJECTID;
-+	ret = calc_root_size(root, &key, 0);
-+	if (ret)
-+		goto out;
-+
-+	printf("Calculating size of csum tree\n");
-+	key.objectid = BTRFS_CSUM_TREE_OBJECTID;
-+	ret = calc_root_size(root, &key, 0);
-+	if (ret)
-+		goto out;
-+
-+	roots[0].key.objectid = BTRFS_FS_TREE_OBJECTID;
-+	roots[0].key.offset = (u64)-1;
-+	printf("Calculatin' size of fs tree\n");
-+	ret = calc_root_size(root, &roots[0].key, 1);
-+	if (ret)
-+		goto out;
-+out:
-+	close_ctree(root);
-+	return ret;
-+}
-diff --git a/cmds-balance.c b/cmds-balance.c
-new file mode 100644
-index 0000000..38a7426
---- /dev/null
-+++ b/cmds-balance.c
-@@ -0,0 +1,713 @@
-+/*
-+ * 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.
-+ */
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <unistd.h>
-+#include <getopt.h>
-+#include <sys/ioctl.h>
-+#include <errno.h>
-+
-+#include "kerncompat.h"
-+#include "ctree.h"
-+#include "ioctl.h"
-+#include "volumes.h"
-+
-+#include "commands.h"
-+
-+static const char * const balance_cmd_group_usage[] = {
-+	"btrfs [filesystem] balance <command> [options] <path>",
-+	"btrfs [filesystem] balance <path>",
-+	NULL
-+};
-+
-+static const char balance_cmd_group_info[] =
-+	"'btrfs filesystem balance' command is deprecated, please use\n"
-+	"'btrfs balance start' command instead.";
-+
-+static int parse_one_profile(const char *profile, u64 *flags)
-+{
-+	if (!strcmp(profile, "raid0")) {
-+		*flags |= BTRFS_BLOCK_GROUP_RAID0;
-+	} else if (!strcmp(profile, "raid1")) {
-+		*flags |= BTRFS_BLOCK_GROUP_RAID1;
-+	} else if (!strcmp(profile, "raid10")) {
-+		*flags |= BTRFS_BLOCK_GROUP_RAID10;
-+	} else if (!strcmp(profile, "dup")) {
-+		*flags |= BTRFS_BLOCK_GROUP_DUP;
-+	} else if (!strcmp(profile, "single")) {
-+		*flags |= BTRFS_AVAIL_ALLOC_BIT_SINGLE;
-+	} else {
-+		fprintf(stderr, "Unknown profile '%s'\n", profile);
-+		return 1;
-+	}
-+
-+	return 0;
-+}
-+
-+static int parse_profiles(char *profiles, u64 *flags)
-+{
-+	char *this_char;
-+	char *save_ptr;
-+
-+	for (this_char = strtok_r(profiles, "|", &save_ptr);
-+	     this_char != NULL;
-+	     this_char = strtok_r(NULL, "|", &save_ptr)) {
-+		if (parse_one_profile(this_char, flags))
-+			return 1;
-+	}
-+
-+	return 0;
-+}
-+
-+static int parse_u64(const char *str, u64 *result)
-+{
-+	char *endptr;
-+	u64 val;
-+
-+	val = strtoull(str, &endptr, 10);
-+	if (*endptr)
-+		return 1;
-+
-+	*result = val;
-+	return 0;
-+}
-+
-+static int parse_range(const char *range, u64 *start, u64 *end)
-+{
-+	char *dots;
-+
-+	dots = strstr(range, "..");
-+	if (dots) {
-+		const char *rest = dots + 2;
-+		int skipped = 0;
-+
-+		*dots = 0;
-+
-+		if (!*rest) {
-+			*end = (u64)-1;
-+			skipped++;
-+		} else {
-+			if (parse_u64(rest, end))
-+				return 1;
-+		}
-+		if (dots == range) {
-+			*start = 0;
-+			skipped++;
-+		} else {
-+			if (parse_u64(range, start))
-+				return 1;
-+		}
-+
-+		if (*start >= *end) {
-+			fprintf(stderr, "Range %llu..%llu doesn't make "
-+				"sense\n", (unsigned long long)*start,
-+				(unsigned long long)*end);
-+			return 1;
-+		}
-+
-+		if (skipped <= 1)
-+			return 0;
-+	}
-+
-+	return 1;
-+}
-+
-+static int parse_filters(char *filters, struct btrfs_balance_args *args)
-+{
-+	char *this_char;
-+	char *value;
-+	char *save_ptr;
-+
-+	if (!filters)
-+		return 0;
-+
-+	for (this_char = strtok_r(filters, ",", &save_ptr);
-+	     this_char != NULL;
-+	     this_char = strtok_r(NULL, ",", &save_ptr)) {
-+		if ((value = strchr(this_char, '=')) != NULL)
-+			*value++ = 0;
-+		if (!strcmp(this_char, "profiles")) {
-+			if (!value || !*value) {
-+				fprintf(stderr, "the profiles filter requires "
-+				       "an argument\n");
-+				return 1;
-+			}
-+			if (parse_profiles(value, &args->profiles)) {
-+				fprintf(stderr, "Invalid profiles argument\n");
-+				return 1;
-+			}
-+			args->flags |= BTRFS_BALANCE_ARGS_PROFILES;
-+		} else if (!strcmp(this_char, "usage")) {
-+			if (!value || !*value) {
-+				fprintf(stderr, "the usage filter requires "
-+				       "an argument\n");
-+				return 1;
-+			}
-+			if (parse_u64(value, &args->usage) ||
-+			    args->usage < 1 || args->usage > 100) {
-+				fprintf(stderr, "Invalid usage argument: %s\n",
-+				       value);
-+				return 1;
-+			}
-+			args->flags |= BTRFS_BALANCE_ARGS_USAGE;
-+		} else if (!strcmp(this_char, "devid")) {
-+			if (!value || !*value) {
-+				fprintf(stderr, "the devid filter requires "
-+				       "an argument\n");
-+				return 1;
-+			}
-+			if (parse_u64(value, &args->devid) ||
-+			    args->devid == 0) {
-+				fprintf(stderr, "Invalid devid argument: %s\n",
-+				       value);
-+				return 1;
-+			}
-+			args->flags |= BTRFS_BALANCE_ARGS_DEVID;
-+		} else if (!strcmp(this_char, "drange")) {
-+			if (!value || !*value) {
-+				fprintf(stderr, "the drange filter requires "
-+				       "an argument\n");
-+				return 1;
-+			}
-+			if (parse_range(value, &args->pstart, &args->pend)) {
-+				fprintf(stderr, "Invalid drange argument\n");
-+				return 1;
-+			}
-+			args->flags |= BTRFS_BALANCE_ARGS_DRANGE;
-+		} else if (!strcmp(this_char, "vrange")) {
-+			if (!value || !*value) {
-+				fprintf(stderr, "the vrange filter requires "
-+				       "an argument\n");
-+				return 1;
-+			}
-+			if (parse_range(value, &args->vstart, &args->vend)) {
-+				fprintf(stderr, "Invalid vrange argument\n");
-+				return 1;
-+			}
-+			args->flags |= BTRFS_BALANCE_ARGS_VRANGE;
-+		} else if (!strcmp(this_char, "convert")) {
-+			if (!value || !*value) {
-+				fprintf(stderr, "the convert option requires "
-+				       "an argument\n");
-+				return 1;
-+			}
-+			if (parse_one_profile(value, &args->target)) {
-+				fprintf(stderr, "Invalid convert argument\n");
-+				return 1;
-+			}
-+			args->flags |= BTRFS_BALANCE_ARGS_CONVERT;
-+		} else if (!strcmp(this_char, "soft")) {
-+			args->flags |= BTRFS_BALANCE_ARGS_SOFT;
-+		} else {
-+			fprintf(stderr, "Unrecognized balance option '%s'\n",
-+				this_char);
-+			return 1;
-+		}
-+	}
-+
-+	return 0;
-+}
-+
-+static void dump_balance_args(struct btrfs_balance_args *args)
-+{
-+	if (args->flags & BTRFS_BALANCE_ARGS_CONVERT) {
-+		printf("converting, target=%llu, soft is %s",
-+		       (unsigned long long)args->target,
-+		       (args->flags & BTRFS_BALANCE_ARGS_SOFT) ? "on" : "off");
-+	} else {
-+		printf("balancing");
-+	}
-+
-+	if (args->flags & BTRFS_BALANCE_ARGS_PROFILES)
-+		printf(", profiles=%llu", (unsigned long long)args->profiles);
-+	if (args->flags & BTRFS_BALANCE_ARGS_USAGE)
-+		printf(", usage=%llu", (unsigned long long)args->usage);
-+	if (args->flags & BTRFS_BALANCE_ARGS_DEVID)
-+		printf(", devid=%llu", (unsigned long long)args->devid);
-+	if (args->flags & BTRFS_BALANCE_ARGS_DRANGE)
-+		printf(", drange=%llu..%llu",
-+		       (unsigned long long)args->pstart,
-+		       (unsigned long long)args->pend);
-+	if (args->flags & BTRFS_BALANCE_ARGS_VRANGE)
-+		printf(", vrange=%llu..%llu",
-+		       (unsigned long long)args->vstart,
-+		       (unsigned long long)args->vend);
-+
-+	printf("\n");
-+}
-+
-+static void dump_ioctl_balance_args(struct btrfs_ioctl_balance_args *args)
-+{
-+	printf("Dumping filters: flags 0x%llx, state 0x%llx, force is %s\n",
-+	       (unsigned long long)args->flags, (unsigned long long)args->state,
-+	       (args->flags & BTRFS_BALANCE_FORCE) ? "on" : "off");
-+	if (args->flags & BTRFS_BALANCE_DATA) {
-+		printf("  DATA (flags 0x%llx): ",
-+		       (unsigned long long)args->data.flags);
-+		dump_balance_args(&args->data);
-+	}
-+	if (args->flags & BTRFS_BALANCE_METADATA) {
-+		printf("  METADATA (flags 0x%llx): ",
-+		       (unsigned long long)args->meta.flags);
-+		dump_balance_args(&args->meta);
-+	}
-+	if (args->flags & BTRFS_BALANCE_SYSTEM) {
-+		printf("  SYSTEM (flags 0x%llx): ",
-+		       (unsigned long long)args->sys.flags);
-+		dump_balance_args(&args->sys);
-+	}
-+}
-+
-+static int do_balance_v1(int fd)
-+{
-+	struct btrfs_ioctl_vol_args args;
-+	int ret;
-+
-+	memset(&args, 0, sizeof(args));
-+	ret = ioctl(fd, BTRFS_IOC_BALANCE, &args);
-+	return ret;
-+}
-+
-+static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args,
-+		      int nofilters)
-+{
-+	int fd;
-+	int ret;
-+	int e;
-+
-+	fd = open_file_or_dir(path);
-+	if (fd < 0) {
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-+		return 12;
-+	}
-+
-+	ret = ioctl(fd, BTRFS_IOC_BALANCE_V2, args);
-+	e = errno;
-+
-+	if (ret < 0) {
-+		/*
-+		 * older kernels don't have the new balance ioctl, try the
-+		 * old one.  But, the old one doesn't know any filters, so
-+		 * don't fall back if they tried to use the fancy new things
-+		 */
-+		if (e == ENOTTY && nofilters) {
-+			ret = do_balance_v1(fd);
-+			if (ret == 0)
-+				goto out;
-+			e = errno;
-+		}
-+
-+		if (e == ECANCELED) {
-+			if (args->state & BTRFS_BALANCE_STATE_PAUSE_REQ)
-+				fprintf(stderr, "balance paused by user\n");
-+			if (args->state & BTRFS_BALANCE_STATE_CANCEL_REQ)
-+				fprintf(stderr, "balance canceled by user\n");
-+			ret = 0;
-+		} else {
-+			fprintf(stderr, "ERROR: error during balancing '%s' "
-+				"- %s\n", path, strerror(e));
-+			if (e != EINPROGRESS)
-+				fprintf(stderr, "There may be more info in "
-+					"syslog - try dmesg | tail\n");
-+			ret = 19;
-+		}
-+	} else {
-+		printf("Done, had to relocate %llu out of %llu chunks\n",
-+		       (unsigned long long)args->stat.completed,
-+		       (unsigned long long)args->stat.considered);
-+		ret = 0;
-+	}
-+
-+out:
-+	close(fd);
-+	return ret;
-+}
-+
-+static const char * const cmd_balance_start_usage[] = {
-+	"btrfs [filesystem] balance start [options] <path>",
-+	"Balance chunks across the devices",
-+	"Balance and/or convert (change allocation profile of) chunks that",
-+	"passed all filters in a comma-separated list of filters for a",
-+	"particular chunk type.  If filter list is not given balance all",
-+	"chunks of that type.  In case none of the -d, -m or -s options is",
-+	"given balance all chunks in a filesystem.",
-+	"",
-+	"-d[filters]    act on data chunks",
-+	"-m[filters]    act on metadata chunks",
-+	"-s[filetrs]    act on system chunks (only under -f)",
-+	"-v             be verbose",
-+	"-f             force reducing of metadata integrity",
-+	NULL
-+};
-+
-+static int cmd_balance_start(int argc, char **argv)
-+{
-+	struct btrfs_ioctl_balance_args args;
-+	struct btrfs_balance_args *ptrs[] = { &args.data, &args.sys,
-+						&args.meta, NULL };
-+	int force = 0;
-+	int verbose = 0;
-+	int nofilters = 1;
-+	int i;
-+
-+	memset(&args, 0, sizeof(args));
-+
-+	optind = 1;
-+	while (1) {
-+		int longindex;
-+		static struct option longopts[] = {
-+			{ "data", optional_argument, NULL, 'd'},
-+			{ "metadata", optional_argument, NULL, 'm' },
-+			{ "system", optional_argument, NULL, 's' },
-+			{ "force", no_argument, NULL, 'f' },
-+			{ "verbose", no_argument, NULL, 'v' },
-+			{ 0, 0, 0, 0 }
-+		};
-+
-+		int opt = getopt_long(argc, argv, "d::s::m::fv", longopts,
-+				      &longindex);
-+		if (opt < 0)
-+			break;
-+
-+		switch (opt) {
-+		case 'd':
-+			nofilters = 0;
-+			args.flags |= BTRFS_BALANCE_DATA;
-+
-+			if (parse_filters(optarg, &args.data))
-+				return 1;
-+			break;
-+		case 's':
-+			nofilters = 0;
-+			args.flags |= BTRFS_BALANCE_SYSTEM;
-+
-+			if (parse_filters(optarg, &args.sys))
-+				return 1;
-+			break;
-+		case 'm':
-+			nofilters = 0;
-+			args.flags |= BTRFS_BALANCE_METADATA;
-+
-+			if (parse_filters(optarg, &args.meta))
-+				return 1;
-+			break;
-+		case 'f':
-+			force = 1;
-+			break;
-+		case 'v':
-+			verbose = 1;
-+			break;
-+		default:
-+			usage(cmd_balance_start_usage);
-+		}
-+	}
-+
-+	if (check_argc_exact(argc - optind, 1))
-+		usage(cmd_balance_start_usage);
-+
-+	/*
-+	 * allow -s only under --force, otherwise do with system chunks
-+	 * the same thing we were ordered to do with meta chunks
-+	 */
-+	if (args.flags & BTRFS_BALANCE_SYSTEM) {
-+		if (!force) {
-+			fprintf(stderr,
-+"Refusing to explicitly operate on system chunks.\n"
-+"Pass --force if you really want to do that.\n");
-+			return 1;
-+		}
-+	} else if (args.flags & BTRFS_BALANCE_METADATA) {
-+		args.flags |= BTRFS_BALANCE_SYSTEM;
-+		memcpy(&args.sys, &args.meta,
-+			sizeof(struct btrfs_balance_args));
-+	}
-+
-+	if (nofilters) {
-+		/* relocate everything - no filters */
-+		args.flags |= BTRFS_BALANCE_TYPE_MASK;
-+	}
-+
-+	/* drange makes sense only when devid is set */
-+	for (i = 0; ptrs[i]; i++) {
-+		if ((ptrs[i]->flags & BTRFS_BALANCE_ARGS_DRANGE) &&
-+		    !(ptrs[i]->flags & BTRFS_BALANCE_ARGS_DEVID)) {
-+			fprintf(stderr, "drange filter can be used only if "
-+				"devid filter is used\n");
-+			return 1;
-+		}
-+	}
-+
-+	/* soft makes sense only when convert for corresponding type is set */
-+	for (i = 0; ptrs[i]; i++) {
-+		if ((ptrs[i]->flags & BTRFS_BALANCE_ARGS_SOFT) &&
-+		    !(ptrs[i]->flags & BTRFS_BALANCE_ARGS_CONVERT)) {
-+			fprintf(stderr, "'soft' option can be used only if "
-+				"changing profiles\n");
-+			return 1;
-+		}
-+	}
-+
-+	if (force)
-+		args.flags |= BTRFS_BALANCE_FORCE;
-+	if (verbose)
-+		dump_ioctl_balance_args(&args);
-+
-+	return do_balance(argv[optind], &args, nofilters);
-+}
-+
-+static const char * const cmd_balance_pause_usage[] = {
-+	"btrfs [filesystem] balance pause <path>",
-+	"Pause running balance",
-+	NULL
-+};
-+
-+static int cmd_balance_pause(int argc, char **argv)
-+{
-+	const char *path;
-+	int fd;
-+	int ret;
-+	int e;
-+
-+	if (check_argc_exact(argc, 2))
-+		usage(cmd_balance_pause_usage);
-+
-+	path = argv[1];
-+
-+	fd = open_file_or_dir(path);
-+	if (fd < 0) {
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-+		return 12;
-+	}
-+
-+	ret = ioctl(fd, BTRFS_IOC_BALANCE_CTL, BTRFS_BALANCE_CTL_PAUSE);
-+	e = errno;
-+	close(fd);
-+
-+	if (ret < 0) {
-+		fprintf(stderr, "ERROR: balance pause on '%s' failed - %s\n",
-+			path, (e == ENOTCONN) ? "Not running" : strerror(e));
-+		return 19;
-+	}
-+
-+	return 0;
-+}
-+
-+static const char * const cmd_balance_cancel_usage[] = {
-+	"btrfs [filesystem] balance cancel <path>",
-+	"Cancel running or paused balance",
-+	NULL
-+};
-+
-+static int cmd_balance_cancel(int argc, char **argv)
-+{
-+	const char *path;
-+	int fd;
-+	int ret;
-+	int e;
-+
-+	if (check_argc_exact(argc, 2))
-+		usage(cmd_balance_cancel_usage);
-+
-+	path = argv[1];
-+
-+	fd = open_file_or_dir(path);
-+	if (fd < 0) {
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-+		return 12;
-+	}
-+
-+	ret = ioctl(fd, BTRFS_IOC_BALANCE_CTL, BTRFS_BALANCE_CTL_CANCEL);
-+	e = errno;
-+	close(fd);
-+
-+	if (ret < 0) {
-+		fprintf(stderr, "ERROR: balance cancel on '%s' failed - %s\n",
-+			path, (e == ENOTCONN) ? "Not in progress" : strerror(e));
-+		return 19;
-+	}
-+
-+	return 0;
-+}
-+
-+static const char * const cmd_balance_resume_usage[] = {
-+	"btrfs [filesystem] balance resume <path>",
-+	"Resume interrupted balance",
-+	NULL
-+};
-+
-+static int cmd_balance_resume(int argc, char **argv)
-+{
-+	struct btrfs_ioctl_balance_args args;
-+	const char *path;
-+	int fd;
-+	int ret;
-+	int e;
-+
-+	if (check_argc_exact(argc, 2))
-+		usage(cmd_balance_resume_usage);
-+
-+	path = argv[1];
-+
-+	fd = open_file_or_dir(path);
-+	if (fd < 0) {
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-+		return 12;
-+	}
-+
-+	memset(&args, 0, sizeof(args));
-+	args.flags |= BTRFS_BALANCE_RESUME;
-+
-+	ret = ioctl(fd, BTRFS_IOC_BALANCE_V2, &args);
-+	e = errno;
-+	close(fd);
-+
-+	if (ret < 0) {
-+		if (e == ECANCELED) {
-+			if (args.state & BTRFS_BALANCE_STATE_PAUSE_REQ)
-+				fprintf(stderr, "balance paused by user\n");
-+			if (args.state & BTRFS_BALANCE_STATE_CANCEL_REQ)
-+				fprintf(stderr, "balance canceled by user\n");
-+		} else if (e == ENOTCONN || e == EINPROGRESS) {
-+			fprintf(stderr, "ERROR: balance resume on '%s' "
-+				"failed - %s\n", path,
-+				(e == ENOTCONN) ? "Not in progress" :
-+						  "Already running");
-+			return 19;
-+		} else {
-+			fprintf(stderr,
-+"ERROR: error during balancing '%s' - %s\n"
-+"There may be more info in syslog - try dmesg | tail\n", path, strerror(e));
-+			return 19;
-+		}
-+	} else {
-+		printf("Done, had to relocate %llu out of %llu chunks\n",
-+		       (unsigned long long)args.stat.completed,
-+		       (unsigned long long)args.stat.considered);
-+	}
-+
-+	return 0;
-+}
-+
-+static const char * const cmd_balance_status_usage[] = {
-+	"btrfs [filesystem] balance status [-v] <path>",
-+	"Show status of running or paused balance",
-+	"",
-+	"-v     be verbose",
-+	NULL
-+};
-+
-+static int cmd_balance_status(int argc, char **argv)
-+{
-+	struct btrfs_ioctl_balance_args args;
-+	const char *path;
-+	int fd;
-+	int verbose = 0;
-+	int ret;
-+	int e;
-+
-+	optind = 1;
-+	while (1) {
-+		int longindex;
-+		static struct option longopts[] = {
-+			{ "verbose", no_argument, NULL, 'v' },
-+			{ 0, 0, 0, 0}
-+		};
-+
-+		int opt = getopt_long(argc, argv, "v", longopts, &longindex);
-+		if (opt < 0)
-+			break;
-+
-+		switch (opt) {
-+		case 'v':
-+			verbose = 1;
-+			break;
-+		default:
-+			usage(cmd_balance_status_usage);
-+		}
-+	}
-+
-+	if (check_argc_exact(argc - optind, 1))
-+		usage(cmd_balance_status_usage);
-+
-+	path = argv[optind];
-+
-+	fd = open_file_or_dir(path);
-+	if (fd < 0) {
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-+		return 12;
-+	}
-+
-+	ret = ioctl(fd, BTRFS_IOC_BALANCE_PROGRESS, &args);
-+	e = errno;
-+	close(fd);
-+
-+	if (ret < 0) {
-+		fprintf(stderr, "ERROR: balance status on '%s' failed - %s\n",
-+			path, (e == ENOTCONN) ? "Not in progress" : strerror(e));
-+		return 19;
-+	}
-+
-+	if (args.state & BTRFS_BALANCE_STATE_RUNNING) {
-+		printf("Balance on '%s' is running", path);
-+		if (args.state & BTRFS_BALANCE_STATE_CANCEL_REQ)
-+			printf(", cancel requested\n");
-+		else if (args.state & BTRFS_BALANCE_STATE_PAUSE_REQ)
-+			printf(", pause requested\n");
-+		else
-+			printf("\n");
-+	} else {
-+		printf("Balance on '%s' is paused\n", path);
-+	}
-+
-+	printf("%llu out of about %llu chunks balanced (%llu considered), "
-+	       "%3.f%% left\n", (unsigned long long)args.stat.completed,
-+	       (unsigned long long)args.stat.expected,
-+	       (unsigned long long)args.stat.considered,
-+	       100 * (1 - (float)args.stat.completed/args.stat.expected));
-+
-+	if (verbose)
-+		dump_ioctl_balance_args(&args);
-+
-+	return 0;
-+}
-+
-+const struct cmd_group balance_cmd_group = {
-+	balance_cmd_group_usage, balance_cmd_group_info, {
-+		{ "start", cmd_balance_start, cmd_balance_start_usage, NULL, 0 },
-+		{ "pause", cmd_balance_pause, cmd_balance_pause_usage, NULL, 0 },
-+		{ "cancel", cmd_balance_cancel, cmd_balance_cancel_usage, NULL, 0 },
-+		{ "resume", cmd_balance_resume, cmd_balance_resume_usage, NULL, 0 },
-+		{ "status", cmd_balance_status, cmd_balance_status_usage, NULL, 0 },
-+		{ 0, 0, 0, 0, 0 }
-+	}
-+};
-+
-+int cmd_balance(int argc, char **argv)
-+{
-+	if (argc == 2) {
-+		/* old 'btrfs filesystem balance <path>' syntax */
-+		struct btrfs_ioctl_balance_args args;
-+
-+		memset(&args, 0, sizeof(args));
-+		args.flags |= BTRFS_BALANCE_TYPE_MASK;
-+
-+		return do_balance(argv[1], &args, 1);
-+	}
-+
-+	return handle_command_group(&balance_cmd_group, argc, argv);
-+}
-diff --git a/cmds-device.c b/cmds-device.c
-new file mode 100644
-index 0000000..db625a6
---- /dev/null
-+++ b/cmds-device.c
-@@ -0,0 +1,261 @@
-+/*
-+ * 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.
-+ */
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <unistd.h>
-+#include <fcntl.h>
-+#include <sys/ioctl.h>
-+#include <errno.h>
-+#include <sys/stat.h>
-+
-+#include "kerncompat.h"
-+#include "ctree.h"
-+#include "ioctl.h"
-+#include "utils.h"
-+
-+#include "commands.h"
-+
-+/* FIXME - imported cruft, fix sparse errors and warnings */
-+#ifdef __CHECKER__
-+#define BLKGETSIZE64 0
-+#define BTRFS_IOC_SNAP_CREATE_V2 0
-+#define BTRFS_VOL_NAME_MAX 255
-+struct btrfs_ioctl_vol_args { char name[BTRFS_VOL_NAME_MAX]; };
-+static inline int ioctl(int fd, int define, void *arg) { return 0; }
-+#endif
-+
-+static const char * const device_cmd_group_usage[] = {
-+	"btrfs device <command> [<args>]",
-+	NULL
-+};
-+
-+static const char * const cmd_add_dev_usage[] = {
-+	"btrfs device add <device> [<device>...] <path>",
-+	"Add a device to a filesystem",
-+	NULL
-+};
-+
-+static int cmd_add_dev(int argc, char **argv)
-+{
-+	char	*mntpnt;
-+	int	i, fdmnt, ret=0, e;
-+
-+	if (check_argc_min(argc, 3))
-+		usage(cmd_add_dev_usage);
-+
-+	mntpnt = argv[argc - 1];
-+
-+	fdmnt = open_file_or_dir(mntpnt);
-+	if (fdmnt < 0) {
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt);
-+		return 12;
-+	}
-+
-+	for (i = 1; i < argc - 1; i++ ){
-+		struct btrfs_ioctl_vol_args ioctl_args;
-+		int	devfd, res;
-+		u64 dev_block_count = 0;
-+		struct stat st;
-+		int mixed = 0;
-+
-+		res = check_mounted(argv[i]);
-+		if (res < 0) {
-+			fprintf(stderr, "error checking %s mount status\n",
-+				argv[i]);
-+			ret++;
-+			continue;
-+		}
-+		if (res == 1) {
-+			fprintf(stderr, "%s is mounted\n", argv[i]);
-+			ret++;
-+			continue;
-+		}
-+
-+		devfd = open(argv[i], O_RDWR);
-+		if (!devfd) {
-+			fprintf(stderr, "ERROR: Unable to open device '%s'\n", argv[i]);
-+			close(devfd);
-+			ret++;
-+			continue;
-+		}
-+		res = fstat(devfd, &st);
-+		if (res) {
-+			fprintf(stderr, "ERROR: Unable to stat '%s'\n", argv[i]);
-+			close(devfd);
-+			ret++;
-+			continue;
-+		}
-+		if (!S_ISBLK(st.st_mode)) {
-+			fprintf(stderr, "ERROR: '%s' is not a block device\n", argv[i]);
-+			close(devfd);
-+			ret++;
-+			continue;
-+		}
-+
-+		res = btrfs_prepare_device(devfd, argv[i], 1, &dev_block_count, &mixed);
-+		if (res) {
-+			fprintf(stderr, "ERROR: Unable to init '%s'\n", argv[i]);
-+			close(devfd);
-+			ret++;
-+			continue;
-+		}
-+		close(devfd);
-+
-+		strncpy(ioctl_args.name, argv[i], BTRFS_PATH_NAME_MAX);
-+		res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args);
-+		e = errno;
-+		if(res<0){
-+			fprintf(stderr, "ERROR: error adding the device '%s' - %s\n",
-+				argv[i], strerror(e));
-+			ret++;
-+		}
-+
-+	}
-+
-+	close(fdmnt);
-+	if (ret)
-+		return ret+20;
-+	else
-+		return 0;
-+}
-+
-+static const char * const cmd_rm_dev_usage[] = {
-+	"btrfs device delete <device> [<device>...] <path>",
-+	"Remove a device from a filesystem",
-+	NULL
-+};
-+
-+static int cmd_rm_dev(int argc, char **argv)
-+{
-+	char	*mntpnt;
-+	int	i, fdmnt, ret=0, e;
-+
-+	if (check_argc_min(argc, 3))
-+		usage(cmd_rm_dev_usage);
-+
-+	mntpnt = argv[argc - 1];
-+
-+	fdmnt = open_file_or_dir(mntpnt);
-+	if (fdmnt < 0) {
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt);
-+		return 12;
-+	}
-+
-+	for(i=1 ; i < argc - 1; i++ ){
-+		struct	btrfs_ioctl_vol_args arg;
-+		int	res;
-+
-+		strncpy(arg.name, argv[i], BTRFS_PATH_NAME_MAX);
-+		res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg);
-+		e = errno;
-+		if(res<0){
-+			fprintf(stderr, "ERROR: error removing the device '%s' - %s\n",
-+				argv[i], strerror(e));
-+			ret++;
-+		}
-+	}
-+
-+	close(fdmnt);
-+	if( ret)
-+		return ret+20;
-+	else
-+		return 0;
-+}
-+
-+static const char * const cmd_scan_dev_usage[] = {
-+	"btrfs device scan [<device>...]",
-+	"Scan devices for a btrfs filesystem",
-+	NULL
-+};
-+
-+static int cmd_scan_dev(int argc, char **argv)
-+{
-+	int	i, fd, e;
-+	int	checklist = 1;
-+	int	devstart = 1;
-+
-+	if( argc > 1 && !strcmp(argv[1],"--all-devices")){
-+		if (check_argc_max(argc, 2))
-+			usage(cmd_scan_dev_usage);
-+
-+		checklist = 0;
-+		devstart += 1;
-+	}
-+
-+	if(argc<=devstart){
-+
-+		int ret;
-+
-+		printf("Scanning for Btrfs filesystems\n");
-+		if(checklist)
-+			ret = btrfs_scan_block_devices(1);
-+		else
-+			ret = btrfs_scan_one_dir("/dev", 1);
-+		if (ret){
-+			fprintf(stderr, "ERROR: error %d while scanning\n", ret);
-+			return 18;
-+		}
-+		return 0;
-+	}
-+
-+	fd = open("/dev/btrfs-control", O_RDWR);
-+	if (fd < 0) {
-+		perror("failed to open /dev/btrfs-control");
-+		return 10;
-+	}
-+
-+	for( i = devstart ; i < argc ; i++ ){
-+		struct btrfs_ioctl_vol_args args;
-+		int ret;
-+
-+		printf("Scanning for Btrfs filesystems in '%s'\n", argv[i]);
-+
-+		strncpy(args.name, argv[i], BTRFS_PATH_NAME_MAX);
-+		/*
-+		 * FIXME: which are the error code returned by this ioctl ?
-+		 * it seems that is impossible to understand if there no is
-+		 * a btrfs filesystem from an I/O error !!!
-+		 */
-+		ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args);
-+		e = errno;
-+
-+		if( ret < 0 ){
-+			close(fd);
-+			fprintf(stderr, "ERROR: unable to scan the device '%s' - %s\n",
-+				argv[i], strerror(e));
-+			return 11;
-+		}
-+	}
-+
-+	close(fd);
-+	return 0;
-+}
-+
-+const struct cmd_group device_cmd_group = {
-+	device_cmd_group_usage, NULL, {
-+		{ "add", cmd_add_dev, cmd_add_dev_usage, NULL, 0 },
-+		{ "delete", cmd_rm_dev, cmd_rm_dev_usage, NULL, 0 },
-+		{ "scan", cmd_scan_dev, cmd_scan_dev_usage, NULL, 0 },
-+		{ 0, 0, 0, 0, 0 }
-+	}
-+};
-+
-+int cmd_device(int argc, char **argv)
-+{
-+	return handle_command_group(&device_cmd_group, argc, argv);
-+}
-diff --git a/cmds-filesystem.c b/cmds-filesystem.c
-new file mode 100644
-index 0000000..1f53d1c
---- /dev/null
-+++ b/cmds-filesystem.c
-@@ -0,0 +1,538 @@
-+/*
-+ * 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.
-+ */
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <unistd.h>
-+#include <sys/ioctl.h>
-+#include <errno.h>
-+#include <uuid/uuid.h>
-+#include <ctype.h>
-+
-+#include "kerncompat.h"
-+#include "ctree.h"
-+#include "ioctl.h"
-+#include "utils.h"
-+#include "volumes.h"
-+
-+#include "version.h"
-+
-+#include "commands.h"
-+#include "btrfslabel.h"
-+
-+static const char * const filesystem_cmd_group_usage[] = {
-+	"btrfs filesystem [<group>] <command> [<args>]",
-+	NULL
-+};
-+
-+static const char * const cmd_df_usage[] = {
-+	"btrfs filesystem df <path>",
-+	"Show space usage information for a mount point",
-+	NULL
-+};
-+
-+static int cmd_df(int argc, char **argv)
-+{
-+	struct btrfs_ioctl_space_args *sargs;
-+	u64 count = 0, i;
-+	int ret;
-+	int fd;
-+	int e;
-+	char *path;
-+
-+	if (check_argc_exact(argc, 2))
-+		usage(cmd_df_usage);
-+
-+	path = argv[1];
-+
-+	fd = open_file_or_dir(path);
-+	if (fd < 0) {
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-+		return 12;
-+	}
-+
-+	sargs = malloc(sizeof(struct btrfs_ioctl_space_args));
-+	if (!sargs)
-+		return -ENOMEM;
-+
-+	sargs->space_slots = 0;
-+	sargs->total_spaces = 0;
-+
-+	ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
-+	e = errno;
-+	if (ret) {
-+		fprintf(stderr, "ERROR: couldn't get space info on '%s' - %s\n",
-+			path, strerror(e));
-+		free(sargs);
-+		return ret;
-+	}
-+	if (!sargs->total_spaces)
-+		return 0;
-+
-+	count = sargs->total_spaces;
-+
-+	sargs = realloc(sargs, sizeof(struct btrfs_ioctl_space_args) +
-+			(count * sizeof(struct btrfs_ioctl_space_info)));
-+	if (!sargs)
-+		return -ENOMEM;
-+
-+	sargs->space_slots = count;
-+	sargs->total_spaces = 0;
-+
-+	ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
-+	e = errno;
-+	if (ret) {
-+		fprintf(stderr, "ERROR: couldn't get space info on '%s' - %s\n",
-+			path, strerror(e));
-+		close(fd);
-+		free(sargs);
-+		return ret;
-+	}
-+
-+	for (i = 0; i < sargs->total_spaces; i++) {
-+		char description[80];
-+		char *total_bytes;
-+		char *used_bytes;
-+		int written = 0;
-+		u64 flags = sargs->spaces[i].flags;
-+
-+		memset(description, 0, 80);
-+
-+		if (flags & BTRFS_BLOCK_GROUP_DATA) {
-+			if (flags & BTRFS_BLOCK_GROUP_METADATA) {
-+				snprintf(description, 14, "%s",
-+					 "Data+Metadata");
-+				written += 13;
-+			} else {
-+				snprintf(description, 5, "%s", "Data");
-+				written += 4;
-+			}
-+		} else if (flags & BTRFS_BLOCK_GROUP_SYSTEM) {
-+			snprintf(description, 7, "%s", "System");
-+			written += 6;
-+		} else if (flags & BTRFS_BLOCK_GROUP_METADATA) {
-+			snprintf(description, 9, "%s", "Metadata");
-+			written += 8;
-+		}
-+
-+		if (flags & BTRFS_BLOCK_GROUP_RAID0) {
-+			snprintf(description+written, 8, "%s", ", RAID0");
-+			written += 7;
-+		} else if (flags & BTRFS_BLOCK_GROUP_RAID1) {
-+			snprintf(description+written, 8, "%s", ", RAID1");
-+			written += 7;
-+		} else if (flags & BTRFS_BLOCK_GROUP_DUP) {
-+			snprintf(description+written, 6, "%s", ", DUP");
-+			written += 5;
-+		} else if (flags & BTRFS_BLOCK_GROUP_RAID10) {
-+			snprintf(description+written, 9, "%s", ", RAID10");
-+			written += 8;
-+		}
-+
-+		total_bytes = pretty_sizes(sargs->spaces[i].total_bytes);
-+		used_bytes = pretty_sizes(sargs->spaces[i].used_bytes);
-+		printf("%s: total=%s, used=%s\n", description, total_bytes,
-+		       used_bytes);
-+	}
-+	free(sargs);
-+
-+	return 0;
-+}
-+
-+static int uuid_search(struct btrfs_fs_devices *fs_devices, char *search)
-+{
-+	struct list_head *cur;
-+	struct btrfs_device *device;
-+
-+	list_for_each(cur, &fs_devices->devices) {
-+		device = list_entry(cur, struct btrfs_device, dev_list);
-+		if ((device->label && strcmp(device->label, search) == 0) ||
-+		    strcmp(device->name, search) == 0)
-+			return 1;
-+	}
-+	return 0;
-+}
-+
-+static void print_one_uuid(struct btrfs_fs_devices *fs_devices)
-+{
-+	char uuidbuf[37];
-+	struct list_head *cur;
-+	struct btrfs_device *device;
-+	char *super_bytes_used;
-+	u64 devs_found = 0;
-+	u64 total;
-+
-+	uuid_unparse(fs_devices->fsid, uuidbuf);
-+	device = list_entry(fs_devices->devices.next, struct btrfs_device,
-+			    dev_list);
-+	if (device->label && device->label[0])
-+		printf("Label: '%s' ", device->label);
-+	else
-+		printf("Label: none ");
-+
-+	super_bytes_used = pretty_sizes(device->super_bytes_used);
-+
-+	total = device->total_devs;
-+	printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
-+	       (unsigned long long)total, super_bytes_used);
-+
-+	free(super_bytes_used);
-+
-+	list_for_each(cur, &fs_devices->devices) {
-+		char *total_bytes;
-+		char *bytes_used;
-+		device = list_entry(cur, struct btrfs_device, dev_list);
-+		total_bytes = pretty_sizes(device->total_bytes);
-+		bytes_used = pretty_sizes(device->bytes_used);
-+		printf("\tdevid %4llu size %s used %s path %s\n",
-+		       (unsigned long long)device->devid,
-+		       total_bytes, bytes_used, device->name);
-+		free(total_bytes);
-+		free(bytes_used);
-+		devs_found++;
-+	}
-+	if (devs_found < total) {
-+		printf("\t*** Some devices missing\n");
-+	}
-+	printf("\n");
-+}
-+
-+static const char * const cmd_show_usage[] = {
-+	"btrfs filesystem show [--all-devices] [<uuid>|<label>]",
-+	"Show the structure of a filesystem",
-+	"If no argument is given, structure of all present filesystems is shown.",
-+	NULL
-+};
-+
-+static int cmd_show(int argc, char **argv)
-+{
-+	struct list_head *all_uuids;
-+	struct btrfs_fs_devices *fs_devices;
-+	struct list_head *cur_uuid;
-+	char *search = 0;
-+	int ret;
-+	int checklist = 1;
-+	int searchstart = 1;
-+
-+	if( argc > 1 && !strcmp(argv[1],"--all-devices")){
-+		checklist = 0;
-+		searchstart += 1;
-+	}
-+
-+	if (check_argc_max(argc, searchstart + 1))
-+		usage(cmd_show_usage);
-+
-+	if(checklist)
-+		ret = btrfs_scan_block_devices(0);
-+	else
-+		ret = btrfs_scan_one_dir("/dev", 0);
-+
-+	if (ret){
-+		fprintf(stderr, "ERROR: error %d while scanning\n", ret);
-+		return 18;
-+	}
-+	
-+	if(searchstart < argc)
-+		search = argv[searchstart];
-+
-+	all_uuids = btrfs_scanned_uuids();
-+	list_for_each(cur_uuid, all_uuids) {
-+		fs_devices = list_entry(cur_uuid, struct btrfs_fs_devices,
-+					list);
-+		if (search && uuid_search(fs_devices, search) == 0)
-+			continue;
-+		print_one_uuid(fs_devices);
-+	}
-+	printf("%s\n", BTRFS_BUILD_VERSION);
-+	return 0;
-+}
-+
-+static const char * const cmd_sync_usage[] = {
-+	"btrfs filesystem sync <path>",
-+	"Force a sync on a filesystem",
-+	NULL
-+};
-+
-+static int cmd_sync(int argc, char **argv)
-+{
-+	int 	fd, res, e;
-+	char	*path;
-+
-+	if (check_argc_exact(argc, 2))
-+		usage(cmd_sync_usage);
-+
-+	path = argv[1];
-+
-+	fd = open_file_or_dir(path);
-+	if (fd < 0) {
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-+		return 12;
-+	}
-+
-+	printf("FSSync '%s'\n", path);
-+	res = ioctl(fd, BTRFS_IOC_SYNC);
-+	e = errno;
-+	close(fd);
-+	if( res < 0 ){
-+		fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n", 
-+			path, strerror(e));
-+		return 16;
-+	}
-+
-+	return 0;
-+}
-+
-+static u64 parse_size(char *s)
-+{
-+	int len = strlen(s);
-+	char c;
-+	u64 mult = 1;
-+
-+	if (!isdigit(s[len - 1])) {
-+		c = tolower(s[len - 1]);
-+		switch (c) {
-+		case 'g':
-+			mult *= 1024;
-+		case 'm':
-+			mult *= 1024;
-+		case 'k':
-+			mult *= 1024;
-+		case 'b':
-+			break;
-+		default:
-+			fprintf(stderr, "Unknown size descriptor %c\n", c);
-+			exit(1);
-+		}
-+		s[len - 1] = '\0';
-+	}
-+	return atoll(s) * mult;
-+}
-+
-+static int parse_compress_type(char *s)
-+{
-+	if (strcmp(optarg, "zlib") == 0)
-+		return BTRFS_COMPRESS_ZLIB;
-+	else if (strcmp(optarg, "lzo") == 0)
-+		return BTRFS_COMPRESS_LZO;
-+	else {
-+		fprintf(stderr, "Unknown compress type %s\n", s);
-+		exit(1);
-+	};
-+}
-+
-+static const char * const cmd_defrag_usage[] = {
-+	"btrfs filesystem defragment [options] <file>|<dir> [<file>|<dir>...]",
-+	"Defragment a file or a directory",
-+	"",
-+	"-v             be verbose",
-+	"-c[zlib,lzo]   compress the file while defragmenting",
-+	"-f             flush data to disk immediately after defragmenting",
-+	"-s start       defragment only from byte onward",
-+	"-l len         defragment only up to len bytes",
-+	"-t size        minimal size of file to be considered for defragmenting",
-+	NULL
-+};
-+
-+static int cmd_defrag(int argc, char **argv)
-+{
-+	int fd;
-+	int flush = 0;
-+	u64 start = 0;
-+	u64 len = (u64)-1;
-+	u32 thresh = 0;
-+	int i;
-+	int errors = 0;
-+	int ret = 0;
-+	int verbose = 0;
-+	int fancy_ioctl = 0;
-+	struct btrfs_ioctl_defrag_range_args range;
-+	int e=0;
-+	int compress_type = BTRFS_COMPRESS_NONE;
-+
-+	optind = 1;
-+	while(1) {
-+		int c = getopt(argc, argv, "vc::fs:l:t:");
-+		if (c < 0)
-+			break;
-+
-+		switch(c) {
-+		case 'c':
-+			compress_type = BTRFS_COMPRESS_ZLIB;
-+			if (optarg)
-+				compress_type = parse_compress_type(optarg);
-+			fancy_ioctl = 1;
-+			break;
-+		case 'f':
-+			flush = 1;
-+			fancy_ioctl = 1;
-+			break;
-+		case 'v':
-+			verbose = 1;
-+			break;
-+		case 's':
-+			start = parse_size(optarg);
-+			fancy_ioctl = 1;
-+			break;
-+		case 'l':
-+			len = parse_size(optarg);
-+			fancy_ioctl = 1;
-+			break;
-+		case 't':
-+			thresh = parse_size(optarg);
-+			fancy_ioctl = 1;
-+			break;
-+		default:
-+			usage(cmd_defrag_usage);
-+		}
-+	}
-+
-+	if (check_argc_min(argc - optind, 1))
-+		usage(cmd_defrag_usage);
-+
-+	memset(&range, 0, sizeof(range));
-+	range.start = start;
-+	range.len = len;
-+	range.extent_thresh = thresh;
-+	if (compress_type) {
-+		range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS;
-+		range.compress_type = compress_type;
-+	}
-+	if (flush)
-+		range.flags |= BTRFS_DEFRAG_RANGE_START_IO;
-+
-+	for (i = optind; i < argc; i++) {
-+		if (verbose)
-+			printf("%s\n", argv[i]);
-+		fd = open_file_or_dir(argv[i]);
-+		if (fd < 0) {
-+			fprintf(stderr, "failed to open %s\n", argv[i]);
-+			perror("open:");
-+			errors++;
-+			continue;
-+		}
-+		if (!fancy_ioctl) {
-+			ret = ioctl(fd, BTRFS_IOC_DEFRAG, NULL);
-+			e=errno;
-+		} else {
-+			ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &range);
-+			if (ret && errno == ENOTTY) {
-+				fprintf(stderr, "ERROR: defrag range ioctl not "
-+					"supported in this kernel, please try "
-+					"without any options.\n");
-+				errors++;
-+				close(fd);
-+				break;
-+			}
-+		}
-+		if (ret) {
-+			fprintf(stderr, "ERROR: defrag failed on %s - %s\n",
-+				argv[i], strerror(e));
-+			errors++;
-+		}
-+		close(fd);
-+	}
-+	if (verbose)
-+		printf("%s\n", BTRFS_BUILD_VERSION);
-+	if (errors) {
-+		fprintf(stderr, "total %d failures\n", errors);
-+		exit(1);
-+	}
-+
-+	return errors + 20;
-+}
-+
-+static const char * const cmd_resize_usage[] = {
-+	"btrfs filesystem resize [+/-]<newsize>[gkm]|max <path>",
-+	"Resize a filesystem",
-+	"If 'max' is passed, the filesystem will occupy all available space",
-+	"on the device.",
-+	NULL
-+};
-+
-+static int cmd_resize(int argc, char **argv)
-+{
-+	struct btrfs_ioctl_vol_args	args;
-+	int	fd, res, len, e;
-+	char	*amount, *path;
-+
-+	if (check_argc_exact(argc, 3))
-+		usage(cmd_resize_usage);
-+
-+	amount = argv[1];
-+	path = argv[2];
-+
-+	fd = open_file_or_dir(path);
-+	if (fd < 0) {
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-+		return 12;
-+	}
-+	len = strlen(amount);
-+	if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
-+		fprintf(stderr, "ERROR: size value too long ('%s)\n",
-+			amount);
-+		return 14;
-+	}
-+
-+	printf("Resize '%s' of '%s'\n", path, amount);
-+	strncpy(args.name, amount, BTRFS_PATH_NAME_MAX);
-+	res = ioctl(fd, BTRFS_IOC_RESIZE, &args);
-+	e = errno;
-+	close(fd);
-+	if( res < 0 ){
-+		fprintf(stderr, "ERROR: unable to resize '%s' - %s\n", 
-+			path, strerror(e));
-+		return 30;
-+	}
-+	return 0;
-+}
-+
-+static const char * const cmd_label_usage[] = {
-+	"btrfs filesystem label <device> [<newlabel>]",
-+	"Get or change the label of an unmounted filesystem",
-+	"With one argument, get the label of filesystem on <device>.",
-+	"If <newlabel> is passed, set the filesystem label to <newlabel>.",
-+	NULL
-+};
-+
-+static int cmd_label(int argc, char **argv)
-+{
-+	if (check_argc_min(argc, 2) || check_argc_max(argc, 3))
-+		usage(cmd_label_usage);
-+
-+	if (argc > 2)
-+		return set_label(argv[1], argv[2]);
-+	else
-+		return get_label(argv[1]);
-+}
-+
-+const struct cmd_group filesystem_cmd_group = {
-+	filesystem_cmd_group_usage, NULL, {
-+		{ "df", cmd_df, cmd_df_usage, NULL, 0 },
-+		{ "show", cmd_show, cmd_show_usage, NULL, 0 },
-+		{ "sync", cmd_sync, cmd_sync_usage, NULL, 0 },
-+		{ "defragment", cmd_defrag, cmd_defrag_usage, NULL, 0 },
-+		{ "balance", cmd_balance, NULL, &balance_cmd_group, 1 },
-+		{ "resize", cmd_resize, cmd_resize_usage, NULL, 0 },
-+		{ "label", cmd_label, cmd_label_usage, NULL, 0 },
-+		{ 0, 0, 0, 0, 0 },
-+	}
-+};
-+
-+int cmd_filesystem(int argc, char **argv)
-+{
-+	return handle_command_group(&filesystem_cmd_group, argc, argv);
-+}
-diff --git a/cmds-inspect.c b/cmds-inspect.c
-new file mode 100644
-index 0000000..2f0228f
---- /dev/null
-+++ b/cmds-inspect.c
-@@ -0,0 +1,243 @@
-+/*
-+ * 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.
-+ */
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <sys/ioctl.h>
-+#include <errno.h>
-+
-+#include "kerncompat.h"
-+#include "ioctl.h"
-+
-+#include "commands.h"
-+
-+/* btrfs-list.c */
-+char *path_for_root(int fd, u64 root);
-+
-+static const char * const inspect_cmd_group_usage[] = {
-+	"btrfs inspect-internal <command> <args>",
-+	NULL
-+};
-+
-+static int __ino_to_path_fd(u64 inum, int fd, int verbose, const char *prepend)
-+{
-+	int ret;
-+	int i;
-+	struct btrfs_ioctl_ino_path_args ipa;
-+	struct btrfs_data_container *fspath;
-+
-+	fspath = malloc(4096);
-+	if (!fspath)
-+		return 1;
-+
-+	ipa.inum = inum;
-+	ipa.size = 4096;
-+	ipa.fspath = (u64)fspath;
-+
-+	ret = ioctl(fd, BTRFS_IOC_INO_PATHS, &ipa);
-+	if (ret) {
-+		printf("ioctl ret=%d, error: %s\n", ret, strerror(errno));
-+		goto out;
-+	}
-+
-+	if (verbose)
-+		printf("ioctl ret=%d, bytes_left=%lu, bytes_missing=%lu, "
-+			"cnt=%d, missed=%d\n", ret,
-+			(unsigned long)fspath->bytes_left,
-+			(unsigned long)fspath->bytes_missing,
-+			fspath->elem_cnt, fspath->elem_missed);
-+
-+	for (i = 0; i < fspath->elem_cnt; ++i) {
-+		char **str = (char **)fspath->val;
-+		str[i] += (unsigned long)fspath->val;
-+		if (prepend)
-+			printf("%s/%s\n", prepend, str[i]);
-+		else
-+			printf("%s\n", str[i]);
-+	}
-+
-+out:
-+	free(fspath);
-+	return ret;
-+}
-+
-+static const char * const cmd_inode_resolve_usage[] = {
-+	"btrfs inspect-internal inode-resolve [-v] <inode> <path>",
-+	"Get file system paths for the given inode",
-+	NULL
-+};
-+
-+static int cmd_inode_resolve(int argc, char **argv)
-+{
-+	int fd;
-+	int verbose = 0;
-+
-+	optind = 1;
-+	while (1) {
-+		int c = getopt(argc, argv, "v");
-+		if (c < 0)
-+			break;
-+
-+		switch (c) {
-+		case 'v':
-+			verbose = 1;
-+			break;
-+		default:
-+			usage(cmd_inode_resolve_usage);
-+		}
-+	}
-+
-+	if (check_argc_exact(argc - optind, 2))
-+		usage(cmd_inode_resolve_usage);
-+
-+	fd = open_file_or_dir(argv[optind+1]);
-+	if (fd < 0) {
-+		fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind+1]);
-+		return 12;
-+	}
-+
-+	return __ino_to_path_fd(atoll(argv[optind]), fd, verbose,
-+				argv[optind+1]);
-+}
-+
-+static const char * const cmd_logical_resolve_usage[] = {
-+	"btrfs inspect-internal logical-resolve [-Pv] <logical> <path>",
-+	"Get file system paths for the given logical address",
-+	NULL
-+};
-+
-+static int cmd_logical_resolve(int argc, char **argv)
-+{
-+	int ret;
-+	int fd;
-+	int i;
-+	int verbose = 0;
-+	int getpath = 1;
-+	int bytes_left;
-+	struct btrfs_ioctl_logical_ino_args loi;
-+	struct btrfs_data_container *inodes;
-+	char full_path[4096];
-+	char *path_ptr;
-+
-+	optind = 1;
-+	while (1) {
-+		int c = getopt(argc, argv, "Pv");
-+		if (c < 0)
-+			break;
-+
-+		switch (c) {
-+		case 'P':
-+			getpath = 0;
-+			break;
-+		case 'v':
-+			verbose = 1;
-+			break;
-+		default:
-+			usage(cmd_logical_resolve_usage);
-+		}
-+	}
-+
-+	if (check_argc_exact(argc - optind, 2))
-+		usage(cmd_logical_resolve_usage);
-+
-+	inodes = malloc(4096);
-+	if (!inodes)
-+		return 1;
-+
-+	loi.logical = atoll(argv[optind]);
-+	loi.size = 4096;
-+	loi.inodes = (u64)inodes;
-+
-+	fd = open_file_or_dir(argv[optind+1]);
-+	if (fd < 0) {
-+		fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind+1]);
-+		ret = 12;
-+		goto out;
-+	}
-+
-+	ret = ioctl(fd, BTRFS_IOC_LOGICAL_INO, &loi);
-+	if (ret) {
-+		printf("ioctl ret=%d, error: %s\n", ret, strerror(errno));
-+		goto out;
-+	}
-+
-+	if (verbose)
-+		printf("ioctl ret=%d, bytes_left=%lu, bytes_missing=%lu, "
-+			"cnt=%d, missed=%d\n", ret,
-+			(unsigned long)inodes->bytes_left,
-+			(unsigned long)inodes->bytes_missing,
-+			inodes->elem_cnt, inodes->elem_missed);
-+
-+	bytes_left = sizeof(full_path);
-+	ret = snprintf(full_path, bytes_left, "%s/", argv[optind+1]);
-+	path_ptr = full_path + ret;
-+	bytes_left -= ret + 1;
-+	BUG_ON(bytes_left < 0);
-+
-+	for (i = 0; i < inodes->elem_cnt; i += 3) {
-+		u64 inum = inodes->val[i];
-+		u64 offset = inodes->val[i+1];
-+		u64 root = inodes->val[i+2];
-+		int path_fd;
-+		char *name;
-+
-+		if (getpath) {
-+			name = path_for_root(fd, root);
-+			if (IS_ERR(name))
-+				return PTR_ERR(name);
-+			if (!name) {
-+				path_ptr[-1] = '\0';
-+				path_fd = fd;
-+			} else {
-+				path_ptr[-1] = '/';
-+				ret = snprintf(path_ptr, bytes_left, "%s",
-+						name);
-+				BUG_ON(ret >= bytes_left);
-+				free(name);
-+				path_fd = open_file_or_dir(full_path);
-+				if (path_fd < 0) {
-+					fprintf(stderr, "ERROR: can't access "
-+						"'%s'\n", full_path);
-+					goto out;
-+				}
-+			}
-+			__ino_to_path_fd(inum, path_fd, verbose, full_path);
-+		} else {
-+			printf("inode %llu offset %llu root %llu\n", inum,
-+				offset, root);
-+		}
-+	}
-+
-+out:
-+	free(inodes);
-+	return ret;
-+}
-+
-+const struct cmd_group inspect_cmd_group = {
-+	inspect_cmd_group_usage, NULL, {
-+		{ "inode-resolve", cmd_inode_resolve, cmd_inode_resolve_usage,
-+			NULL, 0 },
-+		{ "logical-resolve", cmd_logical_resolve,
-+			cmd_logical_resolve_usage, NULL, 0 },
-+		{ 0, 0, 0, 0, 0 }
-+	}
-+};
-+
-+int cmd_inspect(int argc, char **argv)
-+{
-+	return handle_command_group(&inspect_cmd_group, argc, argv);
-+}
-diff --git a/cmds-scrub.c b/cmds-scrub.c
-new file mode 100644
-index 0000000..c4503f4
---- /dev/null
-+++ b/cmds-scrub.c
-@@ -0,0 +1,1721 @@
-+/*
-+ * Copyright (C) 2011 STRATO.  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.
-+ */
-+
-+#include <sys/ioctl.h>
-+#include <sys/wait.h>
-+#include <sys/stat.h>
-+#include <sys/types.h>
-+#include <sys/socket.h>
-+#include <sys/un.h>
-+#include <poll.h>
-+#include <sys/file.h>
-+#include <uuid/uuid.h>
-+#include <fcntl.h>
-+#include <unistd.h>
-+#include <pthread.h>
-+#include <ctype.h>
-+#include <signal.h>
-+#include <stdarg.h>
-+
-+#include "ctree.h"
-+#include "ioctl.h"
-+#include "utils.h"
-+#include "volumes.h"
-+#include "disk-io.h"
-+
-+#include "commands.h"
-+
-+static const char * const scrub_cmd_group_usage[] = {
-+	"btrfs scrub <command> [options] <path>|<device>",
-+	NULL
-+};
-+
-+#define SCRUB_DATA_FILE "/var/lib/btrfs/scrub.status"
-+#define SCRUB_PROGRESS_SOCKET_PATH "/var/lib/btrfs/scrub.progress"
-+#define SCRUB_FILE_VERSION_PREFIX "scrub status"
-+#define SCRUB_FILE_VERSION "1"
-+
-+struct scrub_stats {
-+	time_t t_start;
-+	time_t t_resumed;
-+	u64 duration;
-+	u64 finished;
-+	u64 canceled;
-+};
-+
-+struct scrub_progress {
-+	struct btrfs_ioctl_scrub_args scrub_args;
-+	int fd;
-+	int ret;
-+	int skip;
-+	struct scrub_stats stats;
-+	struct scrub_file_record *resumed;
-+	int ioctl_errno;
-+	pthread_mutex_t progress_mutex;
-+};
-+
-+struct scrub_file_record {
-+	u8 fsid[BTRFS_FSID_SIZE];
-+	u64 devid;
-+	struct scrub_stats stats;
-+	struct btrfs_scrub_progress p;
-+};
-+
-+struct scrub_progress_cycle {
-+	int fdmnt;
-+	int prg_fd;
-+	int do_record;
-+	struct btrfs_ioctl_fs_info_args *fi;
-+	struct scrub_progress *progress;
-+	struct scrub_progress *shared_progress;
-+	pthread_mutex_t *write_mutex;
-+};
-+
-+struct scrub_fs_stat {
-+	struct btrfs_scrub_progress p;
-+	struct scrub_stats s;
-+	int i;
-+};
-+
-+static void print_scrub_full(struct btrfs_scrub_progress *sp)
-+{
-+	printf("\tdata_extents_scrubbed: %lld\n", sp->data_extents_scrubbed);
-+	printf("\ttree_extents_scrubbed: %lld\n", sp->tree_extents_scrubbed);
-+	printf("\tdata_bytes_scrubbed: %lld\n", sp->data_bytes_scrubbed);
-+	printf("\ttree_bytes_scrubbed: %lld\n", sp->tree_bytes_scrubbed);
-+	printf("\tread_errors: %lld\n", sp->read_errors);
-+	printf("\tcsum_errors: %lld\n", sp->csum_errors);
-+	printf("\tverify_errors: %lld\n", sp->verify_errors);
-+	printf("\tno_csum: %lld\n", sp->no_csum);
-+	printf("\tcsum_discards: %lld\n", sp->csum_discards);
-+	printf("\tsuper_errors: %lld\n", sp->super_errors);
-+	printf("\tmalloc_errors: %lld\n", sp->malloc_errors);
-+	printf("\tuncorrectable_errors: %lld\n", sp->uncorrectable_errors);
-+	printf("\tunverified_errors: %lld\n", sp->unverified_errors);
-+	printf("\tcorrected_errors: %lld\n", sp->corrected_errors);
-+	printf("\tlast_physical: %lld\n", sp->last_physical);
-+}
-+
-+#define ERR(test, ...) do {			\
-+	if (test)				\
-+		fprintf(stderr, __VA_ARGS__);	\
-+} while (0)
-+
-+#define PRINT_SCRUB_ERROR(test, desc) do {	\
-+	if (test)				\
-+		printf(" %s=%llu", desc, test);	\
-+} while (0)
-+
-+static void print_scrub_summary(struct btrfs_scrub_progress *p)
-+{
-+	u64 err_cnt;
-+	u64 err_cnt2;
-+	char *bytes;
-+
-+	err_cnt = p->read_errors +
-+			p->csum_errors +
-+			p->verify_errors +
-+			p->super_errors;
-+
-+	err_cnt2 = p->corrected_errors + p->uncorrectable_errors;
-+
-+	if (p->malloc_errors)
-+		printf("*** WARNING: memory allocation failed while scrubbing. "
-+		       "results may be inaccurate\n");
-+	bytes = pretty_sizes(p->data_bytes_scrubbed + p->tree_bytes_scrubbed);
-+	printf("\ttotal bytes scrubbed: %s with %llu errors\n", bytes,
-+		max(err_cnt, err_cnt2));
-+	free(bytes);
-+	if (err_cnt || err_cnt2) {
-+		printf("\terror details:");
-+		PRINT_SCRUB_ERROR(p->read_errors, "read");
-+		PRINT_SCRUB_ERROR(p->super_errors, "super");
-+		PRINT_SCRUB_ERROR(p->verify_errors, "verify");
-+		PRINT_SCRUB_ERROR(p->csum_errors, "csum");
-+		printf("\n");
-+		printf("\tcorrected errors: %llu, uncorrectable errors: %llu, "
-+			"unverified errors: %llu\n", p->corrected_errors,
-+			p->uncorrectable_errors, p->unverified_errors);
-+	}
-+}
-+
-+#define _SCRUB_FS_STAT(p, name, fs_stat) do {	\
-+	fs_stat->p.name += p->name;		\
-+} while (0)
-+
-+#define _SCRUB_FS_STAT_MIN(ss, name, fs_stat)	\
-+do {						\
-+	if (fs_stat->s.name > ss->name) {	\
-+		fs_stat->s.name = ss->name;	\
-+	}					\
-+} while (0)
-+
-+#define _SCRUB_FS_STAT_ZMIN(ss, name, fs_stat)			\
-+do {								\
-+	if (!fs_stat->s.name || fs_stat->s.name > ss->name) {	\
-+		fs_stat->s.name = ss->name;			\
-+	}							\
-+} while (0)
-+
-+#define _SCRUB_FS_STAT_ZMAX(ss, name, fs_stat)				\
-+do {									\
-+	if (!(fs_stat)->s.name || (fs_stat)->s.name < (ss)->name) {	\
-+		(fs_stat)->s.name = (ss)->name;				\
-+	}								\
-+} while (0)
-+
-+static void add_to_fs_stat(struct btrfs_scrub_progress *p,
-+				struct scrub_stats *ss,
-+				struct scrub_fs_stat *fs_stat)
-+{
-+	_SCRUB_FS_STAT(p, data_extents_scrubbed, fs_stat);
-+	_SCRUB_FS_STAT(p, tree_extents_scrubbed, fs_stat);
-+	_SCRUB_FS_STAT(p, data_bytes_scrubbed, fs_stat);
-+	_SCRUB_FS_STAT(p, tree_bytes_scrubbed, fs_stat);
-+	_SCRUB_FS_STAT(p, read_errors, fs_stat);
-+	_SCRUB_FS_STAT(p, csum_errors, fs_stat);
-+	_SCRUB_FS_STAT(p, verify_errors, fs_stat);
-+	_SCRUB_FS_STAT(p, no_csum, fs_stat);
-+	_SCRUB_FS_STAT(p, csum_discards, fs_stat);
-+	_SCRUB_FS_STAT(p, super_errors, fs_stat);
-+	_SCRUB_FS_STAT(p, malloc_errors, fs_stat);
-+	_SCRUB_FS_STAT(p, uncorrectable_errors, fs_stat);
-+	_SCRUB_FS_STAT(p, corrected_errors, fs_stat);
-+	_SCRUB_FS_STAT(p, last_physical, fs_stat);
-+	_SCRUB_FS_STAT_ZMIN(ss, t_start, fs_stat);
-+	_SCRUB_FS_STAT_ZMIN(ss, t_resumed, fs_stat);
-+	_SCRUB_FS_STAT_ZMAX(ss, duration, fs_stat);
-+	_SCRUB_FS_STAT_ZMAX(ss, canceled, fs_stat);
-+	_SCRUB_FS_STAT_MIN(ss, finished, fs_stat);
-+}
-+
-+static void init_fs_stat(struct scrub_fs_stat *fs_stat)
-+{
-+	memset(fs_stat, 0, sizeof(*fs_stat));
-+	fs_stat->s.finished = 1;
-+}
-+
-+static void _print_scrub_ss(struct scrub_stats *ss)
-+{
-+	char t[4096];
-+	struct tm tm;
-+
-+	if (!ss || !ss->t_start) {
-+		printf("\tno stats available\n");
-+		return;
-+	}
-+	if (ss->t_resumed) {
-+		localtime_r(&ss->t_resumed, &tm);
-+		strftime(t, sizeof(t), "%c", &tm);
-+		t[sizeof(t) - 1] = '\0';
-+		printf("\tscrub resumed at %s", t);
-+	} else {
-+		localtime_r(&ss->t_start, &tm);
-+		strftime(t, sizeof(t), "%c", &tm);
-+		t[sizeof(t) - 1] = '\0';
-+		printf("\tscrub started at %s", t);
-+	}
-+	if (ss->finished && !ss->canceled) {
-+		printf(" and finished after %llu seconds\n",
-+		       ss->duration);
-+	} else if (ss->canceled) {
-+		printf(" and was aborted after %llu seconds\n",
-+		       ss->duration);
-+	} else {
-+		printf(", running for %llu seconds\n", ss->duration);
-+	}
-+}
-+
-+static void print_scrub_dev(struct btrfs_ioctl_dev_info_args *di,
-+				struct btrfs_scrub_progress *p, int raw,
-+				const char *append, struct scrub_stats *ss)
-+{
-+	printf("scrub device %s (id %llu) %s\n", di->path, di->devid,
-+	       append ? append : "");
-+
-+	_print_scrub_ss(ss);
-+
-+	if (p) {
-+		if (raw)
-+			print_scrub_full(p);
-+		else
-+			print_scrub_summary(p);
-+	}
-+}
-+
-+static void print_fs_stat(struct scrub_fs_stat *fs_stat, int raw)
-+{
-+	_print_scrub_ss(&fs_stat->s);
-+
-+	if (raw)
-+		print_scrub_full(&fs_stat->p);
-+	else
-+		print_scrub_summary(&fs_stat->p);
-+}
-+
-+static void free_history(struct scrub_file_record **last_scrubs)
-+{
-+	struct scrub_file_record **l = last_scrubs;
-+	if (!l)
-+		return;
-+	while (*l)
-+		free(*l++);
-+	free(last_scrubs);
-+}
-+
-+/*
-+ * cancels a running scrub and makes the master process record the current
-+ * progress status before exiting.
-+ */
-+static int cancel_fd = -1;
-+static void scrub_sigint_record_progress(int signal)
-+{
-+	ioctl(cancel_fd, BTRFS_IOC_SCRUB_CANCEL, NULL);
-+}
-+
-+static int scrub_handle_sigint_parent(void)
-+{
-+	struct sigaction sa = {
-+		.sa_handler = SIG_IGN,
-+		.sa_flags = SA_RESTART,
-+	};
-+
-+	return sigaction(SIGINT, &sa, NULL);
-+}
-+
-+static int scrub_handle_sigint_child(int fd)
-+{
-+	struct sigaction sa = {
-+		.sa_handler = fd == -1 ? SIG_DFL : scrub_sigint_record_progress,
-+	};
-+
-+	cancel_fd = fd;
-+	return sigaction(SIGINT, &sa, NULL);
-+}
-+
-+static int scrub_datafile(const char *fn_base, const char *fn_local,
-+				const char *fn_tmp, char *datafile, int size)
-+{
-+	int ret;
-+	int end = size - 2;
-+
-+	datafile[end + 1] = '\0';
-+	strncpy(datafile, fn_base, end);
-+	ret = strlen(datafile);
-+
-+	if (ret + 1 > end)
-+		return -EOVERFLOW;
-+
-+	datafile[ret] = '.';
-+	strncpy(datafile + ret + 1, fn_local, end - ret - 1);
-+	ret = strlen(datafile);
-+
-+	if (ret + 1 > end)
-+		return -EOVERFLOW;
-+
-+	if (fn_tmp) {
-+		datafile[ret] = '_';
-+		strncpy(datafile + ret + 1, fn_tmp, end - ret - 1);
-+		ret = strlen(datafile);
-+
-+		if (ret > end)
-+			return -EOVERFLOW;
-+	}
-+
-+	return 0;
-+}
-+
-+static int scrub_open_file(const char *datafile, int m)
-+{
-+	int fd;
-+	int ret;
-+
-+	fd = open(datafile, m, 0600);
-+	if (fd < 0)
-+		return -errno;
-+
-+	ret = flock(fd, LOCK_EX|LOCK_NB);
-+	if (ret) {
-+		ret = errno;
-+		close(fd);
-+		return -ret;
-+	}
-+
-+	return fd;
-+}
-+
-+static int scrub_open_file_r(const char *fn_base, const char *fn_local)
-+{
-+	int ret;
-+	char datafile[BTRFS_PATH_NAME_MAX + 1];
-+	ret = scrub_datafile(fn_base, fn_local, NULL,
-+				datafile, sizeof(datafile));
-+	if (ret < 0)
-+		return ret;
-+	return scrub_open_file(datafile, O_RDONLY);
-+}
-+
-+static int scrub_open_file_w(const char *fn_base, const char *fn_local,
-+				const char *tmp)
-+{
-+	int ret;
-+	char datafile[BTRFS_PATH_NAME_MAX + 1];
-+	ret = scrub_datafile(fn_base, fn_local, tmp,
-+				datafile, sizeof(datafile));
-+	if (ret < 0)
-+		return ret;
-+	return scrub_open_file(datafile, O_WRONLY|O_CREAT);
-+}
-+
-+static int scrub_rename_file(const char *fn_base, const char *fn_local,
-+				const char *tmp)
-+{
-+	int ret;
-+	char datafile_old[BTRFS_PATH_NAME_MAX + 1];
-+	char datafile_new[BTRFS_PATH_NAME_MAX + 1];
-+	ret = scrub_datafile(fn_base, fn_local, tmp,
-+				datafile_old, sizeof(datafile_old));
-+	if (ret < 0)
-+		return ret;
-+	ret = scrub_datafile(fn_base, fn_local, NULL,
-+				datafile_new, sizeof(datafile_new));
-+	if (ret < 0)
-+		return ret;
-+	ret = rename(datafile_old, datafile_new);
-+	return ret ? -errno : 0;
-+}
-+
-+#define _SCRUB_KVREAD(ret, i, name, avail, l, dest) if (ret == 0) {	  \
-+	ret = scrub_kvread(i, sizeof(#name), avail, l, #name, dest.name); \
-+}
-+
-+/*
-+ * returns 0 if the key did not match (nothing was read)
-+ *         1 if the key did match (success)
-+ *        -1 if the key did match and an error occured
-+ */
-+static int scrub_kvread(int *i, int len, int avail, const char *buf,
-+			const char *key, u64 *dest)
-+{
-+	int j;
-+
-+	if (*i + len + 1 < avail && strncmp(&buf[*i], key, len - 1) == 0) {
-+		*i += len - 1;
-+		if (buf[*i] != ':')
-+			return -1;
-+		*i += 1;
-+		for (j = 0; isdigit(buf[*i + j]) && *i + j < avail; ++j)
-+			;
-+		if (*i + j >= avail)
-+			return -1;
-+		*dest = atoll(&buf[*i]);
-+		*i += j;
-+		return 1;
-+	}
-+
-+	return 0;
-+}
-+
-+#define _SCRUB_INVALID do {						\
-+	if (report_errors)						\
-+		fprintf(stderr, "WARNING: invalid data in line %d pos "	\
-+			"%d state %d (near \"%.*s\") at %s:%d\n",	\
-+			lineno, i, state, 20 > avail ? avail : 20,	\
-+			l + i,	__FILE__, __LINE__);			\
-+	goto skip;							\
-+} while (0)
-+
-+static struct scrub_file_record **scrub_read_file(int fd, int report_errors)
-+{
-+	int avail = 0;
-+	int old_avail = 0;
-+	char l[16 * 1024];
-+	int state = 0;
-+	int curr = -1;
-+	int i = 0;
-+	int j;
-+	int ret;
-+	int eof = 0;
-+	int lineno = 0;
-+	u64 version;
-+	char empty_uuid[BTRFS_FSID_SIZE] = {0};
-+	struct scrub_file_record **p = NULL;
-+
-+	if (fd < 0)
-+		return ERR_PTR(-EINVAL);
-+
-+again:
-+	old_avail = avail - i;
-+	BUG_ON(old_avail < 0);
-+	if (old_avail)
-+		memmove(l, l + i, old_avail);
-+	avail = read(fd, l + old_avail, sizeof(l) - old_avail);
-+	if (avail == 0)
-+		eof = 1;
-+	if (avail == 0 && old_avail == 0) {
-+		if (curr >= 0 &&
-+		    memcmp(p[curr]->fsid, empty_uuid, BTRFS_FSID_SIZE) == 0) {
-+			p[curr] = NULL;
-+		} else if (curr == -1) {
-+			p = ERR_PTR(-ENODATA);
-+		}
-+		return p;
-+	}
-+	if (avail == -1)
-+		return ERR_PTR(-errno);
-+	avail += old_avail;
-+
-+	i = 0;
-+	while (i < avail) {
-+		switch (state) {
-+		case 0: /* start of file */
-+			ret = scrub_kvread(&i,
-+				sizeof(SCRUB_FILE_VERSION_PREFIX), avail, l,
-+				SCRUB_FILE_VERSION_PREFIX, &version);
-+			if (ret != 1)
-+				_SCRUB_INVALID;
-+			if (version != atoll(SCRUB_FILE_VERSION))
-+				return ERR_PTR(-ENOTSUP);
-+			state = 6;
-+			continue;
-+		case 1: /* start of line, alloc */
-+			/*
-+			 * this state makes sure we have a complete line in
-+			 * further processing, so we don't need wrap-tracking
-+			 * everywhere.
-+			 */
-+			if (!eof && !memchr(l + i, '\n', avail - i))
-+				goto again;
-+			++lineno;
-+			if (curr > -1 && memcmp(p[curr]->fsid, empty_uuid,
-+						BTRFS_FSID_SIZE) == 0) {
-+				state = 2;
-+				continue;
-+			}
-+			++curr;
-+			p = realloc(p, (curr + 2) * sizeof(*p));
-+			if (p)
-+				p[curr] = malloc(sizeof(**p));
-+			if (!p || !p[curr])
-+				return ERR_PTR(-errno);
-+			memset(p[curr], 0, sizeof(**p));
-+			p[curr + 1] = NULL;
-+			++state;
-+			/* fall through */
-+		case 2: /* start of line, skip space */
-+			while (isspace(l[i]) && i < avail) {
-+				if (l[i] == '\n')
-+					++lineno;
-+				++i;
-+			}
-+			if (i >= avail ||
-+			    (!eof && !memchr(l + i, '\n', avail - i)))
-+				goto again;
-+			++state;
-+			/* fall through */
-+		case 3: /* read fsid */
-+			if (i == avail)
-+				continue;
-+			for (j = 0; l[i + j] != ':' && i + j < avail; ++j)
-+				;
-+			if (i + j + 1 >= avail)
-+				_SCRUB_INVALID;
-+			if (j != 36)
-+				_SCRUB_INVALID;
-+			l[i + j] = '\0';
-+			ret = uuid_parse(l + i, p[curr]->fsid);
-+			if (ret)
-+				_SCRUB_INVALID;
-+			i += j + 1;
-+			++state;
-+			/* fall through */
-+		case 4: /* read dev id */
-+			for (j = 0; isdigit(l[i + j]) && i+j < avail; ++j)
-+				;
-+			if (j == 0 || i + j + 1 >= avail)
-+				_SCRUB_INVALID;
-+			p[curr]->devid = atoll(&l[i]);
-+			i += j + 1;
-+			++state;
-+			/* fall through */
-+		case 5: /* read key/value pair */
-+			ret = 0;
-+			_SCRUB_KVREAD(ret, &i, data_extents_scrubbed, avail, l,
-+					&p[curr]->p);
-+			_SCRUB_KVREAD(ret, &i, data_extents_scrubbed, avail, l,
-+					&p[curr]->p);
-+			_SCRUB_KVREAD(ret, &i, tree_extents_scrubbed, avail, l,
-+					&p[curr]->p);
-+			_SCRUB_KVREAD(ret, &i, data_bytes_scrubbed, avail, l,
-+					&p[curr]->p);
-+			_SCRUB_KVREAD(ret, &i, tree_bytes_scrubbed, avail, l,
-+					&p[curr]->p);
-+			_SCRUB_KVREAD(ret, &i, read_errors, avail, l,
-+					&p[curr]->p);
-+			_SCRUB_KVREAD(ret, &i, csum_errors, avail, l,
-+					&p[curr]->p);
-+			_SCRUB_KVREAD(ret, &i, verify_errors, avail, l,
-+					&p[curr]->p);
-+			_SCRUB_KVREAD(ret, &i, no_csum, avail, l,
-+					&p[curr]->p);
-+			_SCRUB_KVREAD(ret, &i, csum_discards, avail, l,
-+					&p[curr]->p);
-+			_SCRUB_KVREAD(ret, &i, super_errors, avail, l,
-+					&p[curr]->p);
-+			_SCRUB_KVREAD(ret, &i, malloc_errors, avail, l,
-+					&p[curr]->p);
-+			_SCRUB_KVREAD(ret, &i, uncorrectable_errors, avail, l,
-+					&p[curr]->p);
-+			_SCRUB_KVREAD(ret, &i, corrected_errors, avail, l,
-+					&p[curr]->p);
-+			_SCRUB_KVREAD(ret, &i, last_physical, avail, l,
-+					&p[curr]->p);
-+			_SCRUB_KVREAD(ret, &i, finished, avail, l,
-+					&p[curr]->stats);
-+			_SCRUB_KVREAD(ret, &i, t_start, avail, l,
-+					(u64 *)&p[curr]->stats);
-+			_SCRUB_KVREAD(ret, &i, t_resumed, avail, l,
-+					(u64 *)&p[curr]->stats);
-+			_SCRUB_KVREAD(ret, &i, duration, avail, l,
-+					(u64 *)&p[curr]->stats);
-+			_SCRUB_KVREAD(ret, &i, canceled, avail, l,
-+					&p[curr]->stats);
-+			if (ret != 1)
-+				_SCRUB_INVALID;
-+			++state;
-+			/* fall through */
-+		case 6: /* after number */
-+			if (l[i] == '|')
-+				state = 5;
-+			else if (l[i] == '\n')
-+				state = 1;
-+			else
-+				_SCRUB_INVALID;
-+			++i;
-+			continue;
-+		case 99: /* skip rest of line */
-+skip:
-+			state = 99;
-+			do {
-+				++i;
-+				if (l[i - 1] == '\n') {
-+					state = 1;
-+					break;
-+				}
-+			} while (i < avail);
-+			continue;
-+		}
-+		BUG();
-+	}
-+	goto again;
-+}
-+
-+static int scrub_write_buf(int fd, const void *data, int len)
-+{
-+	int ret;
-+	ret = write(fd, data, len);
-+	return ret - len;
-+}
-+
-+static int scrub_writev(int fd, char *buf, int max, const char *fmt, ...)
-+				__attribute__ ((format (printf, 4, 5)));
-+static int scrub_writev(int fd, char *buf, int max, const char *fmt, ...)
-+{
-+	int ret;
-+	va_list args;
-+
-+	va_start(args, fmt);
-+	ret = vsnprintf(buf, max, fmt, args);
-+	va_end(args);
-+	if (ret >= max)
-+		return ret - max;
-+	return scrub_write_buf(fd, buf, ret);
-+}
-+
-+#define _SCRUB_SUM(dest, data, name) dest->scrub_args.progress.name =	\
-+			data->resumed->p.name + data->scrub_args.progress.name
-+
-+static struct scrub_progress *scrub_resumed_stats(struct scrub_progress *data,
-+						  struct scrub_progress *dest)
-+{
-+	if (!data->resumed || data->skip)
-+		return data;
-+
-+	_SCRUB_SUM(dest, data, data_extents_scrubbed);
-+	_SCRUB_SUM(dest, data, tree_extents_scrubbed);
-+	_SCRUB_SUM(dest, data, data_bytes_scrubbed);
-+	_SCRUB_SUM(dest, data, tree_bytes_scrubbed);
-+	_SCRUB_SUM(dest, data, read_errors);
-+	_SCRUB_SUM(dest, data, csum_errors);
-+	_SCRUB_SUM(dest, data, verify_errors);
-+	_SCRUB_SUM(dest, data, no_csum);
-+	_SCRUB_SUM(dest, data, csum_discards);
-+	_SCRUB_SUM(dest, data, super_errors);
-+	_SCRUB_SUM(dest, data, malloc_errors);
-+	_SCRUB_SUM(dest, data, uncorrectable_errors);
-+	_SCRUB_SUM(dest, data, corrected_errors);
-+	_SCRUB_SUM(dest, data, last_physical);
-+	dest->stats.canceled = data->stats.canceled;
-+	dest->stats.finished = data->stats.finished;
-+	dest->stats.t_resumed = data->stats.t_start;
-+	dest->stats.t_start = data->resumed->stats.t_start;
-+	dest->stats.duration = data->resumed->stats.duration +
-+							data->stats.duration;
-+	dest->scrub_args.devid = data->scrub_args.devid;
-+	return dest;
-+}
-+
-+#define _SCRUB_KVWRITE(fd, buf, name, use)		\
-+	scrub_kvwrite(fd, buf, sizeof(buf), #name,	\
-+			use->scrub_args.progress.name)
-+
-+#define _SCRUB_KVWRITE_STATS(fd, buf, name, use)	\
-+	scrub_kvwrite(fd, buf, sizeof(buf), #name,	\
-+			use->stats.name)
-+
-+static int scrub_kvwrite(int fd, char *buf, int max, const char *key, u64 val)
-+{
-+	return scrub_writev(fd, buf, max, "|%s:%lld", key, val);
-+}
-+
-+static int scrub_write_file(int fd, const char *fsid,
-+				struct scrub_progress *data, int n)
-+{
-+	int ret = 0;
-+	int i;
-+	char buf[1024];
-+	struct scrub_progress local;
-+	struct scrub_progress *use;
-+
-+	if (n < 1)
-+		return -EINVAL;
-+
-+	/* each -1 is to subtract one \0 byte, the + 2 is for ':' and '\n' */
-+	ret = scrub_write_buf(fd, SCRUB_FILE_VERSION_PREFIX ":"
-+				SCRUB_FILE_VERSION "\n",
-+				(sizeof(SCRUB_FILE_VERSION_PREFIX) - 1) +
-+				(sizeof(SCRUB_FILE_VERSION) - 1) + 2);
-+	if (ret)
-+		return -EOVERFLOW;
-+
-+	for (i = 0; i < n; ++i) {
-+		use = scrub_resumed_stats(&data[i], &local);
-+		if (scrub_write_buf(fd, fsid, strlen(fsid)) ||
-+		    scrub_write_buf(fd, ":", 1) ||
-+		    scrub_writev(fd, buf, sizeof(buf), "%lld",
-+					use->scrub_args.devid) ||
-+		    scrub_write_buf(fd, buf, ret) ||
-+		    _SCRUB_KVWRITE(fd, buf, data_extents_scrubbed, use) ||
-+		    _SCRUB_KVWRITE(fd, buf, tree_extents_scrubbed, use) ||
-+		    _SCRUB_KVWRITE(fd, buf, data_bytes_scrubbed, use) ||
-+		    _SCRUB_KVWRITE(fd, buf, tree_bytes_scrubbed, use) ||
-+		    _SCRUB_KVWRITE(fd, buf, read_errors, use) ||
-+		    _SCRUB_KVWRITE(fd, buf, csum_errors, use) ||
-+		    _SCRUB_KVWRITE(fd, buf, verify_errors, use) ||
-+		    _SCRUB_KVWRITE(fd, buf, no_csum, use) ||
-+		    _SCRUB_KVWRITE(fd, buf, csum_discards, use) ||
-+		    _SCRUB_KVWRITE(fd, buf, super_errors, use) ||
-+		    _SCRUB_KVWRITE(fd, buf, malloc_errors, use) ||
-+		    _SCRUB_KVWRITE(fd, buf, uncorrectable_errors, use) ||
-+		    _SCRUB_KVWRITE(fd, buf, corrected_errors, use) ||
-+		    _SCRUB_KVWRITE(fd, buf, last_physical, use) ||
-+		    _SCRUB_KVWRITE_STATS(fd, buf, t_start, use) ||
-+		    _SCRUB_KVWRITE_STATS(fd, buf, t_resumed, use) ||
-+		    _SCRUB_KVWRITE_STATS(fd, buf, duration, use) ||
-+		    _SCRUB_KVWRITE_STATS(fd, buf, canceled, use) ||
-+		    _SCRUB_KVWRITE_STATS(fd, buf, finished, use) ||
-+		    scrub_write_buf(fd, "\n", 1)) {
-+			return -EOVERFLOW;
-+		}
-+	}
-+
-+	return 0;
-+}
-+
-+static int scrub_write_progress(pthread_mutex_t *m, const char *fsid,
-+				struct scrub_progress *data, int n)
-+{
-+	int ret;
-+	int err;
-+	int fd = 0;
-+	int old;
-+
-+	ret = pthread_mutex_lock(m);
-+	if (ret) {
-+		err = -errno;
-+		goto out;
-+	}
-+
-+	ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old);
-+	if (ret) {
-+		err = -ret;
-+		goto out;
-+	}
-+
-+	fd = scrub_open_file_w(SCRUB_DATA_FILE, fsid, "tmp");
-+	if (fd < 0) {
-+		err = fd;
-+		goto out;
-+	}
-+	err = scrub_write_file(fd, fsid, data, n);
-+	if (err)
-+		goto out;
-+	err = scrub_rename_file(SCRUB_DATA_FILE, fsid, "tmp");
-+	if (err)
-+		goto out;
-+
-+out:
-+	if (fd > 0) {
-+		ret = close(fd);
-+		if (ret)
-+			err = -errno;
-+	}
-+
-+	ret = pthread_mutex_unlock(m);
-+	if (ret && !err)
-+		err = -ret;
-+
-+	ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old);
-+	if (ret && !err)
-+		err = -ret;
-+
-+	return err;
-+}
-+
-+static void *scrub_one_dev(void *ctx)
-+{
-+	struct scrub_progress *sp = ctx;
-+	int ret;
-+	struct timeval tv;
-+
-+	sp->stats.canceled = 0;
-+	sp->stats.duration = 0;
-+	sp->stats.finished = 0;
-+
-+	ret = ioctl(sp->fd, BTRFS_IOC_SCRUB, &sp->scrub_args);
-+	gettimeofday(&tv, NULL);
-+	sp->ret = ret;
-+	sp->stats.duration = tv.tv_sec - sp->stats.t_start;
-+	sp->stats.canceled = !!ret;
-+	sp->ioctl_errno = errno;
-+	ret = pthread_mutex_lock(&sp->progress_mutex);
-+	if (ret)
-+		return ERR_PTR(-ret);
-+	sp->stats.finished = 1;
-+	ret = pthread_mutex_unlock(&sp->progress_mutex);
-+	if (ret)
-+		return ERR_PTR(-ret);
-+
-+	return NULL;
-+}
-+
-+static void *progress_one_dev(void *ctx)
-+{
-+	struct scrub_progress *sp = ctx;
-+
-+	sp->ret = ioctl(sp->fd, BTRFS_IOC_SCRUB_PROGRESS, &sp->scrub_args);
-+	sp->ioctl_errno = errno;
-+
-+	return NULL;
-+}
-+
-+static void *scrub_progress_cycle(void *ctx)
-+{
-+	int ret;
-+	int old;
-+	int i;
-+	char fsid[37];
-+	struct scrub_progress *sp;
-+	struct scrub_progress *sp_last;
-+	struct scrub_progress *sp_shared;
-+	struct timeval tv;
-+	struct scrub_progress_cycle *spc = ctx;
-+	int ndev = spc->fi->num_devices;
-+	int this = 1;
-+	int last = 0;
-+	int peer_fd = -1;
-+	struct pollfd accept_poll_fd = {
-+		.fd = spc->prg_fd,
-+		.events = POLLIN,
-+		.revents = 0,
-+	};
-+	struct pollfd write_poll_fd = {
-+		.events = POLLOUT,
-+		.revents = 0,
-+	};
-+	struct sockaddr_un peer;
-+	socklen_t peer_size = sizeof(peer);
-+
-+	ret = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old);
-+	if (ret)
-+		return ERR_PTR(-ret);
-+
-+	uuid_unparse(spc->fi->fsid, fsid);
-+
-+	for (i = 0; i < ndev; ++i) {
-+		sp = &spc->progress[i];
-+		sp_last = &spc->progress[i + ndev];
-+		sp_shared = &spc->shared_progress[i];
-+		sp->scrub_args.devid = sp_last->scrub_args.devid =
-+						sp_shared->scrub_args.devid;
-+		sp->fd = sp_last->fd = spc->fdmnt;
-+		sp->stats.t_start = sp_last->stats.t_start =
-+						sp_shared->stats.t_start;
-+		sp->resumed = sp_last->resumed = sp_shared->resumed;
-+		sp->skip = sp_last->skip = sp_shared->skip;
-+		sp->stats.finished = sp_last->stats.finished =
-+						sp_shared->stats.finished;
-+	}
-+
-+	while (1) {
-+		ret = poll(&accept_poll_fd, 1, 5 * 1000);
-+		if (ret == -1)
-+			return ERR_PTR(-errno);
-+		if (ret)
-+			peer_fd = accept(spc->prg_fd, (struct sockaddr *)&peer,
-+					 &peer_size);
-+		gettimeofday(&tv, NULL);
-+		this = (this + 1)%2;
-+		last = (last + 1)%2;
-+		for (i = 0; i < ndev; ++i) {
-+			sp = &spc->progress[this * ndev + i];
-+			sp_last = &spc->progress[last * ndev + i];
-+			sp_shared = &spc->shared_progress[i];
-+			if (sp->stats.finished)
-+				continue;
-+			progress_one_dev(sp);
-+			sp->stats.duration = tv.tv_sec - sp->stats.t_start;
-+			if (!sp->ret)
-+				continue;
-+			if (sp->ioctl_errno != ENOTCONN &&
-+			    sp->ioctl_errno != ENODEV)
-+				return ERR_PTR(-sp->ioctl_errno);
-+			/*
-+			 * scrub finished or device removed, check the
-+			 * finished flag. if unset, just use the last
-+			 * result we got for the current write and go
-+			 * on. flag should be set on next cycle, then.
-+			 */
-+			ret = pthread_mutex_lock(&sp_shared->progress_mutex);
-+			if (ret)
-+				return ERR_PTR(-ret);
-+			if (!sp_shared->stats.finished) {
-+				ret = pthread_mutex_unlock(
-+						&sp_shared->progress_mutex);
-+				if (ret)
-+					return ERR_PTR(-ret);
-+				memcpy(sp, sp_last, sizeof(*sp));
-+				continue;
-+			}
-+			ret = pthread_mutex_unlock(&sp_shared->progress_mutex);
-+			if (ret)
-+				return ERR_PTR(-ret);
-+			memcpy(sp, sp_shared, sizeof(*sp));
-+			memcpy(sp_last, sp_shared, sizeof(*sp));
-+		}
-+		if (peer_fd != -1) {
-+			write_poll_fd.fd = peer_fd;
-+			ret = poll(&write_poll_fd, 1, 0);
-+			if (ret == -1)
-+				return ERR_PTR(-errno);
-+			if (ret) {
-+				ret = scrub_write_file(
-+					peer_fd, fsid,
-+					&spc->progress[this * ndev], ndev);
-+				if (ret)
-+					return ERR_PTR(ret);
-+			}
-+			close(peer_fd);
-+			peer_fd = -1;
-+		}
-+		if (!spc->do_record)
-+			continue;
-+		ret = scrub_write_progress(spc->write_mutex, fsid,
-+					   &spc->progress[this * ndev], ndev);
-+		if (ret)
-+			return ERR_PTR(ret);
-+	}
-+}
-+
-+static struct scrub_file_record *last_dev_scrub(
-+		struct scrub_file_record *const *const past_scrubs, u64 devid)
-+{
-+	int i;
-+
-+	if (!past_scrubs || IS_ERR(past_scrubs))
-+		return NULL;
-+
-+	for (i = 0; past_scrubs[i]; ++i)
-+		if (past_scrubs[i]->devid == devid)
-+			return past_scrubs[i];
-+
-+	return NULL;
-+}
-+
-+static int scrub_device_info(int fd, u64 devid,
-+			     struct btrfs_ioctl_dev_info_args *di_args)
-+{
-+	int ret;
-+
-+	di_args->devid = devid;
-+	memset(&di_args->uuid, '\0', sizeof(di_args->uuid));
-+
-+	ret = ioctl(fd, BTRFS_IOC_DEV_INFO, di_args);
-+	return ret ? -errno : 0;
-+}
-+
-+static int scrub_fs_info(int fd, char *path,
-+				struct btrfs_ioctl_fs_info_args *fi_args,
-+				struct btrfs_ioctl_dev_info_args **di_ret)
-+{
-+	int ret = 0;
-+	int ndevs = 0;
-+	int i = 1;
-+	struct btrfs_fs_devices *fs_devices_mnt = NULL;
-+	struct btrfs_ioctl_dev_info_args *di_args;
-+	char mp[BTRFS_PATH_NAME_MAX + 1];
-+
-+	memset(fi_args, 0, sizeof(*fi_args));
-+
-+	ret = ioctl(fd, BTRFS_IOC_FS_INFO, fi_args);
-+	if (ret && errno == EINVAL) {
-+		/* path is no mounted btrfs. try if it's a device */
-+		ret = check_mounted_where(fd, path, mp, sizeof(mp),
-+						&fs_devices_mnt);
-+		if (!ret)
-+			return -EINVAL;
-+		if (ret < 0)
-+			return ret;
-+		fi_args->num_devices = 1;
-+		fi_args->max_id = fs_devices_mnt->latest_devid;
-+		i = fs_devices_mnt->latest_devid;
-+		memcpy(fi_args->fsid, fs_devices_mnt->fsid, BTRFS_FSID_SIZE);
-+		close(fd);
-+		fd = open_file_or_dir(mp);
-+		if (fd < 0)
-+			return -errno;
-+	} else if (ret) {
-+		return -errno;
-+	}
-+
-+	if (!fi_args->num_devices)
-+		return 0;
-+
-+	di_args = *di_ret = malloc(fi_args->num_devices * sizeof(*di_args));
-+	if (!di_args)
-+		return -errno;
-+
-+	for (; i <= fi_args->max_id; ++i) {
-+		BUG_ON(ndevs >= fi_args->num_devices);
-+		ret = scrub_device_info(fd, i, &di_args[ndevs]);
-+		if (ret == -ENODEV)
-+			continue;
-+		if (ret)
-+			return ret;
-+		++ndevs;
-+	}
-+
-+	BUG_ON(ndevs == 0);
-+
-+	return 0;
-+}
-+
-+int mkdir_p(char *path)
-+{
-+	int i;
-+	int ret;
-+
-+	for (i = 1; i < strlen(path); ++i) {
-+		if (path[i] != '/')
-+			continue;
-+		path[i] = '\0';
-+		ret = mkdir(path, 0777);
-+		if (ret && errno != EEXIST)
-+			return 1;
-+		path[i] = '/';
-+	}
-+
-+	return 0;
-+}
-+
-+static const char * const cmd_scrub_start_usage[];
-+static const char * const cmd_scrub_resume_usage[];
-+
-+static int scrub_start(int argc, char **argv, int resume)
-+{
-+	int fdmnt;
-+	int prg_fd = -1;
-+	int fdres = -1;
-+	int ret;
-+	pid_t pid;
-+	int c;
-+	int i;
-+	int err = 0;
-+	int e_uncorrectable = 0;
-+	int e_correctable = 0;
-+	int print_raw = 0;
-+	char *path;
-+	int do_background = 1;
-+	int do_wait = 0;
-+	int do_print = 0;
-+	int do_quiet = 0;
-+	int do_record = 1;
-+	int readonly = 0;
-+	int do_stats_per_dev = 0;
-+	int n_start = 0;
-+	int n_skip = 0;
-+	int n_resume = 0;
-+	struct btrfs_ioctl_fs_info_args fi_args;
-+	struct btrfs_ioctl_dev_info_args *di_args = NULL;
-+	struct scrub_progress *sp = NULL;
-+	struct scrub_fs_stat fs_stat;
-+	struct timeval tv;
-+	struct sockaddr_un addr = {
-+		.sun_family = AF_UNIX,
-+	};
-+	pthread_t *t_devs = NULL;
-+	pthread_t t_prog;
-+	pthread_attr_t t_attr;
-+	struct scrub_file_record **past_scrubs = NULL;
-+	struct scrub_file_record *last_scrub = NULL;
-+	char *datafile = strdup(SCRUB_DATA_FILE);
-+	char fsid[37];
-+	char sock_path[BTRFS_PATH_NAME_MAX + 1] = "";
-+	struct scrub_progress_cycle spc;
-+	pthread_mutex_t spc_write_mutex = PTHREAD_MUTEX_INITIALIZER;
-+	void *terr;
-+	u64 devid;
-+
-+	optind = 1;
-+	while ((c = getopt(argc, argv, "BdqrR")) != -1) {
-+		switch (c) {
-+		case 'B':
-+			do_background = 0;
-+			do_wait = 1;
-+			do_print = 1;
-+			break;
-+		case 'd':
-+			do_stats_per_dev = 1;
-+			break;
-+		case 'q':
-+			do_quiet = 1;
-+			break;
-+		case 'r':
-+			readonly = 1;
-+			break;
-+		case 'R':
-+			print_raw = 1;
-+			break;
-+		case '?':
-+		default:
-+			usage(resume ? cmd_scrub_resume_usage :
-+						cmd_scrub_start_usage);
-+		}
-+	}
-+
-+	/* try to catch most error cases before forking */
-+
-+	if (check_argc_exact(argc - optind, 1)) {
-+		usage(resume ? cmd_scrub_resume_usage :
-+					cmd_scrub_start_usage);
-+	}
-+
-+	spc.progress = NULL;
-+	if (do_quiet && do_print)
-+		do_print = 0;
-+
-+	if (mkdir_p(datafile)) {
-+		ERR(!do_quiet, "WARNING: cannot create scrub data "
-+			       "file, mkdir %s failed: %s. Status recording "
-+			       "disabled\n", datafile, strerror(errno));
-+		do_record = 0;
-+	}
-+	free(datafile);
-+
-+	path = argv[optind];
-+
-+	fdmnt = open_file_or_dir(path);
-+	if (fdmnt < 0) {
-+		ERR(!do_quiet, "ERROR: can't access '%s'\n", path);
-+		return 12;
-+	}
-+
-+	ret = scrub_fs_info(fdmnt, path, &fi_args, &di_args);
-+	if (ret) {
-+		ERR(!do_quiet, "ERROR: getting dev info for scrub failed: "
-+		    "%s\n", strerror(-ret));
-+		err = 1;
-+		goto out;
-+	}
-+	if (!fi_args.num_devices) {
-+		ERR(!do_quiet, "ERROR: no devices found\n");
-+		err = 1;
-+		goto out;
-+	}
-+
-+	uuid_unparse(fi_args.fsid, fsid);
-+	fdres = scrub_open_file_r(SCRUB_DATA_FILE, fsid);
-+	if (fdres < 0 && fdres != -ENOENT) {
-+		ERR(!do_quiet, "WARNING: failed to open status file: "
-+		    "%s\n", strerror(-fdres));
-+	} else if (fdres >= 0) {
-+		past_scrubs = scrub_read_file(fdres, !do_quiet);
-+		if (IS_ERR(past_scrubs))
-+			ERR(!do_quiet, "WARNING: failed to read status file: "
-+			    "%s\n", strerror(-PTR_ERR(past_scrubs)));
-+		close(fdres);
-+	}
-+
-+	t_devs = malloc(fi_args.num_devices * sizeof(*t_devs));
-+	sp = calloc(fi_args.num_devices, sizeof(*sp));
-+	spc.progress = calloc(fi_args.num_devices * 2, sizeof(*spc.progress));
-+
-+	if (!t_devs || !sp || !spc.progress) {
-+		ERR(!do_quiet, "ERROR: scrub failed: %s", strerror(errno));
-+		err = 1;
-+		goto out;
-+	}
-+
-+	ret = pthread_attr_init(&t_attr);
-+	if (ret) {
-+		ERR(!do_quiet, "ERROR: pthread_attr_init failed: %s\n",
-+		    strerror(ret));
-+		err = 1;
-+		goto out;
-+	}
-+
-+	for (i = 0; i < fi_args.num_devices; ++i) {
-+		devid = di_args[i].devid;
-+		ret = pthread_mutex_init(&sp[i].progress_mutex, NULL);
-+		if (ret) {
-+			ERR(!do_quiet, "ERROR: pthread_mutex_init failed: "
-+			    "%s\n", strerror(ret));
-+			err = 1;
-+			goto out;
-+		}
-+		last_scrub = last_dev_scrub(past_scrubs, devid);
-+		sp[i].scrub_args.devid = devid;
-+		sp[i].fd = fdmnt;
-+		if (resume && last_scrub && (last_scrub->stats.canceled ||
-+					     !last_scrub->stats.finished)) {
-+			++n_resume;
-+			sp[i].scrub_args.start = last_scrub->p.last_physical;
-+			sp[i].resumed = last_scrub;
-+		} else if (resume) {
-+			++n_skip;
-+			sp[i].skip = 1;
-+			sp[i].resumed = last_scrub;
-+			continue;
-+		} else {
-+			++n_start;
-+			sp[i].scrub_args.start = 0ll;
-+			sp[i].resumed = NULL;
-+		}
-+		sp[i].skip = 0;
-+		sp[i].scrub_args.end = (u64)-1ll;
-+		sp[i].scrub_args.flags = readonly ? BTRFS_SCRUB_READONLY : 0;
-+	}
-+
-+	if (!n_start && !n_resume) {
-+		if (!do_quiet)
-+			printf("scrub: nothing to resume for %s, fsid %s\n",
-+			       path, fsid);
-+		err = 0;
-+		goto out;
-+	}
-+
-+	ret = prg_fd = socket(AF_UNIX, SOCK_STREAM, 0);
-+	while (ret != -1) {
-+		ret = scrub_datafile(SCRUB_PROGRESS_SOCKET_PATH, fsid, NULL,
-+					sock_path, sizeof(sock_path));
-+		/* ignore EOVERFLOW, try using a shorter path for the socket */
-+		addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
-+		strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
-+		ret = bind(prg_fd, (struct sockaddr *)&addr, sizeof(addr));
-+		if (ret != -1 || errno != EADDRINUSE)
-+			break;
-+		/*
-+		 * bind failed with EADDRINUSE. so let's see if anyone answers
-+		 * when we make a call to the socket ...
-+		 */
-+		ret = connect(prg_fd, (struct sockaddr *)&addr, sizeof(addr));
-+		if (!ret || errno != ECONNREFUSED) {
-+			/* ... yes, so scrub must be running. error out */
-+			fprintf(stderr, "ERROR: scrub already running\n");
-+			close(prg_fd);
-+			goto out;
-+		}
-+		/*
-+		 * ... no, this means someone left us alone with an unused
-+		 * socket in the file system. remove it and try again.
-+		 */
-+		ret = unlink(sock_path);
-+	}
-+	if (ret != -1)
-+		ret = listen(prg_fd, 100);
-+	if (ret == -1) {
-+		ERR(!do_quiet, "WARNING: failed to open the progress status "
-+		    "socket at %s: %s. Progress cannot be queried\n",
-+		    sock_path[0] ? sock_path : SCRUB_PROGRESS_SOCKET_PATH,
-+		    strerror(errno));
-+		if (prg_fd != -1) {
-+			close(prg_fd);
-+			prg_fd = -1;
-+			if (sock_path[0])
-+				unlink(sock_path);
-+		}
-+	}
-+
-+	if (do_record) {
-+		/* write all-zero progress file for a start */
-+		ret = scrub_write_progress(&spc_write_mutex, fsid, sp,
-+					   fi_args.num_devices);
-+		if (ret) {
-+			ERR(!do_quiet, "WARNING: failed to write the progress "
-+			    "status file: %s. Status recording disabled\n",
-+			    strerror(-ret));
-+			do_record = 0;
-+		}
-+	}
-+
-+	if (do_background) {
-+		pid = fork();
-+		if (pid == -1) {
-+			ERR(!do_quiet, "ERROR: cannot scrub, fork failed: "
-+					"%s\n", strerror(errno));
-+			err = 1;
-+			goto out;
-+		}
-+
-+		if (pid) {
-+			int stat;
-+			scrub_handle_sigint_parent();
-+			if (!do_quiet)
-+				printf("scrub %s on %s, fsid %s (pid=%d)\n",
-+				       n_start ? "started" : "resumed",
-+				       path, fsid, pid);
-+			if (!do_wait) {
-+				err = 0;
-+				goto out;
-+			}
-+			ret = wait(&stat);
-+			if (ret != pid) {
-+				ERR(!do_quiet, "ERROR: wait failed: (ret=%d) "
-+				    "%s\n", ret, strerror(errno));
-+				err = 1;
-+				goto out;
-+			}
-+			if (!WIFEXITED(stat) || WEXITSTATUS(stat)) {
-+				ERR(!do_quiet, "ERROR: scrub process failed\n");
-+				err = WIFEXITED(stat) ? WEXITSTATUS(stat) : -1;
-+				goto out;
-+			}
-+			err = 0;
-+			goto out;
-+		}
-+	}
-+
-+	scrub_handle_sigint_child(fdmnt);
-+
-+	for (i = 0; i < fi_args.num_devices; ++i) {
-+		if (sp[i].skip) {
-+			sp[i].scrub_args.progress = sp[i].resumed->p;
-+			sp[i].stats = sp[i].resumed->stats;
-+			sp[i].ret = 0;
-+			sp[i].stats.finished = 1;
-+			continue;
-+		}
-+		devid = di_args[i].devid;
-+		gettimeofday(&tv, NULL);
-+		sp[i].stats.t_start = tv.tv_sec;
-+		ret = pthread_create(&t_devs[i], &t_attr,
-+					scrub_one_dev, &sp[i]);
-+		if (ret) {
-+			if (do_print)
-+				fprintf(stderr, "ERROR: creating "
-+					"scrub_one_dev[%llu] thread failed: "
-+					"%s\n", devid, strerror(ret));
-+			err = 1;
-+			goto out;
-+		}
-+	}
-+
-+	spc.fdmnt = fdmnt;
-+	spc.prg_fd = prg_fd;
-+	spc.do_record = do_record;
-+	spc.write_mutex = &spc_write_mutex;
-+	spc.shared_progress = sp;
-+	spc.fi = &fi_args;
-+	ret = pthread_create(&t_prog, &t_attr, scrub_progress_cycle, &spc);
-+	if (ret) {
-+		if (do_print)
-+			fprintf(stderr, "ERROR: creating progress thread "
-+				"failed: %s\n", strerror(ret));
-+		err = 1;
-+		goto out;
-+	}
-+
-+	err = 0;
-+	for (i = 0; i < fi_args.num_devices; ++i) {
-+		if (sp[i].skip)
-+			continue;
-+		devid = di_args[i].devid;
-+		ret = pthread_join(t_devs[i], NULL);
-+		if (ret) {
-+			if (do_print)
-+				fprintf(stderr, "ERROR: pthread_join failed "
-+					"for scrub_one_dev[%llu]: %s\n", devid,
-+					strerror(ret));
-+			++err;
-+			continue;
-+		}
-+		if (sp[i].ret && sp[i].ioctl_errno == ENODEV) {
-+			if (do_print)
-+				fprintf(stderr, "WARNING: device %lld not "
-+					"present\n", devid);
-+			continue;
-+		}
-+		if (sp[i].ret && sp[i].ioctl_errno == ECANCELED) {
-+			++err;
-+		} else if (sp[i].ret) {
-+			if (do_print)
-+				fprintf(stderr, "ERROR: scrubbing %s failed "
-+					"for device id %lld (%s)\n", path,
-+					devid, strerror(sp[i].ioctl_errno));
-+			++err;
-+			continue;
-+		}
-+		if (sp[i].scrub_args.progress.uncorrectable_errors > 0)
-+			e_uncorrectable++;
-+		if (sp[i].scrub_args.progress.corrected_errors > 0
-+		    || sp[i].scrub_args.progress.unverified_errors > 0)
-+			e_correctable++;
-+	}
-+
-+	if (do_print) {
-+		const char *append = "done";
-+		if (!do_stats_per_dev)
-+			init_fs_stat(&fs_stat);
-+		for (i = 0; i < fi_args.num_devices; ++i) {
-+			if (do_stats_per_dev) {
-+				print_scrub_dev(&di_args[i],
-+						&sp[i].scrub_args.progress,
-+						print_raw,
-+						sp[i].ret ? "canceled" : "done",
-+						&sp[i].stats);
-+			} else {
-+				if (sp[i].ret)
-+					append = "canceled";
-+				add_to_fs_stat(&sp[i].scrub_args.progress,
-+						&sp[i].stats, &fs_stat);
-+			}
-+		}
-+		if (!do_stats_per_dev) {
-+			printf("scrub %s for %s\n", append, fsid);
-+			print_fs_stat(&fs_stat, print_raw);
-+		}
-+	}
-+
-+	ret = pthread_cancel(t_prog);
-+	if (!ret)
-+		ret = pthread_join(t_prog, &terr);
-+	if (do_print && ret) {
-+		fprintf(stderr, "ERROR: progress thead handling failed: %s\n",
-+			strerror(ret));
-+	}
-+
-+	if (do_print && terr && terr != PTHREAD_CANCELED) {
-+		fprintf(stderr, "ERROR: recording progress "
-+			"failed: %s\n", strerror(-PTR_ERR(terr)));
-+	}
-+
-+	if (do_record) {
-+		ret = scrub_write_progress(&spc_write_mutex, fsid, sp,
-+					   fi_args.num_devices);
-+		if (ret && do_print) {
-+			fprintf(stderr, "ERROR: failed to record the result: "
-+				"%s\n", strerror(-ret));
-+		}
-+	}
-+
-+	scrub_handle_sigint_child(-1);
-+
-+out:
-+	free_history(past_scrubs);
-+	free(di_args);
-+	free(t_devs);
-+	free(sp);
-+	free(spc.progress);
-+	if (prg_fd > -1) {
-+		close(prg_fd);
-+		if (sock_path[0])
-+			unlink(sock_path);
-+	}
-+	close(fdmnt);
-+
-+	if (err)
-+		return 1;
-+	if (e_correctable)
-+		return 7;
-+	if (e_uncorrectable)
-+		return 8;
-+	return 0;
-+}
-+
-+static const char * const cmd_scrub_start_usage[] = {
-+	"btrfs scrub start [-Bdqr] <path>|<device>",
-+	"Start a new scrub",
-+	"",
-+	"-B     do not background",
-+	"-d     stats per device (-B only)",
-+	"-q     be quiet",
-+	"-r     read only mode",
-+	NULL
-+};
-+
-+static int cmd_scrub_start(int argc, char **argv)
-+{
-+	return scrub_start(argc, argv, 0);
-+}
-+
-+static const char * const cmd_scrub_cancel_usage[] = {
-+	"btrfs scrub cancel <path>|<device>",
-+	"Cancel a running scrub",
-+	NULL
-+};
-+
-+static int cmd_scrub_cancel(int argc, char **argv)
-+{
-+	char *path;
-+	int ret;
-+	int fdmnt;
-+	int err;
-+	char mp[BTRFS_PATH_NAME_MAX + 1];
-+	struct btrfs_fs_devices *fs_devices_mnt = NULL;
-+
-+	if (check_argc_exact(argc, 2))
-+		usage(cmd_scrub_cancel_usage);
-+
-+	path = argv[1];
-+
-+	fdmnt = open_file_or_dir(path);
-+	if (fdmnt < 0) {
-+		fprintf(stderr, "ERROR: scrub cancel failed\n");
-+		return 12;
-+	}
-+
-+again:
-+	ret = ioctl(fdmnt, BTRFS_IOC_SCRUB_CANCEL, NULL);
-+	err = errno;
-+	close(fdmnt);
-+
-+	if (ret && err == EINVAL) {
-+		/* path is no mounted btrfs. try if it's a device */
-+		ret = check_mounted_where(fdmnt, path, mp, sizeof(mp),
-+					  &fs_devices_mnt);
-+		close(fdmnt);
-+		if (ret) {
-+			fdmnt = open_file_or_dir(mp);
-+			if (fdmnt >= 0) {
-+				path = mp;
-+				goto again;
-+			}
-+		}
-+	}
-+
-+	if (ret) {
-+		fprintf(stderr, "ERROR: scrub cancel failed on %s: %s\n", path,
-+			err == ENOTCONN ? "not running" : strerror(errno));
-+		return 1;
-+	}
-+
-+	printf("scrub cancelled\n");
-+
-+	return 0;
-+}
-+
-+static const char * const cmd_scrub_resume_usage[] = {
-+	"btrfs scrub resume [-Bdqr] <path>|<device>",
-+	"Resume previously canceled or interrupted scrub",
-+	"",
-+	"-B     do not background",
-+	"-d     stats per device (-B only)",
-+	"-q     be quiet",
-+	"-r     read only mode",
-+	NULL
-+};
-+
-+static int cmd_scrub_resume(int argc, char **argv)
-+{
-+	return scrub_start(argc, argv, 1);
-+}
-+
-+static const char * const cmd_scrub_status_usage[] = {
-+	"btrfs scrub status [-dR] <path>|<device>",
-+	"Show status of running or finished scrub",
-+	"",
-+	"-d     stats per device",
-+	"-R     print raw stats",
-+	NULL
-+};
-+
-+static int cmd_scrub_status(int argc, char **argv)
-+{
-+	char *path;
-+	struct btrfs_ioctl_fs_info_args fi_args;
-+	struct btrfs_ioctl_dev_info_args *di_args = NULL;
-+	struct scrub_file_record **past_scrubs = NULL;
-+	struct scrub_file_record *last_scrub;
-+	struct scrub_fs_stat fs_stat;
-+	struct sockaddr_un addr = {
-+		.sun_family = AF_UNIX,
-+	};
-+	int ret;
-+	int fdmnt;
-+	int i;
-+	int print_raw = 0;
-+	int do_stats_per_dev = 0;
-+	char c;
-+	char fsid[37];
-+	int fdres = -1;
-+	int err = 0;
-+
-+	optind = 1;
-+	while ((c = getopt(argc, argv, "dR")) != -1) {
-+		switch (c) {
-+		case 'd':
-+			do_stats_per_dev = 1;
-+			break;
-+		case 'R':
-+			print_raw = 1;
-+			break;
-+		case '?':
-+		default:
-+			usage(cmd_scrub_status_usage);
-+		}
-+	}
-+
-+	if (check_argc_exact(argc - optind, 1))
-+		usage(cmd_scrub_status_usage);
-+
-+	path = argv[optind];
-+
-+	fdmnt = open_file_or_dir(path);
-+	if (fdmnt < 0) {
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-+		return 12;
-+	}
-+
-+	ret = scrub_fs_info(fdmnt, path, &fi_args, &di_args);
-+	if (ret) {
-+		fprintf(stderr, "ERROR: getting dev info for scrub failed: "
-+				"%s\n", strerror(-ret));
-+		err = 1;
-+		goto out;
-+	}
-+	if (!fi_args.num_devices) {
-+		fprintf(stderr, "ERROR: no devices found\n");
-+		err = 1;
-+		goto out;
-+	}
-+
-+	uuid_unparse(fi_args.fsid, fsid);
-+
-+	fdres = socket(AF_UNIX, SOCK_STREAM, 0);
-+	if (fdres == -1) {
-+		fprintf(stderr, "ERROR: failed to create socket to "
-+			"receive progress information: %s\n",
-+			strerror(errno));
-+		err = 1;
-+		goto out;
-+	}
-+	scrub_datafile(SCRUB_PROGRESS_SOCKET_PATH, fsid,
-+			NULL, addr.sun_path, sizeof(addr.sun_path));
-+	/* ignore EOVERFLOW, just use shorter name and hope for the best */
-+	addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
-+	ret = connect(fdres, (struct sockaddr *)&addr, sizeof(addr));
-+	if (ret == -1) {
-+		fdres = scrub_open_file_r(SCRUB_DATA_FILE, fsid);
-+		if (fdres < 0 && fdres != -ENOENT) {
-+			fprintf(stderr, "WARNING: failed to open status file: "
-+				"%s\n", strerror(-fdres));
-+			err = 1;
-+			goto out;
-+		}
-+	}
-+
-+	if (fdres >= 0) {
-+		past_scrubs = scrub_read_file(fdres, 1);
-+		if (IS_ERR(past_scrubs))
-+			fprintf(stderr, "WARNING: failed to read status: %s\n",
-+				strerror(-PTR_ERR(past_scrubs)));
-+	}
-+
-+	printf("scrub status for %s\n", fsid);
-+
-+	if (do_stats_per_dev) {
-+		for (i = 0; i < fi_args.num_devices; ++i) {
-+			last_scrub = last_dev_scrub(past_scrubs,
-+							di_args[i].devid);
-+			if (!last_scrub) {
-+				print_scrub_dev(&di_args[i], NULL, print_raw,
-+						NULL, NULL);
-+				continue;
-+			}
-+			print_scrub_dev(&di_args[i], &last_scrub->p, print_raw,
-+					last_scrub->stats.finished ?
-+							"history" : "status",
-+					&last_scrub->stats);
-+		}
-+	} else {
-+		init_fs_stat(&fs_stat);
-+		for (i = 0; i < fi_args.num_devices; ++i) {
-+			last_scrub = last_dev_scrub(past_scrubs,
-+							di_args[i].devid);
-+			if (!last_scrub)
-+				continue;
-+			add_to_fs_stat(&last_scrub->p, &last_scrub->stats,
-+					&fs_stat);
-+		}
-+		print_fs_stat(&fs_stat, print_raw);
-+	}
-+
-+out:
-+	free_history(past_scrubs);
-+	free(di_args);
-+	close(fdmnt);
-+	if (fdres > -1)
-+		close(fdres);
-+
-+	return err;
-+}
-+
-+const struct cmd_group scrub_cmd_group = {
-+	scrub_cmd_group_usage, NULL, {
-+		{ "start", cmd_scrub_start, cmd_scrub_start_usage, NULL, 0 },
-+		{ "cancel", cmd_scrub_cancel, cmd_scrub_cancel_usage, NULL, 0 },
-+		{ "resume", cmd_scrub_resume, cmd_scrub_resume_usage, NULL, 0 },
-+		{ "status", cmd_scrub_status, cmd_scrub_status_usage, NULL, 0 },
-+		{ 0, 0, 0, 0, 0 }
-+	}
-+};
-+
-+int cmd_scrub(int argc, char **argv)
-+{
-+	return handle_command_group(&scrub_cmd_group, argc, argv);
-+}
-diff --git a/cmds-subvolume.c b/cmds-subvolume.c
-new file mode 100644
-index 0000000..950fa8f
---- /dev/null
-+++ b/cmds-subvolume.c
-@@ -0,0 +1,533 @@
-+/*
-+ * 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.
-+ */
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <unistd.h>
-+#include <sys/ioctl.h>
-+#include <errno.h>
-+#include <sys/stat.h>
-+#include <libgen.h>
-+#include <limits.h>
-+
-+#include "kerncompat.h"
-+#include "ioctl.h"
-+
-+#include "commands.h"
-+
-+/* btrfs-list.c */
-+int list_subvols(int fd, int print_parent, int get_default);
-+int find_updated_files(int fd, u64 root_id, u64 oldest_gen);
-+
-+static const char * const subvolume_cmd_group_usage[] = {
-+	"btrfs subvolume <command> <args>",
-+	NULL
-+};
-+
-+/*
-+ * test if path is a directory
-+ * this function return
-+ * 0-> path exists but it is not a directory
-+ * 1-> path exists and it is  a directory
-+ * -1 -> path is unaccessible
-+ */
-+static int test_isdir(char *path)
-+{
-+	struct stat	st;
-+	int		res;
-+
-+	res = stat(path, &st);
-+	if(res < 0 )
-+		return -1;
-+
-+	return S_ISDIR(st.st_mode);
-+}
-+
-+static const char * const cmd_subvol_create_usage[] = {
-+	"btrfs subvolume create [<dest>/]<name>",
-+	"Create a subvolume",
-+	"Create a subvolume <name> in <dest>.  If <dest> is not given",
-+	"subvolume <name> will be created in the current directory.",
-+	NULL
-+};
-+
-+static int cmd_subvol_create(int argc, char **argv)
-+{
-+	int	res, fddst, len, e;
-+	char	*newname;
-+	char	*dstdir;
-+	struct btrfs_ioctl_vol_args	args;
-+	char	*dst;
-+
-+	if (check_argc_exact(argc, 2))
-+		usage(cmd_subvol_create_usage);
-+
-+	dst = argv[1];
-+
-+	res = test_isdir(dst);
-+	if(res >= 0 ){
-+		fprintf(stderr, "ERROR: '%s' exists\n", dst);
-+		return 12;
-+	}
-+
-+	newname = strdup(dst);
-+	newname = basename(newname);
-+	dstdir = strdup(dst);
-+	dstdir = dirname(dstdir);
-+
-+	if( !strcmp(newname,".") || !strcmp(newname,"..") ||
-+	     strchr(newname, '/') ){
-+		fprintf(stderr, "ERROR: uncorrect subvolume name ('%s')\n",
-+			newname);
-+		return 14;
-+	}
-+
-+	len = strlen(newname);
-+	if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
-+		fprintf(stderr, "ERROR: subvolume name too long ('%s)\n",
-+			newname);
-+		return 14;
-+	}
-+
-+	fddst = open_file_or_dir(dstdir);
-+	if (fddst < 0) {
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
-+		return 12;
-+	}
-+
-+	printf("Create subvolume '%s/%s'\n", dstdir, newname);
-+	strncpy(args.name, newname, BTRFS_PATH_NAME_MAX);
-+	res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
-+	e = errno;
-+
-+	close(fddst);
-+
-+	if(res < 0 ){
-+		fprintf( stderr, "ERROR: cannot create subvolume - %s\n",
-+			strerror(e));
-+		return 11;
-+	}
-+
-+	return 0;
-+}
-+
-+/*
-+ * test if path is a subvolume:
-+ * this function return
-+ * 0-> path exists but it is not a subvolume
-+ * 1-> path exists and it is  a subvolume
-+ * -1 -> path is unaccessible
-+ */
-+static int test_issubvolume(char *path)
-+{
-+	struct stat	st;
-+	int		res;
-+
-+	res = stat(path, &st);
-+	if(res < 0 )
-+		return -1;
-+
-+	return (st.st_ino == 256) && S_ISDIR(st.st_mode);
-+}
-+
-+static const char * const cmd_subvol_delete_usage[] = {
-+	"btrfs subvolume delete <name>",
-+	"Delete a subvolume",
-+	NULL
-+};
-+
-+static int cmd_subvol_delete(int argc, char **argv)
-+{
-+	int	res, fd, len, e;
-+	struct btrfs_ioctl_vol_args	args;
-+	char	*dname, *vname, *cpath;
-+	char	*path;
-+
-+	if (check_argc_exact(argc, 2))
-+		usage(cmd_subvol_delete_usage);
-+
-+	path = argv[1];
-+
-+	res = test_issubvolume(path);
-+	if(res<0){
-+		fprintf(stderr, "ERROR: error accessing '%s'\n", path);
-+		return 12;
-+	}
-+	if(!res){
-+		fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
-+		return 13;
-+	}
-+
-+	cpath = realpath(path, 0);
-+	dname = strdup(cpath);
-+	dname = dirname(dname);
-+	vname = strdup(cpath);
-+	vname = basename(vname);
-+	free(cpath);
-+
-+	if( !strcmp(vname,".") || !strcmp(vname,"..") ||
-+	     strchr(vname, '/') ){
-+		fprintf(stderr, "ERROR: incorrect subvolume name ('%s')\n",
-+			vname);
-+		return 14;
-+	}
-+
-+	len = strlen(vname);
-+	if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
-+		fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
-+			vname);
-+		return 14;
-+	}
-+
-+	fd = open_file_or_dir(dname);
-+	if (fd < 0) {
-+		close(fd);
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", dname);
-+		return 12;
-+	}
-+
-+	printf("Delete subvolume '%s/%s'\n", dname, vname);
-+	strncpy(args.name, vname, BTRFS_PATH_NAME_MAX);
-+	res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
-+	e = errno;
-+
-+	close(fd);
-+
-+	if(res < 0 ){
-+		fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
-+			dname, vname, strerror(e));
-+		return 11;
-+	}
-+
-+	return 0;
-+}
-+
-+static const char * const cmd_subvol_list_usage[] = {
-+	"btrfs subvolume list [-p] <path>",
-+	"List subvolumes (and snapshots)",
-+	"",
-+	"-p     print parent ID",
-+	NULL
-+};
-+
-+static int cmd_subvol_list(int argc, char **argv)
-+{
-+	int fd;
-+	int ret;
-+	int print_parent = 0;
-+	char *subvol;
-+
-+	optind = 1;
-+	while(1) {
-+		int c = getopt(argc, argv, "p");
-+		if (c < 0)
-+			break;
-+
-+		switch(c) {
-+		case 'p':
-+			print_parent = 1;
-+			break;
-+		default:
-+			usage(cmd_subvol_list_usage);
-+		}
-+	}
-+
-+	if (check_argc_exact(argc - optind, 1))
-+		usage(cmd_subvol_list_usage);
-+
-+	subvol = argv[optind];
-+
-+	ret = test_issubvolume(subvol);
-+	if (ret < 0) {
-+		fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
-+		return 12;
-+	}
-+	if (!ret) {
-+		fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
-+		return 13;
-+	}
-+
-+	fd = open_file_or_dir(subvol);
-+	if (fd < 0) {
-+		fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
-+		return 12;
-+	}
-+	ret = list_subvols(fd, print_parent, 0);
-+	if (ret)
-+		return 19;
-+	return 0;
-+}
-+
-+static const char * const cmd_snapshot_usage[] = {
-+	"btrfs subvolume snapshot [-r] <source> [<dest>/]<name>",
-+	"Create a snapshot of the subvolume",
-+	"Create a writable/readonly snapshot of the subvolume <source> with",
-+	"the name <name> in the <dest> directory",
-+	"",
-+	"-r     create a readonly snapshot",
-+	NULL
-+};
-+
-+static int cmd_snapshot(int argc, char **argv)
-+{
-+	char	*subvol, *dst;
-+	int	res, fd, fddst, len, e, readonly = 0;
-+	char	*newname;
-+	char	*dstdir;
-+	struct btrfs_ioctl_vol_args_v2	args;
-+
-+	memset(&args, 0, sizeof(args));
-+
-+	optind = 1;
-+	while (1) {
-+		int c = getopt(argc, argv, "r");
-+		if (c < 0)
-+			break;
-+
-+		switch (c) {
-+		case 'r':
-+			readonly = 1;
-+			break;
-+		default:
-+			usage(cmd_snapshot_usage);
-+		}
-+	}
-+
-+	if (check_argc_exact(argc - optind, 2))
-+		usage(cmd_snapshot_usage);
-+
-+	subvol = argv[optind];
-+	dst = argv[optind + 1];
-+
-+	res = test_issubvolume(subvol);
-+	if(res<0){
-+		fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
-+		return 12;
-+	}
-+	if(!res){
-+		fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
-+		return 13;
-+	}
-+
-+	res = test_isdir(dst);
-+	if(res == 0 ){
-+		fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
-+		return 12;
-+	}
-+
-+	if(res>0){
-+		newname = strdup(subvol);
-+		newname = basename(newname);
-+		dstdir = dst;
-+	}else{
-+		newname = strdup(dst);
-+		newname = basename(newname);
-+		dstdir = strdup(dst);
-+		dstdir = dirname(dstdir);
-+	}
-+
-+	if( !strcmp(newname,".") || !strcmp(newname,"..") ||
-+	     strchr(newname, '/') ){
-+		fprintf(stderr, "ERROR: incorrect snapshot name ('%s')\n",
-+			newname);
-+		return 14;
-+	}
-+
-+	len = strlen(newname);
-+	if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
-+		fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
-+			newname);
-+		return 14;
-+	}
-+
-+	fddst = open_file_or_dir(dstdir);
-+	if (fddst < 0) {
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
-+		return 12;
-+	}
-+
-+	fd = open_file_or_dir(subvol);
-+	if (fd < 0) {
-+		close(fddst);
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
-+		return 12;
-+	}
-+
-+	if (readonly) {
-+		args.flags |= BTRFS_SUBVOL_RDONLY;
-+		printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
-+		       subvol, dstdir, newname);
-+	} else {
-+		printf("Create a snapshot of '%s' in '%s/%s'\n",
-+		       subvol, dstdir, newname);
-+	}
-+
-+	args.fd = fd;
-+	strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX);
-+	res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
-+	e = errno;
-+
-+	close(fd);
-+	close(fddst);
-+
-+	if(res < 0 ){
-+		fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
-+			subvol, strerror(e));
-+		return 11;
-+	}
-+
-+	return 0;
-+}
-+
-+static const char * const cmd_subvol_get_default_usage[] = {
-+	"btrfs subvolume get-dafault <path>",
-+	"Get the default subvolume of a filesystem",
-+	NULL
-+};
-+
-+static int cmd_subvol_get_default(int argc, char **argv)
-+{
-+	int fd;
-+	int ret;
-+	char *subvol;
-+
-+	if (check_argc_exact(argc, 2))
-+		usage(cmd_subvol_get_default_usage);
-+
-+	subvol = argv[1];
-+
-+	ret = test_issubvolume(subvol);
-+	if (ret < 0) {
-+		fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
-+		return 12;
-+	}
-+	if (!ret) {
-+		fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
-+		return 13;
-+	}
-+
-+	fd = open_file_or_dir(subvol);
-+	if (fd < 0) {
-+		fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
-+		return 12;
-+	}
-+	ret = list_subvols(fd, 0, 1);
-+	if (ret)
-+		return 19;
-+	return 0;
-+}
-+
-+static const char * const cmd_subvol_set_default_usage[] = {
-+	"btrfs subvolume set-dafault <subvolid> <path>",
-+	"Set the default subvolume of a filesystem",
-+	NULL
-+};
-+
-+static int cmd_subvol_set_default(int argc, char **argv)
-+{
-+	int	ret=0, fd, e;
-+	u64	objectid;
-+	char	*path;
-+	char	*subvolid;
-+
-+	if (check_argc_exact(argc, 3))
-+		usage(cmd_subvol_set_default_usage);
-+
-+	subvolid = argv[1];
-+	path = argv[2];
-+
-+	fd = open_file_or_dir(path);
-+	if (fd < 0) {
-+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-+		return 12;
-+	}
-+
-+	objectid = (unsigned long long)strtoll(subvolid, NULL, 0);
-+	if (errno == ERANGE) {
-+		fprintf(stderr, "ERROR: invalid tree id (%s)\n",subvolid);
-+		return 30;
-+	}
-+	ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
-+	e = errno;
-+	close(fd);
-+	if( ret < 0 ){
-+		fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
-+			strerror(e));
-+		return 30;
-+	}
-+	return 0;
-+}
-+
-+static const char * const cmd_find_new_usage[] = {
-+	"btrfs subvolume find-new <path> <lastgen>",
-+	"List the recently modified files in a filesystem",
-+	NULL
-+};
-+
-+static int cmd_find_new(int argc, char **argv)
-+{
-+	int fd;
-+	int ret;
-+	char *subvol;
-+	u64 last_gen;
-+
-+	if (check_argc_exact(argc, 3))
-+		usage(cmd_find_new_usage);
-+
-+	subvol = argv[1];
-+	last_gen = atoll(argv[2]);
-+
-+	ret = test_issubvolume(subvol);
-+	if (ret < 0) {
-+		fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
-+		return 12;
-+	}
-+	if (!ret) {
-+		fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
-+		return 13;
-+	}
-+
-+	fd = open_file_or_dir(subvol);
-+	if (fd < 0) {
-+		fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
-+		return 12;
-+	}
-+	ret = find_updated_files(fd, 0, last_gen);
-+	if (ret)
-+		return 19;
-+	return 0;
-+}
-+
-+const struct cmd_group subvolume_cmd_group = {
-+	subvolume_cmd_group_usage, NULL, {
-+		{ "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
-+		{ "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
-+		{ "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
-+		{ "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
-+		{ "get-default", cmd_subvol_get_default,
-+			cmd_subvol_get_default_usage, NULL, 0 },
-+		{ "set-default", cmd_subvol_set_default,
-+			cmd_subvol_set_default_usage, NULL, 0 },
-+		{ "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
-+		{ 0, 0, 0, 0, 0 }
-+	}
-+};
-+
-+int cmd_subvolume(int argc, char **argv)
-+{
-+	return handle_command_group(&subvolume_cmd_group, argc, argv);
-+}
-diff --git a/commands.h b/commands.h
-new file mode 100644
-index 0000000..a303a50
---- /dev/null
-+++ b/commands.h
-@@ -0,0 +1,97 @@
-+/*
-+ * 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 ARGV0_BUF_SIZE	64
-+
-+struct cmd_struct {
-+	const char *token;
-+	int (*fn)(int, char **);
-+
-+	/*
-+	 * Usage strings
-+	 *
-+	 * A NULL-terminated array of the following format:
-+	 *
-+	 *   usagestr[0] - one-line synopsis (required)
-+	 *   usagestr[1] - one-line short description (required)
-+	 *   usagestr[2..m] - a long (possibly multi-line) description
-+	 *                    (optional)
-+	 *   usagestr[m + 1] - an empty line separator (required if at least one
-+	 *                     option string is given, not needed otherwise)
-+	 *   usagestr[m + 2..n] - option strings, one option per line
-+	 *                        (optional)
-+	 *   usagestr[n + 1] - NULL terminator
-+	 *
-+	 * Options (if present) should always (even if there is no long
-+	 * description) be prepended with an empty line.  Supplied strings are
-+	 * indented but otherwise printed as-is, no automatic wrapping is done.
-+	 *
-+	 * Grep for cmd_*_usage[] for examples.
-+	 */
-+	const char * const *usagestr;
-+
-+	/* should be NULL if token is not a subgroup */
-+	const struct cmd_group *next;
-+
-+	/* if true don't list this token in help listings */
-+	int hidden;
-+};
-+
-+struct cmd_group {
-+	const char * const *usagestr;
-+	const char *infostr;
-+
-+	const struct cmd_struct commands[];
-+};
-+
-+/* btrfs.c */
-+int prefixcmp(const char *str, const char *prefix);
-+
-+int check_argc_exact(int nargs, int expected);
-+int check_argc_min(int nargs, int expected);
-+int check_argc_max(int nargs, int expected);
-+
-+int handle_command_group(const struct cmd_group *grp, int argc,
-+			 char **argv);
-+
-+/* help.c */
-+extern const char * const generic_cmd_help_usage[];
-+
-+void usage(const char * const *usagestr);
-+void usage_command(const struct cmd_struct *cmd, int full, int err);
-+void usage_command_group(const struct cmd_group *grp, int all, int err);
-+
-+void help_unknown_token(const char *arg, const struct cmd_group *grp);
-+void help_ambiguous_token(const char *arg, const struct cmd_group *grp);
-+
-+void help_command_group(const struct cmd_group *grp, int argc, char **argv);
-+
-+/* common.c */
-+int open_file_or_dir(const char *fname);
-+
-+extern const struct cmd_group subvolume_cmd_group;
-+extern const struct cmd_group filesystem_cmd_group;
-+extern const struct cmd_group balance_cmd_group;
-+extern const struct cmd_group device_cmd_group;
-+extern const struct cmd_group scrub_cmd_group;
-+extern const struct cmd_group inspect_cmd_group;
-+
-+int cmd_subvolume(int argc, char **argv);
-+int cmd_filesystem(int argc, char **argv);
-+int cmd_balance(int argc, char **argv);
-+int cmd_device(int argc, char **argv);
-+int cmd_scrub(int argc, char **argv);
-+int cmd_inspect(int argc, char **argv);
-diff --git a/common.c b/common.c
-new file mode 100644
-index 0000000..03f6570
---- /dev/null
-+++ b/common.c
-@@ -0,0 +1,46 @@
-+/*
-+ * 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.
-+ */
-+
-+#include <sys/types.h>
-+#include <sys/stat.h>
-+#include <dirent.h>
-+#include <fcntl.h>
-+
-+int open_file_or_dir(const char *fname)
-+{
-+	int ret;
-+	struct stat st;
-+	DIR *dirstream;
-+	int fd;
-+
-+	ret = stat(fname, &st);
-+	if (ret < 0) {
-+		return -1;
-+	}
-+	if (S_ISDIR(st.st_mode)) {
-+		dirstream = opendir(fname);
-+		if (!dirstream) {
-+			return -2;
-+		}
-+		fd = dirfd(dirstream);
-+	} else {
-+		fd = open(fname, O_RDWR);
-+	}
-+	if (fd < 0) {
-+		return -3;
-+	}
-+	return fd;
-+}
-diff --git a/convert.c b/convert.c
-index d2c9efa..fa7bf8c 100644
---- a/convert.c
-+++ b/convert.c
-@@ -370,7 +370,6 @@ static int record_file_extent(struct btrfs_trans_handle *trans,
- 	struct btrfs_extent_item *ei;
- 	u32 blocksize = root->sectorsize;
- 	u64 nbytes;
--	u64 bytes_used;
- 
- 	if (disk_bytenr == 0) {
- 		ret = btrfs_insert_file_extent(trans, root, objectid,
-@@ -432,9 +431,6 @@ static int record_file_extent(struct btrfs_trans_handle *trans,
- 	nbytes = btrfs_stack_inode_nbytes(inode) + num_bytes;
- 	btrfs_set_stack_inode_nbytes(inode, nbytes);
- 
--	bytes_used = btrfs_root_used(&root->root_item);
--	btrfs_set_root_used(&root->root_item, bytes_used + num_bytes);
--
- 	btrfs_release_path(root, &path);
- 
- 	ins_key.objectid = disk_bytenr;
-@@ -454,9 +450,6 @@ static int record_file_extent(struct btrfs_trans_handle *trans,
- 
- 		btrfs_mark_buffer_dirty(leaf);
- 
--		bytes_used = btrfs_super_bytes_used(&info->super_copy);
--		btrfs_set_super_bytes_used(&info->super_copy, bytes_used +
--					   num_bytes);
- 		ret = btrfs_update_block_group(trans, root, disk_bytenr,
- 					       num_bytes, 1, 0);
- 		if (ret)
-@@ -864,7 +857,7 @@ static int copy_single_xattr(struct btrfs_trans_handle *trans,
- 		data = databuf;
- 		datalen = bufsize;
- 	}
--	strcpy(namebuf, xattr_prefix_table[name_index]);
-+	strncpy(namebuf, xattr_prefix_table[name_index], XATTR_NAME_MAX);
- 	strncat(namebuf, EXT2_EXT_ATTR_NAME(entry), entry->e_name_len);
- 	if (name_len + datalen > BTRFS_LEAF_DATA_SIZE(root) -
- 	    sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) {
-@@ -1127,7 +1120,7 @@ fail:
- 	return ret;
- }
- /*
-- * scan ext2's inode bitmap and copy all used inode.
-+ * scan ext2's inode bitmap and copy all used inodes.
-  */
- static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs,
- 		       int datacsum, int packing, int noxattr)
-@@ -1511,66 +1504,6 @@ fail:
- 	return new_root;
- }
- 
--/*
-- * Fixup block accounting. The initial block accounting created by
-- * make_block_groups isn't accuracy in this case.
-- */
--static int fixup_block_accounting(struct btrfs_trans_handle *trans,
--				  struct btrfs_root *root)
--{
--	int ret;
--	int slot;
--	u64 start = 0;
--	u64 bytes_used = 0;
--	struct btrfs_path path;
--	struct btrfs_key key;
--	struct extent_buffer *leaf;
--	struct btrfs_block_group_cache *cache;
--	struct btrfs_fs_info *fs_info = root->fs_info;
--
--	while(1) {
--		cache = btrfs_lookup_block_group(fs_info, start);
--		if (!cache)
--			break;
--		start = cache->key.objectid + cache->key.offset;
--		btrfs_set_block_group_used(&cache->item, 0);
--		cache->space_info->bytes_used = 0;
--	}
--
--	btrfs_init_path(&path);
--	key.offset = 0;
--	key.objectid = 0;
--	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
--	ret = btrfs_search_slot(trans, root->fs_info->extent_root,
--				&key, &path, 0, 0);
--	if (ret < 0)
--		return ret;
--	while(1) {
--		leaf = path.nodes[0];
--		slot = path.slots[0];
--		if (slot >= btrfs_header_nritems(leaf)) {
--			ret = btrfs_next_leaf(root, &path);
--			if (ret < 0)
--				return ret;
--			if (ret > 0)
--				break;
--			leaf = path.nodes[0];
--			slot = path.slots[0];
--		}
--		btrfs_item_key_to_cpu(leaf, &key, slot);
--		if (key.type == BTRFS_EXTENT_ITEM_KEY) {
--			bytes_used += key.offset;
--			ret = btrfs_update_block_group(trans, root,
--				  key.objectid, key.offset, 1, 0);
--			BUG_ON(ret);
--		}
--		path.slots[0]++;
--	}
--	btrfs_set_super_bytes_used(&root->fs_info->super_copy, bytes_used);
--	btrfs_release_path(root, &path);
--	return 0;
--}
--
- static int create_chunk_mapping(struct btrfs_trans_handle *trans,
- 				struct btrfs_root *root)
- {
-@@ -1742,7 +1675,7 @@ static int init_btrfs(struct btrfs_root *root)
- 	ret = btrfs_make_block_groups(trans, root);
- 	if (ret)
- 		goto err;
--	ret = fixup_block_accounting(trans, root);
-+	ret = btrfs_fix_block_accounting(trans, root);
- 	if (ret)
- 		goto err;
- 	ret = create_chunk_mapping(trans, root);
-diff --git a/ctree.c b/ctree.c
-index f70e10c..2d86b1e 100644
---- a/ctree.c
-+++ b/ctree.c
-@@ -19,6 +19,7 @@
- #include "disk-io.h"
- #include "transaction.h"
- #include "print-tree.h"
-+#include "repair.h"
- 
- static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root
- 		      *root, struct btrfs_path *path, int level);
-@@ -32,8 +33,6 @@ static int balance_node_right(struct btrfs_trans_handle *trans,
- 			      struct btrfs_root *root,
- 			      struct extent_buffer *dst_buf,
- 			      struct extent_buffer *src_buf);
--static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
--		   struct btrfs_path *path, int level, int slot);
- 
- inline void btrfs_init_path(struct btrfs_path *p)
- {
-@@ -138,6 +137,48 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
- 	return 0;
- }
- 
-+int btrfs_fsck_reinit_root(struct btrfs_trans_handle *trans,
-+		      struct btrfs_root *root)
-+{
-+	struct extent_buffer *c;
-+	struct extent_buffer *old = root->node;
-+	int level;
-+	struct btrfs_disk_key disk_key = {0,0,0};
-+
-+	level = 0;
-+
-+	c = btrfs_alloc_free_block(trans, root,
-+				   btrfs_level_size(root, 0),
-+				   root->root_key.objectid,
-+				   &disk_key, level, 0, 0);
-+	if (IS_ERR(c)) {
-+		c = old;
-+		extent_buffer_get(c);
-+	}
-+
-+	memset_extent_buffer(c, 0, 0, sizeof(struct btrfs_header));
-+	btrfs_set_header_level(c, level);
-+	btrfs_set_header_bytenr(c, c->start);
-+	btrfs_set_header_generation(c, trans->transid);
-+	btrfs_set_header_backref_rev(c, BTRFS_MIXED_BACKREF_REV);
-+	btrfs_set_header_owner(c, root->root_key.objectid);
-+
-+	write_extent_buffer(c, root->fs_info->fsid,
-+			    (unsigned long)btrfs_header_fsid(c),
-+			    BTRFS_FSID_SIZE);
-+
-+	write_extent_buffer(c, root->fs_info->chunk_tree_uuid,
-+			    (unsigned long)btrfs_header_chunk_tree_uuid(c),
-+			    BTRFS_UUID_SIZE);
-+
-+	btrfs_mark_buffer_dirty(c);
-+
-+	free_extent_buffer(old);
-+	root->node = c;
-+	add_root_to_dirty_list(root);
-+	return 0;
-+}
-+
- /*
-  * check if the tree block can be shared by multiple trees
-  */
-@@ -262,7 +303,6 @@ int __btrfs_cow_block(struct btrfs_trans_handle *trans,
- 			     struct extent_buffer **cow_ret,
- 			     u64 search_start, u64 empty_size)
- {
--	u64 generation;
- 	struct extent_buffer *cow;
- 	struct btrfs_disk_key disk_key;
- 	int level;
-@@ -272,7 +312,6 @@ int __btrfs_cow_block(struct btrfs_trans_handle *trans,
- 	WARN_ON(root->ref_cows && trans->transid != root->last_trans);
- 
- 	level = btrfs_header_level(buf);
--	generation = btrfs_header_generation(buf);
- 
- 	if (level == 0)
- 		btrfs_item_key(buf, &disk_key, 0);
-@@ -551,156 +590,125 @@ static inline unsigned int leaf_data_end(struct btrfs_root *root,
- 	return btrfs_item_offset_nr(leaf, nr - 1);
- }
- 
--static int check_node(struct btrfs_root *root, struct btrfs_path *path,
--		      int level)
-+int btrfs_check_node(struct btrfs_root *root,
-+		      struct btrfs_disk_key *parent_key,
-+		      struct extent_buffer *buf)
- {
--	struct extent_buffer *parent = NULL;
--	struct extent_buffer *node = path->nodes[level];
--	struct btrfs_disk_key parent_key;
--	struct btrfs_disk_key node_key;
--	int parent_slot;
--	int slot;
-+	int i;
- 	struct btrfs_key cpukey;
--	u32 nritems = btrfs_header_nritems(node);
-+	struct btrfs_disk_key key;
-+	u32 nritems = btrfs_header_nritems(buf);
- 
--	if (path->nodes[level + 1])
--		parent = path->nodes[level + 1];
-+	if (nritems == 0 || nritems > BTRFS_NODEPTRS_PER_BLOCK(root))
-+		goto fail;
- 
--	slot = path->slots[level];
--	BUG_ON(nritems == 0);
--	if (parent) {
--		parent_slot = path->slots[level + 1];
--		btrfs_node_key(parent, &parent_key, parent_slot);
--		btrfs_node_key(node, &node_key, 0);
--		BUG_ON(memcmp(&parent_key, &node_key,
--			      sizeof(struct btrfs_disk_key)));
--		BUG_ON(btrfs_node_blockptr(parent, parent_slot) !=
--		       btrfs_header_bytenr(node));
--	}
--	BUG_ON(nritems > BTRFS_NODEPTRS_PER_BLOCK(root));
--	if (slot != 0) {
--		btrfs_node_key_to_cpu(node, &cpukey, slot - 1);
--		btrfs_node_key(node, &node_key, slot);
--		BUG_ON(btrfs_comp_keys(&node_key, &cpukey) <= 0);
--	}
--	if (slot < nritems - 1) {
--		btrfs_node_key_to_cpu(node, &cpukey, slot + 1);
--		btrfs_node_key(node, &node_key, slot);
--		BUG_ON(btrfs_comp_keys(&node_key, &cpukey) >= 0);
-+	if (parent_key && parent_key->type) {
-+		btrfs_node_key(buf, &key, 0);
-+		if (memcmp(parent_key, &key, sizeof(key)))
-+			goto fail;
-+	}
-+	for (i = 0; nritems > 1 && i < nritems - 2; i++) {
-+		btrfs_node_key(buf, &key, i);
-+		btrfs_node_key_to_cpu(buf, &cpukey, i + 1);
-+		if (btrfs_comp_keys(&key, &cpukey) >= 0)
-+			goto fail;
- 	}
- 	return 0;
-+fail:
-+	if (btrfs_header_owner(buf) == BTRFS_EXTENT_TREE_OBJECTID) {
-+		if (parent_key)
-+			btrfs_disk_key_to_cpu(&cpukey, parent_key);
-+		else
-+			btrfs_node_key_to_cpu(buf, &cpukey, 0);
-+		btrfs_add_corrupt_extent_record(root->fs_info, &cpukey,
-+						buf->start, buf->len,
-+						btrfs_header_level(buf));
-+	}
-+	return -EIO;
- }
- 
--static int check_leaf(struct btrfs_root *root, struct btrfs_path *path,
--		      int level)
-+int btrfs_check_leaf(struct btrfs_root *root,
-+		      struct btrfs_disk_key *parent_key,
-+		      struct extent_buffer *buf)
- {
--	struct extent_buffer *leaf = path->nodes[level];
--	struct extent_buffer *parent = NULL;
--	int parent_slot;
-+	int i;
- 	struct btrfs_key cpukey;
--	struct btrfs_disk_key parent_key;
--	struct btrfs_disk_key leaf_key;
--	int slot = path->slots[0];
--
--	u32 nritems = btrfs_header_nritems(leaf);
-+	struct btrfs_disk_key key;
-+	u32 nritems = btrfs_header_nritems(buf);
- 
--	if (path->nodes[level + 1])
--		parent = path->nodes[level + 1];
-+	if (btrfs_header_level(buf) != 0) {
-+		fprintf(stderr, "leaf is not a leaf %llu\n",
-+		       (unsigned long long)btrfs_header_bytenr(buf));
-+		goto fail;
-+	}
-+	if (btrfs_leaf_free_space(root, buf) < 0) {
-+		fprintf(stderr, "leaf free space incorrect %llu %d\n",
-+			(unsigned long long)btrfs_header_bytenr(buf),
-+			btrfs_leaf_free_space(root, buf));
-+		goto fail;
-+	}
- 
- 	if (nritems == 0)
- 		return 0;
- 
--	if (parent) {
--		parent_slot = path->slots[level + 1];
--		btrfs_node_key(parent, &parent_key, parent_slot);
--		btrfs_item_key(leaf, &leaf_key, 0);
--
--		BUG_ON(memcmp(&parent_key, &leaf_key,
--		       sizeof(struct btrfs_disk_key)));
--		BUG_ON(btrfs_node_blockptr(parent, parent_slot) !=
--		       btrfs_header_bytenr(leaf));
-+	btrfs_item_key(buf, &key, 0);
-+	if (parent_key && parent_key->type &&
-+	    memcmp(parent_key, &key, sizeof(key))) {
-+		fprintf(stderr, "leaf parent key incorrect %llu\n",
-+		       (unsigned long long)btrfs_header_bytenr(buf));
-+		goto fail;
- 	}
--#if 0
- 	for (i = 0; nritems > 1 && i < nritems - 2; i++) {
--		btrfs_item_key_to_cpu(leaf, &cpukey, i + 1);
--		btrfs_item_key(leaf, &leaf_key, i);
--		if (comp_keys(&leaf_key, &cpukey) >= 0) {
--			btrfs_print_leaf(root, leaf);
--			printk("slot %d offset bad key\n", i);
--			BUG_ON(1);
--		}
--		if (btrfs_item_offset_nr(leaf, i) !=
--			btrfs_item_end_nr(leaf, i + 1)) {
--			btrfs_print_leaf(root, leaf);
--			printk("slot %d offset bad\n", i);
--			BUG_ON(1);
--		}
--		if (i == 0) {
--			if (btrfs_item_offset_nr(leaf, i) +
--			       btrfs_item_size_nr(leaf, i) !=
--			       BTRFS_LEAF_DATA_SIZE(root)) {
--				btrfs_print_leaf(root, leaf);
--				printk("slot %d first offset bad\n", i);
--				BUG_ON(1);
--			}
--		}
--	}
--	if (nritems > 0) {
--		if (btrfs_item_size_nr(leaf, nritems - 1) > 4096) {
--				btrfs_print_leaf(root, leaf);
--				printk("slot %d bad size \n", nritems - 1);
--				BUG_ON(1);
--		}
--	}
--#endif
--	if (slot != 0 && slot < nritems - 1) {
--		btrfs_item_key(leaf, &leaf_key, slot);
--		btrfs_item_key_to_cpu(leaf, &cpukey, slot - 1);
--		if (btrfs_comp_keys(&leaf_key, &cpukey) <= 0) {
--			btrfs_print_leaf(root, leaf);
--			printk("slot %d offset bad key\n", slot);
--			BUG_ON(1);
-+		btrfs_item_key(buf, &key, i);
-+		btrfs_item_key_to_cpu(buf, &cpukey, i + 1);
-+		if (btrfs_comp_keys(&key, &cpukey) >= 0) {
-+			fprintf(stderr, "bad key ordering %d %d\n", i, i+1);
-+			goto fail;
- 		}
--		if (btrfs_item_offset_nr(leaf, slot - 1) !=
--		       btrfs_item_end_nr(leaf, slot)) {
--			btrfs_print_leaf(root, leaf);
--			printk("slot %d offset bad\n", slot);
--			BUG_ON(1);
-+		if (btrfs_item_offset_nr(buf, i) !=
-+			btrfs_item_end_nr(buf, i + 1)) {
-+			fprintf(stderr, "incorrect offsets %u %u\n",
-+				btrfs_item_offset_nr(buf, i),
-+				btrfs_item_end_nr(buf, i + 1));
-+			goto fail;
- 		}
--	}
--	if (slot < nritems - 1) {
--		btrfs_item_key(leaf, &leaf_key, slot);
--		btrfs_item_key_to_cpu(leaf, &cpukey, slot + 1);
--		BUG_ON(btrfs_comp_keys(&leaf_key, &cpukey) >= 0);
--		if (btrfs_item_offset_nr(leaf, slot) !=
--			btrfs_item_end_nr(leaf, slot + 1)) {
--			btrfs_print_leaf(root, leaf);
--			printk("slot %d offset bad\n", slot);
--			BUG_ON(1);
-+		if (i == 0 && btrfs_item_end_nr(buf, i) !=
-+		    BTRFS_LEAF_DATA_SIZE(root)) {
-+			fprintf(stderr, "bad item end %u wanted %u\n",
-+				btrfs_item_end_nr(buf, i),
-+				(unsigned)BTRFS_LEAF_DATA_SIZE(root));
-+			goto fail;
- 		}
- 	}
--	BUG_ON(btrfs_item_offset_nr(leaf, 0) +
--	       btrfs_item_size_nr(leaf, 0) != BTRFS_LEAF_DATA_SIZE(root));
- 	return 0;
-+fail:
-+	if (btrfs_header_owner(buf) == BTRFS_EXTENT_TREE_OBJECTID) {
-+		if (parent_key)
-+			btrfs_disk_key_to_cpu(&cpukey, parent_key);
-+		else
-+			btrfs_item_key_to_cpu(buf, &cpukey, 0);
-+
-+		btrfs_add_corrupt_extent_record(root->fs_info, &cpukey,
-+						buf->start, buf->len, 0);
-+	}
-+	return -EIO;
- }
- 
- static int noinline check_block(struct btrfs_root *root,
- 				struct btrfs_path *path, int level)
- {
--	return 0;
--#if 0
--	struct extent_buffer *buf = path->nodes[level];
-+	struct btrfs_disk_key key;
-+	struct btrfs_disk_key *key_ptr = NULL;
-+	struct extent_buffer *parent;
- 
--	if (memcmp_extent_buffer(buf, root->fs_info->fsid,
--				 (unsigned long)btrfs_header_fsid(buf),
--				 BTRFS_FSID_SIZE)) {
--		printk("warning bad block %Lu\n", buf->start);
--		return 1;
-+	if (path->nodes[level + 1]) {
-+		parent = path->nodes[level + 1];
-+		btrfs_node_key(parent, &key, path->slots[level + 1]);
-+		key_ptr = &key;
- 	}
--#endif
- 	if (level == 0)
--		return check_leaf(root, path, level);
--	return check_node(root, path, level);
-+		return btrfs_check_leaf(root, key_ptr, path->nodes[0]);
-+	return btrfs_check_node(root, key_ptr, path->nodes[level]);
- }
- 
- /*
-@@ -767,7 +775,7 @@ static int bin_search(struct extent_buffer *eb, struct btrfs_key *key,
- 	return -1;
- }
- 
--static struct extent_buffer *read_node_slot(struct btrfs_root *root,
-+struct extent_buffer *read_node_slot(struct btrfs_root *root,
- 				   struct extent_buffer *parent, int slot)
- {
- 	int level = btrfs_header_level(parent);
-@@ -795,7 +803,6 @@ static int balance_level(struct btrfs_trans_handle *trans,
- 	int wret;
- 	int pslot;
- 	int orig_slot = path->slots[level];
--	int err_on_enospc = 0;
- 	u64 orig_ptr;
- 
- 	if (level == 0)
-@@ -845,9 +852,6 @@ static int balance_level(struct btrfs_trans_handle *trans,
- 	    BTRFS_NODEPTRS_PER_BLOCK(root) / 4)
- 		return 0;
- 
--	if (btrfs_header_nritems(mid) < 2)
--		err_on_enospc = 1;
--
- 	left = read_node_slot(root, parent, pslot - 1);
- 	if (left) {
- 		wret = btrfs_cow_block(trans, root, left,
-@@ -873,8 +877,6 @@ static int balance_level(struct btrfs_trans_handle *trans,
- 		wret = push_node_left(trans, root, left, mid, 1);
- 		if (wret < 0)
- 			ret = wret;
--		if (btrfs_header_nritems(mid) < 2)
--			err_on_enospc = 1;
- 	}
- 
- 	/*
-@@ -892,8 +894,8 @@ static int balance_level(struct btrfs_trans_handle *trans,
- 			wait_on_tree_block_writeback(root, right);
- 			free_extent_buffer(right);
- 			right = NULL;
--			wret = del_ptr(trans, root, path, level + 1, pslot +
--				       1);
-+			wret = btrfs_del_ptr(trans, root, path,
-+					     level + 1, pslot + 1);
- 			if (wret)
- 				ret = wret;
- 			wret = btrfs_free_extent(trans, root, bytenr,
-@@ -940,7 +942,7 @@ static int balance_level(struct btrfs_trans_handle *trans,
- 		wait_on_tree_block_writeback(root, mid);
- 		free_extent_buffer(mid);
- 		mid = NULL;
--		wret = del_ptr(trans, root, path, level + 1, pslot);
-+		wret = btrfs_del_ptr(trans, root, path, level + 1, pslot);
- 		if (wret)
- 			ret = wret;
- 		wret = btrfs_free_extent(trans, root, bytenr, blocksize,
-@@ -996,14 +998,12 @@ static int noinline push_nodes_for_insert(struct btrfs_trans_handle *trans,
- 	int wret;
- 	int pslot;
- 	int orig_slot = path->slots[level];
--	u64 orig_ptr;
- 
- 	if (level == 0)
- 		return 1;
- 
- 	mid = path->nodes[level];
- 	WARN_ON(btrfs_header_generation(mid) != trans->transid);
--	orig_ptr = btrfs_node_blockptr(mid, orig_slot);
- 
- 	if (level < BTRFS_MAX_LEVEL - 1)
- 		parent = path->nodes[level + 1];
-@@ -1102,7 +1102,7 @@ static int noinline push_nodes_for_insert(struct btrfs_trans_handle *trans,
- /*
-  * readahead one full node of leaves
-  */
--static void reada_for_search(struct btrfs_root *root, struct btrfs_path *path,
-+void reada_for_search(struct btrfs_root *root, struct btrfs_path *path,
- 			     int level, int slot, u64 objectid)
- {
- 	struct extent_buffer *node;
-@@ -1199,7 +1199,7 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
- 	u8 lowest_level = 0;
- 
- 	lowest_level = p->lowest_level;
--	WARN_ON(lowest_level && ins_len);
-+	WARN_ON(lowest_level && ins_len > 0);
- 	WARN_ON(p->nodes[0] != NULL);
- 	/*
- 	WARN_ON(!mutex_is_locked(&root->fs_info->fs_mutex));
-@@ -1264,6 +1264,8 @@ again:
- 						 key->objectid);
- 
- 			b = read_node_slot(root, b, slot);
-+			if (!extent_buffer_uptodate(b))
-+				return -EIO;
- 		} else {
- 			p->slots[level] = slot;
- 			if (ins_len > 0 &&
-@@ -2370,7 +2372,6 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,
- {
- 	int ret = 0;
- 	int slot;
--	int slot_orig;
- 	struct extent_buffer *leaf;
- 	struct btrfs_item *item;
- 	u32 nritems;
-@@ -2380,7 +2381,6 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,
- 	unsigned int size_diff;
- 	int i;
- 
--	slot_orig = path->slots[0];
- 	leaf = path->nodes[0];
- 	slot = path->slots[0];
- 
-@@ -2468,7 +2468,6 @@ int btrfs_extend_item(struct btrfs_trans_handle *trans,
- {
- 	int ret = 0;
- 	int slot;
--	int slot_orig;
- 	struct extent_buffer *leaf;
- 	struct btrfs_item *item;
- 	u32 nritems;
-@@ -2477,7 +2476,6 @@ int btrfs_extend_item(struct btrfs_trans_handle *trans,
- 	unsigned int old_size;
- 	int i;
- 
--	slot_orig = path->slots[0];
- 	leaf = path->nodes[0];
- 
- 	nritems = btrfs_header_nritems(leaf);
-@@ -2541,7 +2539,6 @@ int btrfs_insert_empty_items(struct btrfs_trans_handle *trans,
- 	struct btrfs_item *item;
- 	int ret = 0;
- 	int slot;
--	int slot_orig;
- 	int i;
- 	u32 nritems;
- 	u32 total_size = 0;
-@@ -2565,7 +2562,6 @@ int btrfs_insert_empty_items(struct btrfs_trans_handle *trans,
- 	if (ret < 0)
- 		goto out;
- 
--	slot_orig = path->slots[0];
- 	leaf = path->nodes[0];
- 
- 	nritems = btrfs_header_nritems(leaf);
-@@ -2675,7 +2671,7 @@ int btrfs_insert_item(struct btrfs_trans_handle *trans, struct btrfs_root
-  * continuing all the way the root if required.  The root is converted into
-  * a leaf if all the nodes are emptied.
-  */
--static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
-+int btrfs_del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
- 		   struct btrfs_path *path, int level, int slot)
- {
- 	struct extent_buffer *parent = path->nodes[level];
-@@ -2727,7 +2723,7 @@ static noinline int btrfs_del_leaf(struct btrfs_trans_handle *trans,
- 	int ret;
- 
- 	WARN_ON(btrfs_header_generation(leaf) != trans->transid);
--	ret = del_ptr(trans, root, path, 1, path->slots[1]);
-+	ret = btrfs_del_ptr(trans, root, path, 1, path->slots[1]);
- 	if (ret)
- 		return ret;
- 
-@@ -2931,6 +2927,8 @@ int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path)
- 			reada_for_search(root, path, level, slot, 0);
- 
- 		next = read_node_slot(root, c, slot);
-+		if (!next)
-+			return -EIO;
- 		break;
- 	}
- 	path->slots[level] = slot;
-@@ -2945,6 +2943,8 @@ int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path)
- 		if (path->reada)
- 			reada_for_search(root, path, level, 0, 0);
- 		next = read_node_slot(root, next, 0);
-+		if (!next)
-+			return -EIO;
- 	}
- 	return 0;
- }
-diff --git a/ctree.h b/ctree.h
-index a9062ea..6545c50 100644
---- a/ctree.h
-+++ b/ctree.h
-@@ -24,6 +24,7 @@
- #include "radix-tree.h"
- #include "extent-cache.h"
- #include "extent_io.h"
-+#include "ioctl.h"
- 
- struct btrfs_root;
- struct btrfs_trans_handle;
-@@ -60,6 +61,9 @@ struct btrfs_trans_handle;
- #define BTRFS_CSUM_TREE_OBJECTID 7ULL
- 
- 
-+/* for storing balance parameters in the root tree */
-+#define BTRFS_BALANCE_OBJECTID -4ULL
-+
- /* oprhan objectid for tracking unlinked/truncated files */
- #define BTRFS_ORPHAN_OBJECTID -5ULL
- 
-@@ -78,6 +82,15 @@ struct btrfs_trans_handle;
-  */
- #define BTRFS_EXTENT_CSUM_OBJECTID -10ULL
- 
-+/* For storing free space cache */
-+#define BTRFS_FREE_SPACE_OBJECTID -11ULL
-+
-+/*
-+ * The inode number assigned to the special inode for sotring
-+ * free ino cache
-+ */
-+#define BTRFS_FREE_INO_OBJECTID -12ULL
-+
- /* dummy objectid represents multiple objectids */
- #define BTRFS_MULTIPLE_OBJECTIDS -255ULL
- 
-@@ -250,7 +263,6 @@ static inline unsigned long btrfs_chunk_item_size(int num_stripes)
- 		sizeof(struct btrfs_stripe) * (num_stripes - 1);
- }
- 
--#define BTRFS_FSID_SIZE 16
- #define BTRFS_HEADER_FLAG_WRITTEN		(1ULL << 0)
- #define BTRFS_HEADER_FLAG_RELOC			(1ULL << 1)
- #define BTRFS_SUPER_FLAG_SEEDING		(1ULL << 32)
-@@ -290,6 +302,9 @@ struct btrfs_header {
- #define BTRFS_MAX_INLINE_DATA_SIZE(r) (BTRFS_LEAF_DATA_SIZE(r) - \
- 					sizeof(struct btrfs_item) - \
- 					sizeof(struct btrfs_file_extent_item))
-+#define BTRFS_MAX_XATTR_SIZE(r)	(BTRFS_LEAF_DATA_SIZE(r) - \
-+				 sizeof(struct btrfs_item) -\
-+				 sizeof(struct btrfs_dir_item))
- 
- 
- /*
-@@ -300,6 +315,47 @@ struct btrfs_header {
- #define BTRFS_LABEL_SIZE 256
- 
- /*
-+ * just in case we somehow lose the roots and are not able to mount,
-+ * we store an array of the roots from previous transactions
-+ * in the super.
-+ */
-+#define BTRFS_NUM_BACKUP_ROOTS 4
-+struct btrfs_root_backup {
-+	__le64 tree_root;
-+	__le64 tree_root_gen;
-+
-+	__le64 chunk_root;
-+	__le64 chunk_root_gen;
-+
-+	__le64 extent_root;
-+	__le64 extent_root_gen;
-+
-+	__le64 fs_root;
-+	__le64 fs_root_gen;
-+
-+	__le64 dev_root;
-+	__le64 dev_root_gen;
-+
-+	__le64 csum_root;
-+	__le64 csum_root_gen;
-+
-+	__le64 total_bytes;
-+	__le64 bytes_used;
-+	__le64 num_devices;
-+	/* future */
-+	__le64 unsed_64[4];
-+
-+	u8 tree_root_level;
-+	u8 chunk_root_level;
-+	u8 extent_root_level;
-+	u8 fs_root_level;
-+	u8 dev_root_level;
-+	u8 csum_root_level;
-+	/* future and to align */
-+	u8 unused_8[10];
-+} __attribute__ ((__packed__));
-+
-+/*
-  * the super block basically lists the main trees of the FS
-  * it currently lacks any block count etc etc
-  */
-@@ -340,9 +396,12 @@ struct btrfs_super_block {
- 
- 	char label[BTRFS_LABEL_SIZE];
- 
-+	__le64 cache_generation;
-+
- 	/* future expansion */
--	__le64 reserved[32];
-+	__le64 reserved[31];
- 	u8 sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE];
-+	struct btrfs_root_backup super_roots[BTRFS_NUM_BACKUP_ROOTS];
- } __attribute__ ((__packed__));
- 
- /*
-@@ -350,11 +409,32 @@ struct btrfs_super_block {
-  * ones specified below then we will fail to mount
-  */
- #define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF	(1ULL << 0)
-+#define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL	(1ULL << 1)
-+#define BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS	(1ULL << 2)
-+#define BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO	(1ULL << 3)
-+/*
-+ * some patches floated around with a second compression method
-+ * lets save that incompat here for when they do get in
-+ * Note we don't actually support it, we're just reserving the
-+ * number
-+ */
-+#define BTRFS_FEATURE_INCOMPAT_COMPRESS_LZOv2   (1ULL << 4)
-+
-+/*
-+ * older kernels tried to do bigger metadata blocks, but the
-+ * code was pretty buggy.  Lets not let them try anymore.
-+ */
-+#define BTRFS_FEATURE_INCOMPAT_BIG_METADATA     (1ULL << 5)
-+
- 
- #define BTRFS_FEATURE_COMPAT_SUPP		0ULL
- #define BTRFS_FEATURE_COMPAT_RO_SUPP		0ULL
--#define BTRFS_FEATURE_INCOMPAT_SUPP		\
--	BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF
-+#define BTRFS_FEATURE_INCOMPAT_SUPP			\
-+	(BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF |		\
-+	 BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL |	\
-+	 BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO |		\
-+	 BTRFS_FEATURE_INCOMPAT_BIG_METADATA |		\
-+	 BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS)
- 
- /*
-  * A leaf is full of items. offset and size tell us where to find
-@@ -499,9 +579,11 @@ struct btrfs_timespec {
- } __attribute__ ((__packed__));
- 
- typedef enum {
--	BTRFS_COMPRESS_NONE = 0,
--	BTRFS_COMPRESS_ZLIB = 1,
--	BTRFS_COMPRESS_LAST = 2,
-+	BTRFS_COMPRESS_NONE  = 0,
-+	BTRFS_COMPRESS_ZLIB  = 1,
-+	BTRFS_COMPRESS_LZO   = 2,
-+	BTRFS_COMPRESS_TYPES = 2,
-+	BTRFS_COMPRESS_LAST  = 3,
- } btrfs_compression_type;
- 
- /* we don't understand any encryption methods right now */
-@@ -633,13 +715,17 @@ struct btrfs_csum_item {
- } __attribute__ ((__packed__));
- 
- /* tag for the radix tree of block groups in ram */
--#define BTRFS_BLOCK_GROUP_DATA     (1 << 0)
--#define BTRFS_BLOCK_GROUP_SYSTEM   (1 << 1)
--#define BTRFS_BLOCK_GROUP_METADATA (1 << 2)
--#define BTRFS_BLOCK_GROUP_RAID0    (1 << 3)
--#define BTRFS_BLOCK_GROUP_RAID1    (1 << 4)
--#define BTRFS_BLOCK_GROUP_DUP	   (1 << 5)
--#define BTRFS_BLOCK_GROUP_RAID10   (1 << 6)
-+#define BTRFS_BLOCK_GROUP_DATA		(1ULL << 0)
-+#define BTRFS_BLOCK_GROUP_SYSTEM	(1ULL << 1)
-+#define BTRFS_BLOCK_GROUP_METADATA	(1ULL << 2)
-+#define BTRFS_BLOCK_GROUP_RAID0		(1ULL << 3)
-+#define BTRFS_BLOCK_GROUP_RAID1		(1ULL << 4)
-+#define BTRFS_BLOCK_GROUP_DUP		(1ULL << 5)
-+#define BTRFS_BLOCK_GROUP_RAID10	(1ULL << 6)
-+#define BTRFS_BLOCK_GROUP_RESERVED	BTRFS_AVAIL_ALLOC_BIT_SINGLE
-+
-+/* used in struct btrfs_balance_args fields */
-+#define BTRFS_AVAIL_ALLOC_BIT_SINGLE	(1ULL << 48)
- 
- struct btrfs_block_group_item {
- 	__le64 used;
-@@ -726,6 +812,13 @@ struct btrfs_fs_info {
- 	struct list_head space_info;
- 	int system_allocs;
- 	int readonly;
-+	int (*free_extent_hook)(struct btrfs_trans_handle *trans,
-+				struct btrfs_root *root,
-+				u64 bytenr, u64 num_bytes, u64 parent,
-+				u64 root_objectid, u64 owner, u64 offset,
-+				int refs_to_drop);
-+	struct cache_tree *fsck_extent_cache;
-+	struct cache_tree *corrupt_blocks;
- };
- 
- /*
-@@ -847,6 +940,8 @@ struct btrfs_root {
- #define BTRFS_DEV_ITEM_KEY	216
- #define BTRFS_CHUNK_ITEM_KEY	228
- 
-+#define BTRFS_BALANCE_ITEM_KEY	248
-+
- /*
-  * string items are for debugging.  They just store a short string of
-  * data in the FS
-@@ -1047,6 +1142,7 @@ BTRFS_SETGET_STACK_FUNCS(block_group_flags,
- 
- /* struct btrfs_inode_ref */
- BTRFS_SETGET_FUNCS(inode_ref_name_len, struct btrfs_inode_ref, name_len, 16);
-+BTRFS_SETGET_STACK_FUNCS(stack_inode_ref_name_len, struct btrfs_inode_ref, name_len, 16);
- BTRFS_SETGET_FUNCS(inode_ref_index, struct btrfs_inode_ref, index, 64);
- 
- /* struct btrfs_inode_item */
-@@ -1325,6 +1421,10 @@ BTRFS_SETGET_FUNCS(root_ref_dirid, struct btrfs_root_ref, dirid, 64);
- BTRFS_SETGET_FUNCS(root_ref_sequence, struct btrfs_root_ref, sequence, 64);
- BTRFS_SETGET_FUNCS(root_ref_name_len, struct btrfs_root_ref, name_len, 16);
- 
-+BTRFS_SETGET_STACK_FUNCS(stack_root_ref_dirid, struct btrfs_root_ref, dirid, 64);
-+BTRFS_SETGET_STACK_FUNCS(stack_root_ref_sequence, struct btrfs_root_ref, sequence, 64);
-+BTRFS_SETGET_STACK_FUNCS(stack_root_ref_name_len, struct btrfs_root_ref, name_len, 16);
-+
- /* struct btrfs_dir_item */
- BTRFS_SETGET_FUNCS(dir_data_len, struct btrfs_dir_item, data_len, 16);
- BTRFS_SETGET_FUNCS(dir_type, struct btrfs_dir_item, type, 8);
-@@ -1510,6 +1610,55 @@ BTRFS_SETGET_STACK_FUNCS(root_last_snapshot, struct btrfs_root_item,
- 			 last_snapshot, 64);
- 
- 
-+/* struct btrfs_root_backup */
-+BTRFS_SETGET_STACK_FUNCS(backup_tree_root, struct btrfs_root_backup,
-+		   tree_root, 64);
-+BTRFS_SETGET_STACK_FUNCS(backup_tree_root_gen, struct btrfs_root_backup,
-+		   tree_root_gen, 64);
-+BTRFS_SETGET_STACK_FUNCS(backup_tree_root_level, struct btrfs_root_backup,
-+		   tree_root_level, 8);
-+
-+BTRFS_SETGET_STACK_FUNCS(backup_chunk_root, struct btrfs_root_backup,
-+		   chunk_root, 64);
-+BTRFS_SETGET_STACK_FUNCS(backup_chunk_root_gen, struct btrfs_root_backup,
-+		   chunk_root_gen, 64);
-+BTRFS_SETGET_STACK_FUNCS(backup_chunk_root_level, struct btrfs_root_backup,
-+		   chunk_root_level, 8);
-+
-+BTRFS_SETGET_STACK_FUNCS(backup_extent_root, struct btrfs_root_backup,
-+		   extent_root, 64);
-+BTRFS_SETGET_STACK_FUNCS(backup_extent_root_gen, struct btrfs_root_backup,
-+		   extent_root_gen, 64);
-+BTRFS_SETGET_STACK_FUNCS(backup_extent_root_level, struct btrfs_root_backup,
-+		   extent_root_level, 8);
-+
-+BTRFS_SETGET_STACK_FUNCS(backup_fs_root, struct btrfs_root_backup,
-+		   fs_root, 64);
-+BTRFS_SETGET_STACK_FUNCS(backup_fs_root_gen, struct btrfs_root_backup,
-+		   fs_root_gen, 64);
-+BTRFS_SETGET_STACK_FUNCS(backup_fs_root_level, struct btrfs_root_backup,
-+		   fs_root_level, 8);
-+
-+BTRFS_SETGET_STACK_FUNCS(backup_dev_root, struct btrfs_root_backup,
-+		   dev_root, 64);
-+BTRFS_SETGET_STACK_FUNCS(backup_dev_root_gen, struct btrfs_root_backup,
-+		   dev_root_gen, 64);
-+BTRFS_SETGET_STACK_FUNCS(backup_dev_root_level, struct btrfs_root_backup,
-+		   dev_root_level, 8);
-+
-+BTRFS_SETGET_STACK_FUNCS(backup_csum_root, struct btrfs_root_backup,
-+		   csum_root, 64);
-+BTRFS_SETGET_STACK_FUNCS(backup_csum_root_gen, struct btrfs_root_backup,
-+		   csum_root_gen, 64);
-+BTRFS_SETGET_STACK_FUNCS(backup_csum_root_level, struct btrfs_root_backup,
-+		   csum_root_level, 8);
-+BTRFS_SETGET_STACK_FUNCS(backup_total_bytes, struct btrfs_root_backup,
-+		   total_bytes, 64);
-+BTRFS_SETGET_STACK_FUNCS(backup_bytes_used, struct btrfs_root_backup,
-+		   bytes_used, 64);
-+BTRFS_SETGET_STACK_FUNCS(backup_num_devices, struct btrfs_root_backup,
-+		   num_devices, 64);
-+
- /* struct btrfs_super_block */
- 
- BTRFS_SETGET_STACK_FUNCS(super_bytenr, struct btrfs_super_block, bytenr, 64);
-@@ -1557,6 +1706,8 @@ BTRFS_SETGET_STACK_FUNCS(super_incompat_flags, struct btrfs_super_block,
- 			 incompat_flags, 64);
- BTRFS_SETGET_STACK_FUNCS(super_csum_type, struct btrfs_super_block,
- 			 csum_type, 16);
-+BTRFS_SETGET_STACK_FUNCS(super_cache_generation, struct btrfs_super_block,
-+			 cache_generation, 64);
- 
- static inline int btrfs_super_csum_size(struct btrfs_super_block *s)
- {
-@@ -1572,6 +1723,7 @@ static inline unsigned long btrfs_leaf_data(struct extent_buffer *l)
- 
- /* struct btrfs_file_extent_item */
- BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, 8);
-+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_type, struct btrfs_file_extent_item, type, 8);
- 
- static inline unsigned long btrfs_file_extent_inline_start(struct
- 						   btrfs_file_extent_item *e)
-@@ -1588,18 +1740,30 @@ static inline u32 btrfs_file_extent_calc_inline_size(u32 datasize)
- 
- BTRFS_SETGET_FUNCS(file_extent_disk_bytenr, struct btrfs_file_extent_item,
- 		   disk_bytenr, 64);
-+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_disk_bytenr, struct btrfs_file_extent_item,
-+		   disk_bytenr, 64);
- BTRFS_SETGET_FUNCS(file_extent_generation, struct btrfs_file_extent_item,
- 		   generation, 64);
-+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_generation, struct btrfs_file_extent_item,
-+		   generation, 64);
- BTRFS_SETGET_FUNCS(file_extent_disk_num_bytes, struct btrfs_file_extent_item,
- 		   disk_num_bytes, 64);
- BTRFS_SETGET_FUNCS(file_extent_offset, struct btrfs_file_extent_item,
- 		  offset, 64);
-+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_offset, struct btrfs_file_extent_item,
-+		  offset, 64);
- BTRFS_SETGET_FUNCS(file_extent_num_bytes, struct btrfs_file_extent_item,
- 		   num_bytes, 64);
-+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_num_bytes, struct btrfs_file_extent_item,
-+		   num_bytes, 64);
- BTRFS_SETGET_FUNCS(file_extent_ram_bytes, struct btrfs_file_extent_item,
- 		   ram_bytes, 64);
-+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_ram_bytes, struct btrfs_file_extent_item,
-+		   ram_bytes, 64);
- BTRFS_SETGET_FUNCS(file_extent_compression, struct btrfs_file_extent_item,
- 		   compression, 8);
-+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_compression, struct btrfs_file_extent_item,
-+		   compression, 8);
- BTRFS_SETGET_FUNCS(file_extent_encryption, struct btrfs_file_extent_item,
- 		   encryption, 8);
- BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item,
-@@ -1643,6 +1807,10 @@ static inline u32 btrfs_level_size(struct btrfs_root *root, int level) {
- 	btrfs_item_offset_nr(leaf, slot)))
- 
- /* extent-tree.c */
-+int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans,
-+				 struct btrfs_root *root);
-+int btrfs_check_block_accounting(struct btrfs_root *root);
-+void btrfs_pin_extent(struct btrfs_fs_info *fs_info, u64 bytenr, u64 num_bytes);
- int btrfs_extent_post_op(struct btrfs_trans_handle *trans,
- 			 struct btrfs_root *root);
- int btrfs_copy_pinned(struct btrfs_root *root, struct extent_io_tree *copy);
-@@ -1705,6 +1873,20 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans,
- 			     struct btrfs_root *root, u64 bytenr, u64 num,
- 			     int alloc, int mark_free);
- /* ctree.c */
-+int btrfs_del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
-+		   struct btrfs_path *path, int level, int slot);
-+int btrfs_check_node(struct btrfs_root *root,
-+		      struct btrfs_disk_key *parent_key,
-+		      struct extent_buffer *buf);
-+int btrfs_check_leaf(struct btrfs_root *root,
-+		      struct btrfs_disk_key *parent_key,
-+		      struct extent_buffer *buf);
-+int btrfs_fsck_reinit_root(struct btrfs_trans_handle *trans,
-+		      struct btrfs_root *root);
-+void reada_for_search(struct btrfs_root *root, struct btrfs_path *path,
-+			     int level, int slot, u64 objectid);
-+struct extent_buffer *read_node_slot(struct btrfs_root *root,
-+				   struct extent_buffer *parent, int slot);
- int btrfs_previous_item(struct btrfs_root *root,
- 			struct btrfs_path *path, u64 min_objectid,
- 			int type);
-diff --git a/debug-tree.c b/debug-tree.c
-index 1d47519..c497892 100644
---- a/debug-tree.c
-+++ b/debug-tree.c
-@@ -35,44 +35,6 @@ static int print_usage(void)
- 	exit(1);
- }
- 
--static void print_extent_leaf(struct btrfs_root *root, struct extent_buffer *l)
--{
--	int i;
--	struct btrfs_item *item;
--//	struct btrfs_extent_ref *ref;
--	struct btrfs_key key;
--	static u64 last = 0;
--	static u64 last_len = 0;
--	u32 nr = btrfs_header_nritems(l);
--	u32 type;
--
--	for (i = 0 ; i < nr ; i++) {
--		item = btrfs_item_nr(l, i);
--		btrfs_item_key_to_cpu(l, &key, i);
--		type = btrfs_key_type(&key);
--		switch (type) {
--		case BTRFS_EXTENT_ITEM_KEY:
--			last_len = key.offset;
--			last = key.objectid;
--			break;
--#if 0
--		case BTRFS_EXTENT_REF_KEY:
--			ref = btrfs_item_ptr(l, i, struct btrfs_extent_ref);
--			printf("%llu %llu extent back ref root %llu gen %llu "
--			       "owner %llu num_refs %lu\n",
--			       (unsigned long long)last,
--			       (unsigned long long)last_len,
--			       (unsigned long long)btrfs_ref_root(l, ref),
--			       (unsigned long long)btrfs_ref_generation(l, ref),
--			       (unsigned long long)btrfs_ref_objectid(l, ref),
--			       (unsigned long)btrfs_ref_num_refs(l, ref));
--			break;
--#endif
--		};
--		fflush(stdout);
--	}
--}
--
- static void print_extents(struct btrfs_root *root, struct extent_buffer *eb)
- {
- 	int i;
-@@ -81,10 +43,7 @@ static void print_extents(struct btrfs_root *root, struct extent_buffer *eb)
- 
- 	if (!eb)
- 		return;
--	if (btrfs_is_leaf(eb)) {
--		print_extent_leaf(root, eb);
--		return;
--	}
-+
- 	size = btrfs_level_size(root, btrfs_header_level(eb) - 1);
- 	nr = btrfs_header_nritems(eb);
- 	for (i = 0; i < nr; i++) {
-@@ -103,9 +62,49 @@ static void print_extents(struct btrfs_root *root, struct extent_buffer *eb)
- 	}
- }
- 
-+static void print_old_roots(struct btrfs_super_block *super)
-+{
-+	struct btrfs_root_backup *backup;
-+	int i;
-+
-+	for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS; i++) {
-+		backup = super->super_roots + i;
-+		printf("btrfs root backup slot %d\n", i);
-+		printf("\ttree root gen %llu block %llu\n",
-+		       (unsigned long long)btrfs_backup_tree_root_gen(backup),
-+		       (unsigned long long)btrfs_backup_tree_root(backup));
-+
-+		printf("\t\textent root gen %llu block %llu\n",
-+		       (unsigned long long)btrfs_backup_extent_root_gen(backup),
-+		       (unsigned long long)btrfs_backup_extent_root(backup));
-+
-+		printf("\t\tchunk root gen %llu block %llu\n",
-+		       (unsigned long long)btrfs_backup_chunk_root_gen(backup),
-+		       (unsigned long long)btrfs_backup_chunk_root(backup));
-+
-+		printf("\t\tdevice root gen %llu block %llu\n",
-+		       (unsigned long long)btrfs_backup_dev_root_gen(backup),
-+		       (unsigned long long)btrfs_backup_dev_root(backup));
-+
-+		printf("\t\tcsum root gen %llu block %llu\n",
-+		       (unsigned long long)btrfs_backup_csum_root_gen(backup),
-+		       (unsigned long long)btrfs_backup_csum_root(backup));
-+
-+		printf("\t\tfs root gen %llu block %llu\n",
-+		       (unsigned long long)btrfs_backup_fs_root_gen(backup),
-+		       (unsigned long long)btrfs_backup_fs_root(backup));
-+
-+		printf("\t\t%llu used %llu total %llu devices\n",
-+		       (unsigned long long)btrfs_backup_bytes_used(backup),
-+		       (unsigned long long)btrfs_backup_total_bytes(backup),
-+		       (unsigned long long)btrfs_backup_num_devices(backup));
-+	}
-+}
-+
- int main(int ac, char **av)
- {
- 	struct btrfs_root *root;
-+	struct btrfs_fs_info *info;
- 	struct btrfs_path path;
- 	struct btrfs_key key;
- 	struct btrfs_root_item ri;
-@@ -116,19 +115,36 @@ int main(int ac, char **av)
- 	int ret;
- 	int slot;
- 	int extent_only = 0;
-+	int device_only = 0;
-+	int roots_only = 0;
-+	int root_backups = 0;
-+	u64 block_only = 0;
- 	struct btrfs_root *tree_root_scan;
- 
- 	radix_tree_init();
- 
- 	while(1) {
- 		int c;
--		c = getopt(ac, av, "e");
-+		c = getopt(ac, av, "deb:rR");
- 		if (c < 0)
- 			break;
- 		switch(c) {
- 			case 'e':
- 				extent_only = 1;
- 				break;
-+			case 'd':
-+				device_only = 1;
-+				break;
-+			case 'r':
-+				roots_only = 1;
-+				break;
-+			case 'R':
-+				roots_only = 1;
-+				root_backups = 1;
-+				break;
-+			case 'b':
-+				block_only = atoll(optarg);
-+				break;
- 			default:
- 				print_usage();
- 		}
-@@ -137,24 +153,70 @@ int main(int ac, char **av)
- 	if (ac != 1)
- 		print_usage();
- 
--	root = open_ctree(av[optind], 0, 0);
--	if (!root) {
-+	info = open_ctree_fs_info(av[optind], 0, 0, 1);
-+	if (!info) {
- 		fprintf(stderr, "unable to open %s\n", av[optind]);
- 		exit(1);
- 	}
-+	root = info->fs_root;
-+
-+	if (block_only) {
-+		if (!root) {
-+			fprintf(stderr, "unable to open %s\n", av[optind]);
-+			exit(1);
-+		}
-+		leaf = read_tree_block(root,
-+				      block_only,
-+				      root->leafsize, 0);
-+
-+		if (leaf && btrfs_header_level(leaf) != 0) {
-+			free_extent_buffer(leaf);
-+			leaf = NULL;
-+		}
-+
-+		if (!leaf) {
-+			leaf = read_tree_block(root,
-+					      block_only,
-+					      root->nodesize, 0);
-+		}
-+		if (!leaf) {
-+			fprintf(stderr, "failed to read %llu\n",
-+				(unsigned long long)block_only);
-+			return 0;
-+		}
-+		btrfs_print_tree(root, leaf, 0);
-+		return 0;
-+	}
-+
- 	if (!extent_only) {
--		printf("root tree\n");
--		btrfs_print_tree(root->fs_info->tree_root,
--				 root->fs_info->tree_root->node);
-+		if (roots_only) {
-+			printf("root tree: %llu level %d\n",
-+			     (unsigned long long)info->tree_root->node->start,
-+			     btrfs_header_level(info->tree_root->node));
-+			printf("chunk tree: %llu level %d\n",
-+			     (unsigned long long)info->chunk_root->node->start,
-+			     btrfs_header_level(info->chunk_root->node));
-+		} else {
-+			if (info->tree_root->node) {
-+				printf("root tree\n");
-+				btrfs_print_tree(info->tree_root,
-+						 info->tree_root->node, 1);
-+			}
- 
--		printf("chunk tree\n");
--		btrfs_print_tree(root->fs_info->chunk_root,
--				 root->fs_info->chunk_root->node);
-+			if (info->chunk_root->node) {
-+				printf("chunk tree\n");
-+				btrfs_print_tree(info->chunk_root,
-+						 info->chunk_root->node, 1);
-+			}
-+		}
- 	}
--	tree_root_scan = root->fs_info->tree_root;
-+	tree_root_scan = info->tree_root;
- 
- 	btrfs_init_path(&path);
- again:
-+	if (!extent_buffer_uptodate(tree_root_scan->node))
-+		goto no_node;
-+
- 	key.offset = 0;
- 	key.objectid = 0;
- 	btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
-@@ -175,21 +237,27 @@ again:
- 		if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) {
- 			unsigned long offset;
- 			struct extent_buffer *buf;
--			int skip = extent_only;
-+			int skip = extent_only | device_only;
- 
- 			offset = btrfs_item_ptr_offset(leaf, slot);
- 			read_extent_buffer(leaf, &ri, offset, sizeof(ri));
- 			buf = read_tree_block(tree_root_scan,
- 					      btrfs_root_bytenr(&ri),
--					      tree_root_scan->leafsize, 0);
-+					      btrfs_level_size(tree_root_scan,
-+							btrfs_root_level(&ri)),
-+					      0);
-+			if (!extent_buffer_uptodate(buf))
-+				goto next;
-+
- 			switch(found_key.objectid) {
- 			case BTRFS_ROOT_TREE_OBJECTID:
- 				if (!skip)
- 					printf("root");
- 				break;
- 			case BTRFS_EXTENT_TREE_OBJECTID:
--				skip = 0;
--				if (!extent_only)
-+				if (!device_only)
-+					skip = 0;
-+				if (!extent_only && !device_only)
- 					printf("extent");
- 				break;
- 			case BTRFS_CHUNK_TREE_OBJECTID:
-@@ -198,9 +266,8 @@ again:
- 				}
- 				break;
- 			case BTRFS_DEV_TREE_OBJECTID:
--				if (!skip) {
--					printf("device");
--				}
-+				skip = 0;
-+				printf("device");
- 				break;
- 			case BTRFS_FS_TREE_OBJECTID:
- 				if (!skip) {
-@@ -208,9 +275,8 @@ again:
- 				}
- 				break;
- 			case BTRFS_ROOT_TREE_DIR_OBJECTID:
--				if (!skip) {
--					printf("directory");
--				}
-+				skip = 0;
-+				printf("directory");
- 				break;
- 			case BTRFS_CSUM_TREE_OBJECTID:
- 				if (!skip) {
-@@ -256,34 +322,45 @@ again:
- 					printf("file");
- 				}
- 			}
--			if (!skip && !extent_only) {
-+			if (extent_only && !skip) {
-+				print_extents(tree_root_scan, buf);
-+			} else if (!skip) {
- 				printf(" tree ");
- 				btrfs_print_key(&disk_key);
--				printf(" \n");
--				btrfs_print_tree(tree_root_scan, buf);
--			} else if (extent_only && !skip) {
--				print_extents(tree_root_scan, buf);
-+				if (roots_only) {
-+					printf(" %llu level %d\n",
-+					       (unsigned long long)buf->start,
-+					       btrfs_header_level(buf));
-+				} else {
-+					printf(" \n");
-+					btrfs_print_tree(tree_root_scan, buf, 1);
-+				}
- 			}
- 		}
-+next:
- 		path.slots[0]++;
- 	}
-+no_node:
- 	btrfs_release_path(root, &path);
- 
--	if (tree_root_scan == root->fs_info->tree_root &&
--	    root->fs_info->log_root_tree) {
--		tree_root_scan = root->fs_info->log_root_tree;
-+	if (tree_root_scan == info->tree_root &&
-+	    info->log_root_tree) {
-+		tree_root_scan = info->log_root_tree;
- 		goto again;
- 	}
- 
--	if (extent_only)
-+	if (extent_only || device_only)
- 		return 0;
- 
-+	if (root_backups)
-+		print_old_roots(&info->super_copy);
-+
- 	printf("total bytes %llu\n",
--	       (unsigned long long)btrfs_super_total_bytes(&root->fs_info->super_copy));
-+	       (unsigned long long)btrfs_super_total_bytes(&info->super_copy));
- 	printf("bytes used %llu\n",
--	       (unsigned long long)btrfs_super_bytes_used(&root->fs_info->super_copy));
-+	       (unsigned long long)btrfs_super_bytes_used(&info->super_copy));
- 	uuidbuf[36] = '\0';
--	uuid_unparse(root->fs_info->super_copy.fsid, uuidbuf);
-+	uuid_unparse(info->super_copy.fsid, uuidbuf);
- 	printf("uuid %s\n", uuidbuf);
- 	printf("%s\n", BTRFS_BUILD_VERSION);
- 	return 0;
-diff --git a/dir-item.c b/dir-item.c
-index 71373b8..f00485a 100644
---- a/dir-item.c
-+++ b/dir-item.c
-@@ -332,5 +332,5 @@ int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
- 		ret = btrfs_truncate_item(trans, root, path,
- 					  item_len - sub_item_len, 1);
- 	}
--	return 0;
-+	return ret;
- }
-diff --git a/dir-test.c b/dir-test.c
-index 44f2758..3ae9c68 100644
---- a/dir-test.c
-+++ b/dir-test.c
-@@ -485,7 +485,7 @@ int main(int ac, char **av)
- 			if (ret) {
- 				fprintf(stderr, "op %d failed %d:%d\n",
- 					op, i, iterations);
--				btrfs_print_tree(root, root->node);
-+				btrfs_print_tree(root, root->node, 1);
- 				fprintf(stderr, "op %d failed %d:%d\n",
- 					op, i, iterations);
- 				err = ret;
-diff --git a/disk-io.c b/disk-io.c
-index addebe1..b21a87f 100644
---- a/disk-io.c
-+++ b/disk-io.c
-@@ -35,14 +35,19 @@
- #include "utils.h"
- #include "print-tree.h"
- 
-+static int close_all_devices(struct btrfs_fs_info *fs_info);
-+
- static int check_tree_block(struct btrfs_root *root, struct extent_buffer *buf)
- {
- 
- 	struct btrfs_fs_devices *fs_devices;
- 	int ret = 1;
- 
--	if (buf->start != btrfs_header_bytenr(buf))
-+	if (buf->start != btrfs_header_bytenr(buf)) {
-+		printk("Check tree block failed, want=%Lu, have=%Lu\n",
-+		       buf->start, btrfs_header_bytenr(buf));
- 		return ret;
-+	}
- 
- 	fs_devices = root->fs_info->fs_devices;
- 	while (fs_devices) {
-@@ -86,7 +91,7 @@ int csum_tree_block_size(struct extent_buffer *buf, u16 csum_size,
- 		if (memcmp_extent_buffer(buf, result, 0, csum_size)) {
- 			printk("checksum verify failed on %llu wanted %X "
- 			       "found %X\n", (unsigned long long)buf->start,
--			       *((int *)result), *((int *)buf));
-+			       *((int *)result), *((char *)buf->data));
- 			free(result);
- 			return 1;
- 		}
-@@ -123,7 +128,6 @@ int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
- 			 u64 parent_transid)
- {
- 	int ret;
--	int dev_nr;
- 	struct extent_buffer *eb;
- 	u64 length;
- 	struct btrfs_multi_bio *multi = NULL;
-@@ -135,7 +139,6 @@ int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
- 		return 0;
- 	}
- 
--	dev_nr = 0;
- 	length = blocksize;
- 	ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
- 			      bytenr, &length, &multi, 0);
-@@ -149,7 +152,8 @@ int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
- }
- 
- static int verify_parent_transid(struct extent_io_tree *io_tree,
--				 struct extent_buffer *eb, u64 parent_transid)
-+				 struct extent_buffer *eb, u64 parent_transid,
-+				 int ignore)
- {
- 	int ret;
- 
-@@ -165,6 +169,11 @@ static int verify_parent_transid(struct extent_io_tree *io_tree,
- 	       (unsigned long long)eb->start,
- 	       (unsigned long long)parent_transid,
- 	       (unsigned long long)btrfs_header_generation(eb));
-+	if (ignore) {
-+		printk("Ignoring transid failure\n");
-+		return 0;
-+	}
-+
- 	ret = 1;
- out:
- 	clear_extent_buffer_uptodate(io_tree, eb);
-@@ -177,13 +186,15 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
- 				     u32 blocksize, u64 parent_transid)
- {
- 	int ret;
--	int dev_nr;
- 	struct extent_buffer *eb;
- 	u64 length;
-+	u64 best_transid = 0;
- 	struct btrfs_multi_bio *multi = NULL;
- 	struct btrfs_device *device;
- 	int mirror_num = 0;
-+	int good_mirror = 0;
- 	int num_copies;
-+	int ignore = 0;
- 
- 	eb = btrfs_find_create_tree_block(root, bytenr, blocksize);
- 	if (!eb)
-@@ -192,32 +203,50 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
- 	if (btrfs_buffer_uptodate(eb, parent_transid))
- 		return eb;
- 
--	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);
-+		if (ret) {
-+			printk("Couldn't map the block %Lu\n", bytenr);
-+			break;
-+		}
- 		device = multi->stripes[0].dev;
- 		eb->fd = device->fd;
- 		device->total_ios++;
- 		eb->dev_bytenr = multi->stripes[0].physical;
- 		kfree(multi);
- 		ret = read_extent_from_disk(eb);
-+
- 		if (ret == 0 && check_tree_block(root, eb) == 0 &&
- 		    csum_tree_block(root, eb, 1) == 0 &&
--		    verify_parent_transid(eb->tree, eb, parent_transid) == 0) {
-+		    verify_parent_transid(eb->tree, eb, parent_transid, ignore)
-+		    == 0) {
- 			btrfs_set_buffer_uptodate(eb);
- 			return eb;
- 		}
-+		if (ignore) {
-+			if (check_tree_block(root, eb))
-+				printk("read block failed check_tree_block\n");
-+			else
-+				printk("Csum didn't match\n");
-+			break;
-+		}
- 		num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
- 					      eb->start, eb->len);
- 		if (num_copies == 1) {
--			break;
-+			ignore = 1;
-+			continue;
-+		}
-+		if (btrfs_header_generation(eb) > best_transid) {
-+			best_transid = btrfs_header_generation(eb);
-+			good_mirror = mirror_num;
- 		}
- 		mirror_num++;
- 		if (mirror_num > num_copies) {
--			break;
-+			mirror_num = good_mirror;
-+			ignore = 1;
-+			continue;
- 		}
- 	}
- 	free_extent_buffer(eb);
-@@ -364,6 +393,7 @@ static int __commit_transaction(struct btrfs_trans_handle *trans,
- int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
- 			     struct btrfs_root *root)
- {
-+	u64 transid = trans->transid;
- 	int ret = 0;
- 	struct btrfs_fs_info *fs_info = root->fs_info;
- 
-@@ -391,6 +421,7 @@ commit_tree:
- 	free_extent_buffer(root->commit_root);
- 	root->commit_root = NULL;
- 	fs_info->running_transaction = NULL;
-+	fs_info->last_trans_committed = transid;
- 	return 0;
- }
- 
-@@ -413,7 +444,9 @@ static int find_and_setup_root(struct btrfs_root *tree_root,
- 	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 (!extent_buffer_uptodate(root->node))
-+		return -EIO;
-+
- 	return 0;
- }
- 
-@@ -440,7 +473,9 @@ static int find_and_setup_log_root(struct btrfs_root *tree_root,
- 				     btrfs_super_generation(disk_super) + 1);
- 
- 	fs_info->log_root_tree = log_root;
--	BUG_ON(!log_root->node);
-+
-+	if (!extent_buffer_uptodate(log_root->node))
-+		return -EIO;
- 	return 0;
- }
- 
-@@ -549,7 +584,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);
- 
-@@ -570,28 +605,10 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
- 	return root;
- }
- 
--struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes)
--{
--	int fp;
--	struct btrfs_root *root;
--	int flags = O_CREAT | O_RDWR;
--
--	if (!writes)
--		flags = O_RDONLY;
--
--	fp = open(filename, flags, 0600);
--	if (fp < 0) {
--		fprintf (stderr, "Could not open %s\n", filename);
--		return NULL;
--	}
--	root = open_ctree_fd(fp, filename, sb_bytenr, writes);
--	close(fp);
--
--	return root;
--}
--
--struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
--				 int writes)
-+static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
-+					     u64 sb_bytenr,
-+					     u64 root_tree_bytenr, int writes,
-+					     int partial)
- {
- 	u32 sectorsize;
- 	u32 nodesize;
-@@ -620,12 +637,13 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
- 
- 	if (ret) {
- 		fprintf(stderr, "No valid Btrfs found on %s\n", path);
--		return NULL;
-+		goto out;
- 	}
- 
- 	if (total_devs != 1) {
- 		ret = btrfs_scan_for_fsid(fs_devices, total_devs, 1);
--		BUG_ON(ret);
-+		if (ret)
-+			goto out;
- 	}
- 
- 	memset(fs_info, 0, sizeof(*fs_info));
-@@ -660,7 +678,8 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
- 		ret = btrfs_open_devices(fs_devices, O_RDWR);
- 	else
- 		ret = btrfs_open_devices(fs_devices, O_RDONLY);
--	BUG_ON(ret);
-+	if (ret)
-+		goto out_cleanup;
- 
- 	fs_info->super_bytenr = sb_bytenr;
- 	disk_super = &fs_info->super_copy;
-@@ -668,7 +687,7 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
- 				   disk_super, sb_bytenr);
- 	if (ret) {
- 		printk("No valid btrfs found\n");
--		BUG_ON(1);
-+		goto out_devices;
- 	}
- 
- 	memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE);
-@@ -678,8 +697,9 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
- 		   ~BTRFS_FEATURE_INCOMPAT_SUPP;
- 	if (features) {
- 		printk("couldn't open because of unsupported "
--		       "option features (%Lx).\n", features);
--		BUG_ON(1);
-+		       "option features (%Lx).\n",
-+		       (unsigned long long)features);
-+		goto out_devices;
- 	}
- 
- 	features = btrfs_super_incompat_flags(disk_super);
-@@ -692,8 +712,9 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
- 		~BTRFS_FEATURE_COMPAT_RO_SUPP;
- 	if (writes && features) {
- 		printk("couldn't open RDWR because of unsupported "
--		       "option features (%Lx).\n", features);
--		BUG_ON(1);
-+		       "option features (%Lx).\n",
-+		       (unsigned long long)features);
-+		goto out_devices;
- 	}
- 
- 	nodesize = btrfs_super_nodesize(disk_super);
-@@ -706,7 +727,8 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
- 	tree_root->stripesize = stripesize;
- 
- 	ret = btrfs_read_sys_array(tree_root);
--	BUG_ON(ret);
-+	if (ret)
-+		goto out_devices;
- 	blocksize = btrfs_level_size(tree_root,
- 				     btrfs_super_chunk_root_level(disk_super));
- 	generation = btrfs_super_chunk_root_generation(disk_super);
-@@ -717,8 +739,10 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
- 	chunk_root->node = read_tree_block(chunk_root,
- 					   btrfs_super_chunk_root(disk_super),
- 					   blocksize, generation);
--
--	BUG_ON(!chunk_root->node);
-+	if (!extent_buffer_uptodate(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),
-@@ -726,37 +750,52 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
- 
- 	if (!(btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP)) {
- 		ret = btrfs_read_chunk_tree(chunk_root);
--		BUG_ON(ret);
-+		if (ret)
-+			goto out_failed;
- 	}
- 
- 	blocksize = btrfs_level_size(tree_root,
- 				     btrfs_super_root_level(disk_super));
- 	generation = btrfs_super_generation(disk_super);
- 
-+	if (!root_tree_bytenr)
-+		root_tree_bytenr = btrfs_super_root(disk_super);
- 	tree_root->node = read_tree_block(tree_root,
--					  btrfs_super_root(disk_super),
-+					  root_tree_bytenr,
- 					  blocksize, generation);
--	BUG_ON(!tree_root->node);
-+	if (!extent_buffer_uptodate(tree_root->node)) {
-+		printk("Couldn't read tree root\n");
-+		goto out_failed;
-+	}
- 	ret = find_and_setup_root(tree_root, fs_info,
- 				  BTRFS_EXTENT_TREE_OBJECTID, extent_root);
--	BUG_ON(ret);
-+	if (ret) {
-+		printk("Couldn't setup extent tree\n");
-+		goto out_failed;
-+	}
- 	extent_root->track_dirty = 1;
- 
- 	ret = find_and_setup_root(tree_root, fs_info,
- 				  BTRFS_DEV_TREE_OBJECTID, dev_root);
--	BUG_ON(ret);
-+	if (ret) {
-+		printk("Couldn't setup device tree\n");
-+		goto out_failed;
-+	}
- 	dev_root->track_dirty = 1;
- 
- 	ret = find_and_setup_root(tree_root, fs_info,
- 				  BTRFS_CSUM_TREE_OBJECTID, csum_root);
--	BUG_ON(ret);
-+	if (ret) {
-+		printk("Couldn't setup csum tree\n");
-+		if (!partial)
-+			goto out_failed;
-+	}
- 	csum_root->track_dirty = 1;
- 
--	BUG_ON(ret);
--
- 	find_and_setup_log_root(tree_root, fs_info, disk_super);
- 
--	fs_info->generation = generation + 1;
-+	fs_info->generation = generation;
-+	fs_info->last_trans_committed = generation;
- 	btrfs_read_block_groups(fs_info->tree_root);
- 
- 	key.objectid = BTRFS_FS_TREE_OBJECTID;
-@@ -764,11 +803,108 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
- 	key.offset = (u64)-1;
- 	fs_info->fs_root = btrfs_read_fs_root(fs_info, &key);
- 
-+	if (!fs_info->fs_root)
-+		goto out_failed;
-+
- 	fs_info->data_alloc_profile = (u64)-1;
- 	fs_info->metadata_alloc_profile = (u64)-1;
- 	fs_info->system_alloc_profile = fs_info->metadata_alloc_profile;
- 
--	return fs_info->fs_root;
-+	return fs_info;
-+
-+out_failed:
-+	if (partial)
-+		return fs_info;
-+
-+	if (fs_info->csum_root)
-+		free_extent_buffer(fs_info->csum_root->node);
-+	if (fs_info->dev_root)
-+		free_extent_buffer(fs_info->dev_root->node);
-+	if (fs_info->extent_root)
-+		free_extent_buffer(fs_info->extent_root->node);
-+	if (fs_info->tree_root)
-+		free_extent_buffer(fs_info->tree_root->node);
-+	if (fs_info->chunk_root)
-+		free_extent_buffer(fs_info->chunk_root->node);
-+out_devices:
-+	close_all_devices(fs_info);
-+out_cleanup:
-+	extent_io_tree_cleanup(&fs_info->extent_cache);
-+	extent_io_tree_cleanup(&fs_info->free_space_cache);
-+	extent_io_tree_cleanup(&fs_info->block_group_cache);
-+	extent_io_tree_cleanup(&fs_info->pinned_extents);
-+	extent_io_tree_cleanup(&fs_info->pending_del);
-+	extent_io_tree_cleanup(&fs_info->extent_ins);
-+out:
-+	free(tree_root);
-+	free(extent_root);
-+	free(chunk_root);
-+	free(dev_root);
-+	free(csum_root);
-+	free(fs_info);
-+	return NULL;
-+}
-+
-+struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
-+					 u64 sb_bytenr, int writes,
-+					 int partial)
-+{
-+	int fp;
-+	struct btrfs_fs_info *info;
-+	int flags = O_CREAT | O_RDWR;
-+
-+	if (!writes)
-+		flags = O_RDONLY;
-+
-+	fp = open(filename, flags, 0600);
-+	if (fp < 0) {
-+		fprintf (stderr, "Could not open %s\n", filename);
-+		return NULL;
-+	}
-+	info = __open_ctree_fd(fp, filename, sb_bytenr, 0, writes, partial);
-+	close(fp);
-+	return info;
-+}
-+
-+struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes)
-+{
-+	struct btrfs_fs_info *info;
-+
-+	info = open_ctree_fs_info(filename, sb_bytenr, writes, 0);
-+	if (!info)
-+		return NULL;
-+	return info->fs_root;
-+}
-+
-+struct btrfs_root *open_ctree_recovery(const char *filename, u64 sb_bytenr,
-+				       u64 root_tree_bytenr)
-+{
-+	int fp;
-+	struct btrfs_fs_info *info;
-+
-+
-+	fp = open(filename, O_RDONLY);
-+	if (fp < 0) {
-+		fprintf (stderr, "Could not open %s\n", filename);
-+		return NULL;
-+	}
-+	info = __open_ctree_fd(fp, filename, sb_bytenr,
-+			       root_tree_bytenr, 0, 0);
-+	close(fp);
-+
-+	if (!info)
-+		return NULL;
-+	return info->fs_root;
-+}
-+
-+struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
-+				 int writes)
-+{
-+	struct btrfs_fs_info *info;
-+	info = __open_ctree_fd(fp, path, sb_bytenr, 0, writes, 0);
-+	if (!info)
-+		return NULL;
-+	return info->fs_root;
- }
- 
- int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr)
-@@ -828,7 +964,6 @@ int write_dev_supers(struct btrfs_root *root, struct btrfs_super_block *sb,
- 
- 	if (root->fs_info->super_bytenr != BTRFS_SUPER_INFO_OFFSET) {
- 		btrfs_set_super_bytenr(sb, root->fs_info->super_bytenr);
--
- 		crc = ~(u32)0;
- 		crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
- 				      BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
-@@ -946,15 +1081,18 @@ int close_ctree(struct btrfs_root *root)
- 	struct btrfs_trans_handle *trans;
- 	struct btrfs_fs_info *fs_info = root->fs_info;
- 
--	trans = btrfs_start_transaction(root, 1);
--	btrfs_commit_transaction(trans, root);
--	trans = btrfs_start_transaction(root, 1);
--	ret = commit_tree_roots(trans, fs_info);
--	BUG_ON(ret);
--	ret = __commit_transaction(trans, root);
--	BUG_ON(ret);
--	write_ctree_super(trans, root);
--	btrfs_free_transaction(root, trans);
-+	if (fs_info->last_trans_committed !=
-+	    fs_info->generation) {
-+		trans = btrfs_start_transaction(root, 1);
-+		btrfs_commit_transaction(trans, root);
-+		trans = btrfs_start_transaction(root, 1);
-+		ret = commit_tree_roots(trans, fs_info);
-+		BUG_ON(ret);
-+		ret = __commit_transaction(trans, root);
-+		BUG_ON(ret);
-+		write_ctree_super(trans, root);
-+		btrfs_free_transaction(root, trans);
-+	}
- 	btrfs_free_block_groups(fs_info);
- 
- 	free_fs_roots(fs_info);
-@@ -970,13 +1108,13 @@ int close_ctree(struct btrfs_root *root)
- 	if (fs_info->csum_root->node)
- 		free_extent_buffer(fs_info->csum_root->node);
- 
--	if (root->fs_info->log_root_tree) {
--		if (root->fs_info->log_root_tree->node)
--			free_extent_buffer(root->fs_info->log_root_tree->node);
--		free(root->fs_info->log_root_tree);
-+	if (fs_info->log_root_tree) {
-+		if (fs_info->log_root_tree->node)
-+			free_extent_buffer(fs_info->log_root_tree->node);
-+		free(fs_info->log_root_tree);
- 	}
- 
--	close_all_devices(root->fs_info);
-+	close_all_devices(fs_info);
- 	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);
-@@ -1019,7 +1157,7 @@ int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid)
- 	if (!ret)
- 		return ret;
- 
--	ret = verify_parent_transid(buf->tree, buf, parent_transid);
-+	ret = verify_parent_transid(buf->tree, buf, parent_transid, 1);
- 	return !ret;
- }
- 
-diff --git a/disk-io.h b/disk-io.h
-index 49e5692..53e9b17 100644
---- a/disk-io.h
-+++ b/disk-io.h
-@@ -46,7 +46,13 @@ int clean_tree_block(struct btrfs_trans_handle *trans,
- struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes);
- struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
- 				 int writes);
-+struct btrfs_root *open_ctree_recovery(const char *filename, u64 sb_bytenr,
-+				       u64 root_tree_bytenr);
-+struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
-+					 u64 sb_bytenr, int writes,
-+					 int partial);
- int close_ctree(struct btrfs_root *root);
-+int write_all_supers(struct btrfs_root *root);
- int write_ctree_super(struct btrfs_trans_handle *trans,
- 		      struct btrfs_root *root);
- int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr);
-diff --git a/extent-cache.c b/extent-cache.c
-index b871e18..3dd6434 100644
---- a/extent-cache.c
-+++ b/extent-cache.c
-@@ -96,13 +96,11 @@ int insert_existing_cache_extent(struct cache_tree *tree,
- 				 struct cache_extent *pe)
- {
- 	struct rb_node *found;
--	struct cache_extent *entry;
- 
- 	found = tree_insert(&tree->root, pe->start, pe->size, &pe->rb_node);
--	if (found) {
--		entry = rb_entry(found, struct cache_extent, rb_node);
-+	if (found)
- 		return -EEXIST;
--	}
-+
- 	return 0;
- }
- 
-diff --git a/extent-tree.c b/extent-tree.c
-index b2f9bb2..20cdffa 100644
---- a/extent-tree.c
-+++ b/extent-tree.c
-@@ -1039,6 +1039,11 @@ static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
- 		err = ret;
- 		goto out;
- 	}
-+	if (ret) {
-+		printf("Failed to find [%llu, %u, %llu]\n", key.objectid, key.type, key.offset);
-+		return -ENOENT;
-+	}
-+
- 	BUG_ON(ret);
- 
- 	leaf = path->nodes[0];
-@@ -1059,6 +1064,13 @@ static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
- 		item_size = btrfs_item_size_nr(leaf, path->slots[0]);
- 	}
- #endif
-+	if (item_size < sizeof(*ei)) {
-+		printf("Size is %u, needs to be %u, slot %d\n",
-+		       (unsigned)item_size,
-+		       (unsigned)sizeof(*ei), path->slots[0]);
-+		btrfs_print_leaf(root, leaf);
-+		return -EINVAL;
-+	}
- 	BUG_ON(item_size < sizeof(*ei));
- 
- 	ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
-@@ -1071,7 +1083,9 @@ static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
- 		ptr += sizeof(struct btrfs_tree_block_info);
- 		BUG_ON(ptr > end);
- 	} else {
--		BUG_ON(!(flags & BTRFS_EXTENT_FLAG_DATA));
-+		if (!(flags & BTRFS_EXTENT_FLAG_DATA)) {
-+			return -EIO;
-+		}
- 	}
- 
- 	err = -ENOENT;
-@@ -1447,9 +1461,8 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
- 	if (ret < 0)
- 		goto out;
- 	if (ret != 0) {
--		btrfs_print_leaf(root, path->nodes[0]);
--		printk("failed to find block number %Lu\n", bytenr);
--		BUG();
-+		ret = -EIO;
-+		goto out;
- 	}
- 
- 	l = path->nodes[0];
-@@ -1470,9 +1483,8 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
- 			extent_flags = BTRFS_BLOCK_FLAG_FULL_BACKREF;
- #else
- 			BUG();
--#endif		
--		}
--		BUG_ON(num_refs == 0);
-+#endif
-+	}
- 	item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
- 	if (refs)
- 		*refs = num_refs;
-@@ -1549,7 +1561,6 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
- 	int i;
- 	int level;
- 	int ret = 0;
--	int faili = 0;
- 	int (*process_func)(struct btrfs_trans_handle *trans,
- 			    struct btrfs_root *root,
- 			    u64, u64, u64, u64, u64, u64);
-@@ -1592,7 +1603,6 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
- 					   parent, ref_root, key.objectid,
- 					   key.offset);
- 			if (ret) {
--				faili = i;
- 				WARN_ON(1);
- 				goto fail;
- 			}
-@@ -1602,7 +1612,6 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
- 			ret = process_func(trans, root, bytenr, num_bytes,
- 					   parent, ref_root, level - 1, 0);
- 			if (ret) {
--				faili = i;
- 				WARN_ON(1);
- 				goto fail;
- 			}
-@@ -1611,33 +1620,6 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
- 	return 0;
- fail:
- 	WARN_ON(1);
--#if 0
--	for (i =0; i < faili; i++) {
--		if (level == 0) {
--			u64 disk_bytenr;
--			btrfs_item_key_to_cpu(buf, &key, i);
--			if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
--				continue;
--			fi = btrfs_item_ptr(buf, i,
--					    struct btrfs_file_extent_item);
--			if (btrfs_file_extent_type(buf, fi) ==
--			    BTRFS_FILE_EXTENT_INLINE)
--				continue;
--			disk_bytenr = btrfs_file_extent_disk_bytenr(buf, fi);
--			if (disk_bytenr == 0)
--				continue;
--			err = btrfs_free_extent(trans, root, disk_bytenr,
--				    btrfs_file_extent_disk_num_bytes(buf,
--								      fi), 0);
--			BUG_ON(err);
--		} else {
--			bytenr = btrfs_node_blockptr(buf, i);
--			err = btrfs_free_extent(trans, root, bytenr,
--					btrfs_level_size(root, level - 1), 0);
--			BUG_ON(err);
--		}
--	}
--#endif
- 	return ret;
- }
- 
-@@ -1721,7 +1703,6 @@ int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
- 
- 		cache = (struct btrfs_block_group_cache *)(unsigned long)ptr;
- 		ret = write_one_cache_group(trans, root, path, cache);
--		BUG_ON(ret);
- 	}
- 	btrfs_free_path(path);
- 	return 0;
-@@ -1735,7 +1716,7 @@ static struct btrfs_space_info *__find_space_info(struct btrfs_fs_info *info,
- 	struct btrfs_space_info *found;
- 	list_for_each(cur, head) {
- 		found = list_entry(cur, struct btrfs_space_info, list);
--		if (found->flags == flags)
-+		if (found->flags & flags)
- 			return found;
- 	}
- 	return NULL;
-@@ -1752,7 +1733,12 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
- 	if (found) {
- 		found->total_bytes += total_bytes;
- 		found->bytes_used += bytes_used;
--		WARN_ON(found->total_bytes < found->bytes_used);
-+		if (found->total_bytes < found->bytes_used) {
-+			fprintf(stderr, "warning, bad space info total_bytes "
-+				"%llu used %llu\n",
-+			       (unsigned long long)found->total_bytes,
-+			       (unsigned long long)found->bytes_used);
-+		}
- 		*space_info = found;
- 		return 0;
- 	}
-@@ -1775,6 +1761,7 @@ static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags)
- {
- 	u64 extra_flags = flags & (BTRFS_BLOCK_GROUP_RAID0 |
- 				   BTRFS_BLOCK_GROUP_RAID1 |
-+				   BTRFS_BLOCK_GROUP_RAID10 |
- 				   BTRFS_BLOCK_GROUP_DUP);
- 	if (extra_flags) {
- 		if (flags & BTRFS_BLOCK_GROUP_DATA)
-@@ -1812,7 +1799,8 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
- 	    thresh)
- 		return 0;
- 
--	ret = btrfs_alloc_chunk(trans, extent_root, &start, &num_bytes, flags);
-+	ret = btrfs_alloc_chunk(trans, extent_root, &start, &num_bytes,
-+	                        space_info->flags);
- 	if (ret == -ENOSPC) {
- 		space_info->full = 1;
- 		return 0;
-@@ -1820,7 +1808,7 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
- 
- 	BUG_ON(ret);
- 
--	ret = btrfs_make_block_group(trans, extent_root, 0, flags,
-+	ret = btrfs_make_block_group(trans, extent_root, 0, space_info->flags,
- 		     BTRFS_FIRST_CHUNK_TREE_OBJECTID, start, num_bytes);
- 	BUG_ON(ret);
- 	return 0;
-@@ -1869,6 +1857,7 @@ static int update_block_group(struct btrfs_trans_handle *trans,
- 
- 		old_val = btrfs_block_group_used(&cache->item);
- 		num_bytes = min(total, cache->key.offset - byte_in_group);
-+
- 		if (alloc) {
- 			old_val += num_bytes;
- 			cache->space_info->bytes_used += num_bytes;
-@@ -1904,6 +1893,10 @@ static int update_pinned_extents(struct btrfs_root *root,
- 	}
- 	while (num > 0) {
- 		cache = btrfs_lookup_block_group(fs_info, bytenr);
-+		if (!cache) {
-+			len = min((u64)root->sectorsize, num);
-+			goto next;
-+		}
- 		WARN_ON(!cache);
- 		len = min(num, cache->key.offset -
- 			  (bytenr - cache->key.objectid));
-@@ -1916,6 +1909,7 @@ static int update_pinned_extents(struct btrfs_root *root,
- 			cache->space_info->bytes_pinned -= len;
- 			fs_info->total_pinned -= len;
- 		}
-+next:
- 		bytenr += len;
- 		num -= len;
- 	}
-@@ -1963,6 +1957,21 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
- 	return 0;
- }
- 
-+static int extent_root_pending_ops(struct btrfs_fs_info *info)
-+{
-+	u64 start;
-+	u64 end;
-+	int ret;
-+
-+	ret = find_first_extent_bit(&info->extent_ins, 0, &start,
-+				    &end, EXTENT_LOCKED);
-+	if (!ret) {
-+		ret = find_first_extent_bit(&info->pending_del, 0, &start, &end,
-+					    EXTENT_LOCKED);
-+	}
-+	return ret == 0;
-+
-+}
- static int finish_current_insert(struct btrfs_trans_handle *trans,
- 				 struct btrfs_root *extent_root)
- {
-@@ -2047,6 +2056,12 @@ pinit:
- 	return 0;
- }
- 
-+void btrfs_pin_extent(struct btrfs_fs_info *fs_info,
-+		       u64 bytenr, u64 num_bytes)
-+{
-+	update_pinned_extents(fs_info->extent_root, bytenr, num_bytes, 1);
-+}
-+
- /*
-  * remove an extent from the root, returns 0 on success
-  */
-@@ -2072,6 +2087,12 @@ static int __free_extent(struct btrfs_trans_handle *trans,
- 	u32 item_size;
- 	u64 refs;
- 
-+	if (root->fs_info->free_extent_hook) {
-+		root->fs_info->free_extent_hook(trans, root, bytenr, num_bytes,
-+						parent, root_objectid, owner_objectid,
-+						owner_offset, refs_to_drop);
-+
-+	}
- 	path = btrfs_alloc_path();
- 	if (!path)
- 		return -ENOMEM;
-@@ -2132,8 +2153,6 @@ static int __free_extent(struct btrfs_trans_handle *trans,
- 			extent_slot = path->slots[0];
- 		}
- 	} else {
--		btrfs_print_leaf(extent_root, path->nodes[0]);
--		WARN_ON(1);
- 		printk(KERN_ERR "btrfs unable to find ref byte nr %llu "
- 		       "parent %llu root %llu  owner %llu offset %llu\n",
- 		       (unsigned long long)bytenr,
-@@ -2141,6 +2160,8 @@ static int __free_extent(struct btrfs_trans_handle *trans,
- 		       (unsigned long long)root_objectid,
- 		       (unsigned long long)owner_objectid,
- 		       (unsigned long long)owner_offset);
-+		ret = -EIO;
-+		goto fail;
- 	}
- 
- 	leaf = path->nodes[0];
-@@ -2246,10 +2267,9 @@ static int __free_extent(struct btrfs_trans_handle *trans,
- 			BUG_ON(ret);
- 		}
- 
--		ret = update_block_group(trans, root, bytenr, num_bytes, 0,
--					 mark_free);
--		BUG_ON(ret);
-+		update_block_group(trans, root, bytenr, num_bytes, 0, mark_free);
- 	}
-+fail:
- 	btrfs_free_path(path);
- 	finish_current_insert(trans, extent_root);
- 	return ret;
-@@ -2578,13 +2598,7 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
- 
- 	ret = update_block_group(trans, root, ins->objectid, ins->offset,
- 				 1, 0);
--	if (ret) {
--		printk(KERN_ERR "btrfs update block group failed for %llu "
--		       "%llu\n", (unsigned long long)ins->objectid,
--		       (unsigned long long)ins->offset);
--		BUG();
--	}
--	return ret;
-+	return 0;
- }
- 
- static int alloc_tree_block(struct btrfs_trans_handle *trans,
-@@ -3167,7 +3181,6 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
- 
- 	finish_current_insert(trans, extent_root);
- 	ret = del_pending_extents(trans, extent_root);
--	BUG_ON(ret);
- 	set_avail_alloc_bits(extent_root->fs_info, type);
- 	return 0;
- }
-@@ -3283,3 +3296,158 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans,
- 	return update_block_group(trans, root, bytenr, num_bytes,
- 				  alloc, mark_free);
- }
-+
-+static int btrfs_count_extents_in_block_group(struct btrfs_root *root,
-+					      struct btrfs_path *path, u64 start,
-+					      u64 len,
-+					      u64 *total)
-+{
-+	struct btrfs_key key;
-+	struct extent_buffer *leaf;
-+	u64 bytes_used = 0;
-+	int ret;
-+	int slot;
-+
-+	key.offset = 0;
-+	key.objectid = start;
-+	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
-+	ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
-+				&key, path, 0, 0);
-+	if (ret < 0)
-+		return ret;
-+	while(1) {
-+		leaf = path->nodes[0];
-+		slot = path->slots[0];
-+		if (slot >= btrfs_header_nritems(leaf)) {
-+			ret = btrfs_next_leaf(root, path);
-+			if (ret < 0)
-+				return ret;
-+			if (ret > 0)
-+				break;
-+			leaf = path->nodes[0];
-+			slot = path->slots[0];
-+		}
-+		btrfs_item_key_to_cpu(leaf, &key, slot);
-+		if (key.objectid > start + len)
-+			break;
-+		if (key.type == BTRFS_EXTENT_ITEM_KEY)
-+			bytes_used += key.offset;
-+		path->slots[0]++;
-+	}
-+	*total = bytes_used;
-+	btrfs_release_path(root, path);
-+	return 0;
-+}
-+
-+int btrfs_check_block_accounting(struct btrfs_root *root)
-+{
-+	int ret;
-+	u64 start = 0;
-+	u64 bytes_used = 0;
-+	struct btrfs_path path;
-+	struct btrfs_block_group_cache *cache;
-+	struct btrfs_fs_info *fs_info = root->fs_info;
-+
-+	btrfs_init_path(&path);
-+
-+	while(1) {
-+		cache = btrfs_lookup_block_group(fs_info, start);
-+		if (!cache)
-+			break;
-+
-+		ret = btrfs_count_extents_in_block_group(root, &path,
-+							 cache->key.objectid,
-+							 cache->key.offset,
-+							 &bytes_used);
-+
-+		if (ret == 0) {
-+			u64 on_disk = btrfs_block_group_used(&cache->item);
-+			if (on_disk != bytes_used) {
-+				fprintf(stderr, "bad block group accounting found %llu "
-+					"expected %llu block group %llu\n",
-+					(unsigned long long)bytes_used,
-+					(unsigned long long)on_disk,
-+					(unsigned long long)cache->key.objectid);
-+			}
-+		}
-+		start = cache->key.objectid + cache->key.offset;
-+
-+		cache->space_info->bytes_used = 0;
-+	}
-+	return 0;
-+}
-+
-+/*
-+ * Fixup block accounting. The initial block accounting created by
-+ * make_block_groups isn't accuracy in this case.
-+ */
-+int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans,
-+			       struct btrfs_root *root)
-+{
-+	int ret;
-+	int slot;
-+	u64 start = 0;
-+	u64 bytes_used = 0;
-+	struct btrfs_path path;
-+	struct btrfs_key key;
-+	struct extent_buffer *leaf;
-+	struct btrfs_block_group_cache *cache;
-+	struct btrfs_fs_info *fs_info = root->fs_info;
-+
-+	root = root->fs_info->extent_root;
-+
-+	while(extent_root_pending_ops(fs_info)) {
-+		ret = finish_current_insert(trans, root);
-+		if (ret)
-+			return ret;
-+		ret = del_pending_extents(trans, root);
-+		if (ret)
-+			return ret;
-+	}
-+
-+	while(1) {
-+		cache = btrfs_lookup_block_group(fs_info, start);
-+		if (!cache)
-+			break;
-+		start = cache->key.objectid + cache->key.offset;
-+		btrfs_set_block_group_used(&cache->item, 0);
-+		cache->space_info->bytes_used = 0;
-+		set_extent_bits(&root->fs_info->block_group_cache,
-+				cache->key.objectid,
-+				cache->key.objectid + cache->key.offset -1,
-+				BLOCK_GROUP_DIRTY, GFP_NOFS);
-+	}
-+
-+	btrfs_init_path(&path);
-+	key.offset = 0;
-+	key.objectid = 0;
-+	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
-+	ret = btrfs_search_slot(trans, root->fs_info->extent_root,
-+				&key, &path, 0, 0);
-+	if (ret < 0)
-+		return ret;
-+	while(1) {
-+		leaf = path.nodes[0];
-+		slot = path.slots[0];
-+		if (slot >= btrfs_header_nritems(leaf)) {
-+			ret = btrfs_next_leaf(root, &path);
-+			if (ret < 0)
-+				return ret;
-+			if (ret > 0)
-+				break;
-+			leaf = path.nodes[0];
-+			slot = path.slots[0];
-+		}
-+		btrfs_item_key_to_cpu(leaf, &key, slot);
-+		if (key.type == BTRFS_EXTENT_ITEM_KEY) {
-+			bytes_used += key.offset;
-+			ret = btrfs_update_block_group(trans, root,
-+				  key.objectid, key.offset, 1, 0);
-+			BUG_ON(ret);
-+		}
-+		path.slots[0]++;
-+	}
-+	btrfs_set_super_bytes_used(&root->fs_info->super_copy, bytes_used);
-+	btrfs_release_path(root, &path);
-+	return 0;
-+}
-diff --git a/extent_io.c b/extent_io.c
-index 069c199..ebb35b2 100644
---- a/extent_io.c
-+++ b/extent_io.c
-@@ -28,7 +28,8 @@
- #include "extent_io.h"
- #include "list.h"
- 
--u64 cache_max = 1024 * 1024 * 32;
-+u64 cache_soft_max = 1024 * 1024 * 256;
-+u64 cache_hard_max = 1 * 1024 * 1024 * 1024;
- 
- void extent_io_tree_init(struct extent_io_tree *tree)
- {
-@@ -296,7 +297,6 @@ int set_extent_bits(struct extent_io_tree *tree, u64 start,
- 	struct extent_state *prealloc = NULL;
- 	struct cache_extent *node;
- 	int err = 0;
--	int set;
- 	u64 last_start;
- 	u64 last_end;
- again:
-@@ -327,7 +327,6 @@ again:
- 	 * Just lock what we found and keep going
- 	 */
- 	if (state->start == start && state->end <= end) {
--		set = state->state & bits;
- 		state->state |= bits;
- 		merge_state(tree, state);
- 		if (last_end == (u64)-1)
-@@ -352,7 +351,6 @@ again:
- 	 * desired bit on it.
- 	 */
- 	if (state->start < start) {
--		set = state->state & bits;
- 		err = split_state(tree, state, prealloc, start);
- 		BUG_ON(err == -EEXIST);
- 		prealloc = NULL;
-@@ -398,7 +396,6 @@ again:
- 	 * We need to split the extent, and set the bit
- 	 * on the first half
- 	 */
--	set = state->state & bits;
- 	err = split_state(tree, state, prealloc, end + 1);
- 	BUG_ON(err == -EEXIST);
- 
-@@ -544,18 +541,19 @@ static int free_some_buffers(struct extent_io_tree *tree)
- 	struct extent_buffer *eb;
- 	struct list_head *node, *next;
- 
--	if (tree->cache_size < cache_max)
-+	if (tree->cache_size < cache_soft_max)
- 		return 0;
-+
- 	list_for_each_safe(node, next, &tree->lru) {
- 		eb = list_entry(node, struct extent_buffer, lru);
- 		if (eb->refs == 1) {
- 			free_extent_buffer(eb);
--			if (tree->cache_size < cache_max)
-+			if (tree->cache_size < cache_hard_max)
- 				break;
- 		} else {
- 			list_move_tail(&eb->lru, &tree->lru);
- 		}
--		if (nrscan++ > 64)
-+		if (nrscan++ > 64 && tree->cache_size < cache_hard_max)
- 			break;
- 	}
- 	return 0;
-@@ -572,6 +570,7 @@ static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
- 		BUG();
- 		return NULL;
- 	}
-+	memset(eb, 0, sizeof(struct extent_buffer) + blocksize);
- 
- 	eb->start = bytenr;
- 	eb->len = blocksize;
-@@ -657,7 +656,6 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
- 		if (cache) {
- 			eb = container_of(cache, struct extent_buffer,
- 					  cache_node);
--			BUG_ON(eb->refs != 1);
- 			free_extent_buffer(eb);
- 		}
- 		eb = __alloc_extent_buffer(tree, bytenr, blocksize);
-@@ -710,6 +708,9 @@ int clear_extent_buffer_uptodate(struct extent_io_tree *tree,
- 
- int extent_buffer_uptodate(struct extent_buffer *eb)
- {
-+	if (!eb)
-+		return 0;
-+
- 	if (eb->flags & EXTENT_UPTODATE)
- 		return 1;
- 	return 0;
-diff --git a/file-item.c b/file-item.c
-index 9732282..c746b44 100644
---- a/file-item.c
-+++ b/file-item.c
-@@ -193,7 +193,7 @@ int btrfs_csum_file_block(struct btrfs_trans_handle *trans,
- 			  struct btrfs_root *root, u64 alloc_end,
- 			  u64 bytenr, char *data, size_t len)
- {
--	int ret;
-+	int ret = 0;
- 	struct btrfs_key file_key;
- 	struct btrfs_key found_key;
- 	u64 next_offset = (u64)-1;
-@@ -218,6 +218,7 @@ int btrfs_csum_file_block(struct btrfs_trans_handle *trans,
- 	item = btrfs_lookup_csum(trans, root, path, bytenr, 1);
- 	if (!IS_ERR(item)) {
- 		leaf = path->nodes[0];
-+		ret = 0;
- 		goto found;
- 	}
- 	ret = PTR_ERR(item);
-diff --git a/find-root.c b/find-root.c
-new file mode 100644
-index 0000000..c0f38b8
---- /dev/null
-+++ b/find-root.c
-@@ -0,0 +1,458 @@
-+/*
-+ * Copyright (C) 2011 Red Hat.  All rights reserved.
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public
-+ * License v2 as published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+ * General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public
-+ * License along with this program; if not, write to the
-+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-+ * Boston, MA 021110-1307, USA.
-+ */
-+
-+#define _XOPEN_SOURCE 500
-+#define _GNU_SOURCE 1
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <fcntl.h>
-+#include <sys/stat.h>
-+#include <zlib.h>
-+#include "kerncompat.h"
-+#include "ctree.h"
-+#include "disk-io.h"
-+#include "print-tree.h"
-+#include "transaction.h"
-+#include "list.h"
-+#include "version.h"
-+#include "volumes.h"
-+#include "utils.h"
-+#include "crc32c.h"
-+
-+static int verbose = 0;
-+static u16 csum_size = 0;
-+static u64 search_objectid = BTRFS_ROOT_TREE_OBJECTID;
-+
-+static void usage()
-+{
-+	fprintf(stderr, "Usage: find-roots [-v] <device>\n");
-+}
-+
-+int csum_block(void *buf, u32 len)
-+{
-+	char *result;
-+	u32 crc = ~(u32)0;
-+	int ret = 0;
-+
-+	result = malloc(csum_size * sizeof(char));
-+	if (!result) {
-+		fprintf(stderr, "No memory\n");
-+		return 1;
-+	}
-+
-+	len -= BTRFS_CSUM_SIZE;
-+	crc = crc32c(crc, buf + BTRFS_CSUM_SIZE, len);
-+	btrfs_csum_final(crc, result);
-+
-+	if (memcmp(buf, result, csum_size))
-+		ret = 1;
-+	free(result);
-+	return ret;
-+}
-+
-+static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
-+			u32 stripesize, struct btrfs_root *root,
-+			struct btrfs_fs_info *fs_info, u64 objectid)
-+{
-+	root->node = NULL;
-+	root->commit_root = NULL;
-+	root->sectorsize = sectorsize;
-+	root->nodesize = nodesize;
-+	root->leafsize = leafsize;
-+	root->stripesize = stripesize;
-+	root->ref_cows = 0;
-+	root->track_dirty = 0;
-+
-+	root->fs_info = fs_info;
-+	root->objectid = objectid;
-+	root->last_trans = 0;
-+	root->highest_inode = 0;
-+	root->last_inode_alloc = 0;
-+
-+	INIT_LIST_HEAD(&root->dirty_list);
-+	memset(&root->root_key, 0, sizeof(root->root_key));
-+	memset(&root->root_item, 0, sizeof(root->root_item));
-+	root->root_key.objectid = objectid;
-+	return 0;
-+}
-+
-+static int close_all_devices(struct btrfs_fs_info *fs_info)
-+{
-+	struct list_head *list;
-+	struct list_head *next;
-+	struct btrfs_device *device;
-+
-+	return 0;
-+
-+	list = &fs_info->fs_devices->devices;
-+	list_for_each(next, list) {
-+		device = list_entry(next, struct btrfs_device, dev_list);
-+		close(device->fd);
-+	}
-+	return 0;
-+}
-+
-+static struct btrfs_root *open_ctree_broken(int fd, const char *device)
-+{
-+	u32 sectorsize;
-+	u32 nodesize;
-+	u32 leafsize;
-+	u32 blocksize;
-+	u32 stripesize;
-+	u64 generation;
-+	struct btrfs_root *tree_root = malloc(sizeof(struct btrfs_root));
-+	struct btrfs_root *extent_root = malloc(sizeof(struct btrfs_root));
-+	struct btrfs_root *chunk_root = malloc(sizeof(struct btrfs_root));
-+	struct btrfs_root *dev_root = malloc(sizeof(struct btrfs_root));
-+	struct btrfs_root *csum_root = malloc(sizeof(struct btrfs_root));
-+	struct btrfs_fs_info *fs_info = malloc(sizeof(*fs_info));
-+	int ret;
-+	struct btrfs_super_block *disk_super;
-+	struct btrfs_fs_devices *fs_devices = NULL;
-+	u64 total_devs;
-+	u64 features;
-+
-+	ret = btrfs_scan_one_device(fd, device, &fs_devices,
-+				    &total_devs, BTRFS_SUPER_INFO_OFFSET);
-+
-+	if (ret) {
-+		fprintf(stderr, "No valid Btrfs found on %s\n", device);
-+		goto out;
-+	}
-+
-+	if (total_devs != 1) {
-+		ret = btrfs_scan_for_fsid(fs_devices, total_devs, 1);
-+		if (ret)
-+			goto out;
-+	}
-+
-+	memset(fs_info, 0, sizeof(*fs_info));
-+	fs_info->tree_root = tree_root;
-+	fs_info->extent_root = extent_root;
-+	fs_info->chunk_root = chunk_root;
-+	fs_info->dev_root = dev_root;
-+	fs_info->csum_root = csum_root;
-+
-+	fs_info->readonly = 1;
-+
-+	extent_io_tree_init(&fs_info->extent_cache);
-+	extent_io_tree_init(&fs_info->free_space_cache);
-+	extent_io_tree_init(&fs_info->block_group_cache);
-+	extent_io_tree_init(&fs_info->pinned_extents);
-+	extent_io_tree_init(&fs_info->pending_del);
-+	extent_io_tree_init(&fs_info->extent_ins);
-+	cache_tree_init(&fs_info->fs_root_cache);
-+
-+	cache_tree_init(&fs_info->mapping_tree.cache_tree);
-+
-+	mutex_init(&fs_info->fs_mutex);
-+	fs_info->fs_devices = fs_devices;
-+	INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots);
-+	INIT_LIST_HEAD(&fs_info->space_info);
-+
-+	__setup_root(4096, 4096, 4096, 4096, tree_root,
-+		     fs_info, BTRFS_ROOT_TREE_OBJECTID);
-+
-+	ret = btrfs_open_devices(fs_devices, O_RDONLY);
-+	if (ret)
-+		goto out_cleanup;
-+
-+	fs_info->super_bytenr = BTRFS_SUPER_INFO_OFFSET;
-+	disk_super = &fs_info->super_copy;
-+	ret = btrfs_read_dev_super(fs_devices->latest_bdev,
-+				   disk_super, BTRFS_SUPER_INFO_OFFSET);
-+	if (ret) {
-+		printk("No valid btrfs found\n");
-+		goto out_devices;
-+	}
-+
-+	memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE);
-+
-+
-+	features = btrfs_super_incompat_flags(disk_super) &
-+		   ~BTRFS_FEATURE_INCOMPAT_SUPP;
-+	if (features) {
-+		printk("couldn't open because of unsupported "
-+		       "option features (%Lx).\n", features);
-+		goto out_devices;
-+	}
-+
-+	features = btrfs_super_incompat_flags(disk_super);
-+	if (!(features & BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF)) {
-+		features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF;
-+		btrfs_set_super_incompat_flags(disk_super, features);
-+	}
-+
-+	nodesize = btrfs_super_nodesize(disk_super);
-+	leafsize = btrfs_super_leafsize(disk_super);
-+	sectorsize = btrfs_super_sectorsize(disk_super);
-+	stripesize = btrfs_super_stripesize(disk_super);
-+	tree_root->nodesize = nodesize;
-+	tree_root->leafsize = leafsize;
-+	tree_root->sectorsize = sectorsize;
-+	tree_root->stripesize = stripesize;
-+
-+	ret = btrfs_read_sys_array(tree_root);
-+	if (ret)
-+		goto out_devices;
-+	blocksize = btrfs_level_size(tree_root,
-+				     btrfs_super_chunk_root_level(disk_super));
-+	generation = btrfs_super_chunk_root_generation(disk_super);
-+
-+	__setup_root(nodesize, leafsize, sectorsize, stripesize,
-+		     chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID);
-+
-+	chunk_root->node = read_tree_block(chunk_root,
-+					   btrfs_super_chunk_root(disk_super),
-+					   blocksize, generation);
-+	if (!chunk_root->node) {
-+		printk("Couldn't read chunk root\n");
-+		goto out_devices;
-+	}
-+
-+	read_extent_buffer(chunk_root->node, fs_info->chunk_tree_uuid,
-+	         (unsigned long)btrfs_header_chunk_tree_uuid(chunk_root->node),
-+		 BTRFS_UUID_SIZE);
-+
-+	if (!(btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP)) {
-+		ret = btrfs_read_chunk_tree(chunk_root);
-+		if (ret)
-+			goto out_chunk;
-+	}
-+
-+	return fs_info->chunk_root;
-+out_chunk:
-+	free_extent_buffer(fs_info->chunk_root->node);
-+out_devices:
-+	close_all_devices(fs_info);
-+out_cleanup:
-+	extent_io_tree_cleanup(&fs_info->extent_cache);
-+	extent_io_tree_cleanup(&fs_info->free_space_cache);
-+	extent_io_tree_cleanup(&fs_info->block_group_cache);
-+	extent_io_tree_cleanup(&fs_info->pinned_extents);
-+	extent_io_tree_cleanup(&fs_info->pending_del);
-+	extent_io_tree_cleanup(&fs_info->extent_ins);
-+out:
-+	free(tree_root);
-+	free(extent_root);
-+	free(chunk_root);
-+	free(dev_root);
-+	free(csum_root);
-+	free(fs_info);
-+	return NULL;
-+}
-+
-+static int search_iobuf(struct btrfs_root *root, void *iobuf,
-+			size_t iobuf_size, off_t offset)
-+{
-+	u64 gen = btrfs_super_generation(&root->fs_info->super_copy);
-+	u64 objectid = search_objectid;
-+	u32 size = btrfs_super_nodesize(&root->fs_info->super_copy);
-+	u8 level = root->fs_info->super_copy.root_level;
-+	size_t block_off = 0;
-+
-+	while (block_off < iobuf_size) {
-+		void *block = iobuf + block_off;
-+		struct btrfs_header *header = block;
-+		u64 h_byte, h_level, h_gen, h_owner;
-+
-+//		printf("searching %Lu\n", offset + block_off);
-+		h_byte = le64_to_cpu(header->bytenr);
-+		h_owner = le64_to_cpu(header->owner);
-+		h_level = header->level;
-+		h_gen = le64_to_cpu(header->generation);
-+
-+		if (h_owner != objectid)
-+			goto next;
-+		if (h_byte != (offset + block_off))
-+			goto next;
-+		if (h_level != level)
-+			goto next;
-+		if (csum_block(block, size)) {
-+			fprintf(stderr, "Well block %Lu seems good, "
-+				"but the csum doesn't match\n",
-+				h_byte);
-+			goto next;
-+		}
-+		if (h_gen != gen) {
-+			fprintf(stderr, "Well block %Lu seems great, "
-+				"but generation doesn't match, "
-+				"have=%Lu, want=%Lu\n", h_byte, h_gen,
-+				gen);
-+			goto next;
-+		}
-+		printf("Found tree root at %Lu\n", h_byte);
-+		return 0;
-+next:
-+		block_off += size;
-+	}
-+
-+	return 1;
-+}
-+
-+static int read_physical(struct btrfs_root *root, int fd, u64 offset,
-+			 u64 bytenr, u64 len)
-+{
-+	char *iobuf = malloc(len);
-+	ssize_t done;
-+	size_t total_read = 0;
-+	int ret = 1;
-+
-+	if (!iobuf) {
-+		fprintf(stderr, "No memory\n");
-+		return -1;
-+	}
-+
-+	while (total_read < len) {
-+		done = pread64(fd, iobuf + total_read, len - total_read,
-+			       bytenr + total_read);
-+		if (done < 0) {
-+			fprintf(stderr, "Failed to read: %s\n",
-+				strerror(errno));
-+			ret = -1;
-+			goto out;
-+		}
-+		total_read += done;
-+	}
-+
-+	ret = search_iobuf(root, iobuf, total_read, offset);
-+out:
-+	free(iobuf);
-+	return ret;
-+}
-+
-+static int find_root(struct btrfs_root *root)
-+{
-+	struct btrfs_multi_bio *multi = NULL;
-+	struct btrfs_device *device;
-+	u64 metadata_offset = 0, metadata_size = 0;
-+	off_t offset = 0;
-+	off_t bytenr;
-+	int fd;
-+	int err;
-+	int ret = 1;
-+
-+	printf("Super think's the tree root is at %Lu, chunk root %Lu\n",
-+	       btrfs_super_root(&root->fs_info->super_copy),
-+	       btrfs_super_chunk_root(&root->fs_info->super_copy));
-+
-+	err = btrfs_next_metadata(&root->fs_info->mapping_tree,
-+				  &metadata_offset, &metadata_size);
-+	if (err)
-+		return ret;
-+
-+	offset = metadata_offset;
-+	while (1) {
-+		u64 map_length = 4096;
-+		u64 type;
-+
-+		if (offset >
-+		    btrfs_super_total_bytes(&root->fs_info->super_copy)) {
-+			printf("Went past the fs size, exiting");
-+			break;
-+		}
-+		if (offset >= (metadata_offset + metadata_size)) {
-+			err = btrfs_next_metadata(&root->fs_info->mapping_tree,
-+						  &metadata_offset,
-+						  &metadata_size);
-+			if (err) {
-+				printf("No more metdata to scan, exiting\n");
-+				break;
-+			}
-+			offset = metadata_offset;
-+		}
-+		err = __btrfs_map_block(&root->fs_info->mapping_tree, READ,
-+				      offset, &map_length, &type, &multi, 0);
-+		if (err) {
-+			offset += map_length;
-+			continue;
-+		}
-+
-+		if (!(type & BTRFS_BLOCK_GROUP_METADATA)) {
-+			offset += map_length;
-+			continue;
-+		}
-+
-+		device = multi->stripes[0].dev;
-+		fd = device->fd;
-+		bytenr = multi->stripes[0].physical;
-+		kfree(multi);
-+
-+		err = read_physical(root, fd, offset, bytenr, map_length);
-+		if (!err) {
-+			ret = 0;
-+			break;
-+		} else if (err < 0) {
-+			ret = err;
-+			break;
-+		}
-+		offset += map_length;
-+	}
-+	return ret;
-+}
-+
-+int main(int argc, char **argv)
-+{
-+	struct btrfs_root *root;
-+	int dev_fd;
-+	int opt;
-+	int ret;
-+
-+	while ((opt = getopt(argc, argv, "vo:")) != -1) {
-+		switch(opt) {
-+			case 'v':
-+				verbose++;
-+				break;
-+			case 'o':
-+				errno = 0;
-+				search_objectid = (u64)strtoll(optarg, NULL,
-+							       10);
-+				if (errno) {
-+					fprintf(stderr, "Error parsing "
-+						"objectid\n");
-+					exit(1);
-+				}
-+				break;
-+			default:
-+				usage();
-+				exit(1);
-+		}
-+	}
-+
-+	if (optind >= argc) {
-+		usage();
-+		exit(1);
-+	}
-+
-+	dev_fd = open(argv[optind], O_RDONLY);
-+	if (dev_fd < 0) {
-+		fprintf(stderr, "Failed to open device %s\n", argv[optind]);
-+		exit(1);
-+	}
-+
-+	root = open_ctree_broken(dev_fd, argv[optind]);
-+	close(dev_fd);
-+	if (!root)
-+		exit(1);
-+
-+	csum_size = btrfs_super_csum_size(&root->fs_info->super_copy);
-+	ret = find_root(root);
-+	close_ctree(root);
-+	return ret;
-+}
-diff --git a/help.c b/help.c
-new file mode 100644
-index 0000000..6d04293
---- /dev/null
-+++ b/help.c
-@@ -0,0 +1,214 @@
-+/*
-+ * 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.
-+ */
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+
-+#include "commands.h"
-+
-+extern char argv0_buf[ARGV0_BUF_SIZE];
-+
-+#define USAGE_SHORT		1U
-+#define USAGE_LONG		2U
-+#define USAGE_OPTIONS		4U
-+#define USAGE_LISTING		8U
-+
-+static int do_usage_one_command(const char * const *usagestr,
-+				unsigned int flags, FILE *outf)
-+{
-+	int pad = 4;
-+
-+	if (!usagestr || !*usagestr)
-+		return -1;
-+
-+	fprintf(outf, "%s%s\n", (flags & USAGE_LISTING) ? "    " : "usage: ",
-+		*usagestr++);
-+
-+	/* a short one-line description (mandatory) */
-+	if ((flags & USAGE_SHORT) == 0)
-+		return 0;
-+	else if (!*usagestr)
-+		return -2;
-+
-+	if (flags & USAGE_LISTING)
-+		pad = 8;
-+	else
-+		fputc('\n', outf);
-+
-+	fprintf(outf, "%*s%s\n", pad, "", *usagestr++);
-+
-+	/* a long (possibly multi-line) description (optional) */
-+	if (!*usagestr || ((flags & USAGE_LONG) == 0))
-+		return 0;
-+
-+	if (**usagestr)
-+		fputc('\n', outf);
-+	while (*usagestr && **usagestr)
-+		fprintf(outf, "%*s%s\n", pad, "", *usagestr++);
-+
-+	/* options (optional) */
-+	if (!*usagestr || ((flags & USAGE_OPTIONS) == 0))
-+		return 0;
-+
-+	/*
-+	 * options (if present) should always (even if there is no long
-+	 * description) be prepended with an empty line, skip it
-+	 */
-+	usagestr++;
-+
-+	fputc('\n', outf);
-+	while (*usagestr)
-+		fprintf(outf, "%*s%s\n", pad, "", *usagestr++);
-+
-+	return 0;
-+}
-+
-+static int usage_command_internal(const char * const *usagestr,
-+				  const char *token, int full, int lst,
-+				  FILE *outf)
-+{
-+	unsigned int flags = USAGE_SHORT;
-+	int ret;
-+
-+	if (full)
-+		flags |= USAGE_LONG | USAGE_OPTIONS;
-+	if (lst)
-+		flags |= USAGE_LISTING;
-+
-+	ret = do_usage_one_command(usagestr, flags, outf);
-+	switch (ret) {
-+	case -1:
-+		fprintf(outf, "No usage for '%s'\n", token);
-+		break;
-+	case -2:
-+		fprintf(outf, "No short description for '%s'\n", token);
-+		break;
-+	}
-+
-+	return ret;
-+}
-+
-+static void usage_command_usagestr(const char * const *usagestr,
-+				   const char *token, int full, int err)
-+{
-+	FILE *outf = err ? stderr : stdout;
-+	int ret;
-+
-+	ret = usage_command_internal(usagestr, token, full, 0, outf);
-+	if (!ret)
-+		fputc('\n', outf);
-+}
-+
-+void usage_command(const struct cmd_struct *cmd, int full, int err)
-+{
-+	usage_command_usagestr(cmd->usagestr, cmd->token, full, err);
-+}
-+
-+void usage(const char * const *usagestr)
-+{
-+	usage_command_usagestr(usagestr, NULL, 1, 1);
-+	exit(129);
-+}
-+
-+static void usage_command_group_internal(const struct cmd_group *grp, int full,
-+					 FILE *outf)
-+{
-+	const struct cmd_struct *cmd = grp->commands;
-+	int do_sep = 0;
-+
-+	for (; cmd->token; cmd++) {
-+		if (cmd->hidden)
-+			continue;
-+
-+		if (full && cmd != grp->commands)
-+			fputc('\n', outf);
-+
-+		if (!cmd->next) {
-+			if (do_sep) {
-+				fputc('\n', outf);
-+				do_sep = 0;
-+			}
-+
-+			usage_command_internal(cmd->usagestr, cmd->token, full,
-+					       1, outf);
-+			continue;
-+		}
-+
-+		/* this is an entry point to a nested command group */
-+
-+		if (!full && cmd != grp->commands)
-+			fputc('\n', outf);
-+
-+		usage_command_group_internal(cmd->next, full, outf);
-+
-+		if (!full)
-+			do_sep = 1;
-+	}
-+}
-+
-+void usage_command_group(const struct cmd_group *grp, int full, int err)
-+{
-+	const char * const *usagestr = grp->usagestr;
-+	FILE *outf = err ? stderr : stdout;
-+
-+	if (usagestr && *usagestr) {
-+		fprintf(outf, "usage: %s\n", *usagestr++);
-+		while (*usagestr)
-+			fprintf(outf, "   or: %s\n", *usagestr++);
-+	}
-+
-+	fputc('\n', outf);
-+	usage_command_group_internal(grp, full, outf);
-+	fputc('\n', outf);
-+
-+	if (grp->infostr)
-+		fprintf(outf, "%s\n", grp->infostr);
-+}
-+
-+void help_unknown_token(const char *arg, const struct cmd_group *grp)
-+{
-+	fprintf(stderr, "%s: unknown token '%s'\n", argv0_buf, arg);
-+	usage_command_group(grp, 0, 1);
-+	exit(1);
-+}
-+
-+void help_ambiguous_token(const char *arg, const struct cmd_group *grp)
-+{
-+	const struct cmd_struct *cmd = grp->commands;
-+
-+	fprintf(stderr, "%s: ambiguous token '%s'\n", argv0_buf, arg);
-+	fprintf(stderr, "\nDid you mean one of these ?\n");
-+
-+	for (; cmd->token; cmd++) {
-+		if (!prefixcmp(cmd->token, arg))
-+			fprintf(stderr, "\t%s\n", cmd->token);
-+	}
-+
-+	exit(1);
-+}
-+
-+void help_command_group(const struct cmd_group *grp, int argc, char **argv)
-+{
-+	int full = 0;
-+
-+	if (argc > 1) {
-+		if (!strcmp(argv[1], "--full"))
-+			full = 1;
-+	}
-+
-+	usage_command_group(grp, full, 0);
-+}
-diff --git a/ioctl-test.c b/ioctl-test.c
-new file mode 100644
-index 0000000..1c27d61
---- /dev/null
-+++ b/ioctl-test.c
-@@ -0,0 +1,37 @@
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include "kerncompat.h"
-+#include "ioctl.h"
-+
-+unsigned long ioctls[] = {
-+	BTRFS_IOC_SNAP_CREATE,
-+	BTRFS_IOC_DEFRAG,
-+	BTRFS_IOC_RESIZE,
-+	BTRFS_IOC_SCAN_DEV,
-+	BTRFS_IOC_TRANS_START,
-+	BTRFS_IOC_TRANS_END,
-+	BTRFS_IOC_SYNC,
-+	BTRFS_IOC_CLONE,
-+	BTRFS_IOC_ADD_DEV,
-+	BTRFS_IOC_RM_DEV,
-+	BTRFS_IOC_BALANCE,
-+	BTRFS_IOC_SUBVOL_CREATE,
-+	BTRFS_IOC_SNAP_DESTROY,
-+	BTRFS_IOC_DEFRAG_RANGE,
-+	BTRFS_IOC_TREE_SEARCH,
-+	BTRFS_IOC_INO_LOOKUP,
-+	BTRFS_IOC_DEFAULT_SUBVOL,
-+	BTRFS_IOC_SPACE_INFO,
-+	BTRFS_IOC_SNAP_CREATE_V2,
-+	0 };
-+
-+int main(int ac, char **av)
-+{
-+	int i = 0;
-+	while(ioctls[i]) {
-+		printf("%lu\n" ,ioctls[i]);
-+		i++;
-+	}
-+	return 0;
-+}
-+
-diff --git a/ioctl.h b/ioctl.h
-index a084f33..f2e5d8d 100644
---- a/ioctl.h
-+++ b/ioctl.h
-@@ -23,13 +23,256 @@
- 
- #define BTRFS_IOCTL_MAGIC 0x94
- #define BTRFS_VOL_NAME_MAX 255
--#define BTRFS_PATH_NAME_MAX 4087
- 
-+/* this should be 4k */
-+#define BTRFS_PATH_NAME_MAX 4087
- struct btrfs_ioctl_vol_args {
- 	__s64 fd;
- 	char name[BTRFS_PATH_NAME_MAX + 1];
- };
- 
-+#define BTRFS_SUBVOL_RDONLY		(1ULL << 1)
-+#define BTRFS_SUBVOL_NAME_MAX 4039
-+
-+struct btrfs_ioctl_vol_args_v2 {
-+	__s64 fd;
-+	__u64 transid;
-+	__u64 flags;
-+	__u64 unused[4];
-+	char name[BTRFS_SUBVOL_NAME_MAX + 1];
-+};
-+
-+#define BTRFS_FSID_SIZE 16
-+#define BTRFS_UUID_SIZE 16
-+
-+struct btrfs_scrub_progress {
-+	__u64 data_extents_scrubbed;
-+	__u64 tree_extents_scrubbed;
-+	__u64 data_bytes_scrubbed;
-+	__u64 tree_bytes_scrubbed;
-+	__u64 read_errors;
-+	__u64 csum_errors;
-+	__u64 verify_errors;
-+	__u64 no_csum;
-+	__u64 csum_discards;
-+	__u64 super_errors;
-+	__u64 malloc_errors;
-+	__u64 uncorrectable_errors;
-+	__u64 corrected_errors;
-+	__u64 last_physical;
-+	__u64 unverified_errors;
-+};
-+
-+#define BTRFS_SCRUB_READONLY	1
-+struct btrfs_ioctl_scrub_args {
-+	__u64 devid;				/* in */
-+	__u64 start;				/* in */
-+	__u64 end;				/* in */
-+	__u64 flags;				/* in */
-+	struct btrfs_scrub_progress progress;	/* out */
-+	/* pad to 1k */
-+	__u64 unused[(1024-32-sizeof(struct btrfs_scrub_progress))/8];
-+};
-+
-+#define BTRFS_DEVICE_PATH_NAME_MAX 1024
-+struct btrfs_ioctl_dev_info_args {
-+	__u64 devid;				/* in/out */
-+	__u8 uuid[BTRFS_UUID_SIZE];		/* in/out */
-+	__u64 bytes_used;			/* out */
-+	__u64 total_bytes;			/* out */
-+	__u64 unused[379];			/* pad to 4k */
-+	__u8 path[BTRFS_DEVICE_PATH_NAME_MAX];	/* out */
-+};
-+
-+struct btrfs_ioctl_fs_info_args {
-+	__u64 max_id;				/* out */
-+	__u64 num_devices;			/* out */
-+	__u8 fsid[BTRFS_FSID_SIZE];		/* out */
-+	__u64 reserved[124];			/* pad to 1k */
-+};
-+
-+/* balance control ioctl modes */
-+#define BTRFS_BALANCE_CTL_PAUSE		1
-+#define BTRFS_BALANCE_CTL_CANCEL	2
-+#define BTRFS_BALANCE_CTL_RESUME	3
-+
-+/*
-+ * this is packed, because it should be exactly the same as its disk
-+ * byte order counterpart (struct btrfs_disk_balance_args)
-+ */
-+struct btrfs_balance_args {
-+	__u64 profiles;
-+	__u64 usage;
-+	__u64 devid;
-+	__u64 pstart;
-+	__u64 pend;
-+	__u64 vstart;
-+	__u64 vend;
-+
-+	__u64 target;
-+
-+	__u64 flags;
-+
-+	__u64 unused[8];
-+} __attribute__ ((__packed__));
-+
-+struct btrfs_balance_progress {
-+	__u64 expected;
-+	__u64 considered;
-+	__u64 completed;
-+};
-+
-+#define BTRFS_BALANCE_STATE_RUNNING	(1ULL << 0)
-+#define BTRFS_BALANCE_STATE_PAUSE_REQ	(1ULL << 1)
-+#define BTRFS_BALANCE_STATE_CANCEL_REQ	(1ULL << 2)
-+
-+struct btrfs_ioctl_balance_args {
-+	__u64 flags;				/* in/out */
-+	__u64 state;				/* out */
-+
-+	struct btrfs_balance_args data;		/* in/out */
-+	struct btrfs_balance_args meta;		/* in/out */
-+	struct btrfs_balance_args sys;		/* in/out */
-+
-+	struct btrfs_balance_progress stat;	/* out */
-+
-+	__u64 unused[72];			/* pad to 1k */
-+};
-+
-+struct btrfs_ioctl_search_key {
-+	/* which root are we searching.  0 is the tree of tree roots */
-+	__u64 tree_id;
-+
-+	/* keys returned will be >= min and <= max */
-+	__u64 min_objectid;
-+	__u64 max_objectid;
-+
-+	/* keys returned will be >= min and <= max */
-+	__u64 min_offset;
-+	__u64 max_offset;
-+
-+	/* max and min transids to search for */
-+	__u64 min_transid;
-+	__u64 max_transid;
-+
-+	/* keys returned will be >= min and <= max */
-+	__u32 min_type;
-+	__u32 max_type;
-+
-+	/*
-+	 * how many items did userland ask for, and how many are we
-+	 * returning
-+	 */
-+	__u32 nr_items;
-+
-+	/* align to 64 bits */
-+	__u32 unused;
-+
-+	/* some extra for later */
-+	__u64 unused1;
-+	__u64 unused2;
-+	__u64 unused3;
-+	__u64 unused4;
-+};
-+
-+struct btrfs_ioctl_search_header {
-+	__u64 transid;
-+	__u64 objectid;
-+	__u64 offset;
-+	__u32 type;
-+	__u32 len;
-+} __attribute__((may_alias));
-+
-+#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key))
-+/*
-+ * the buf is an array of search headers where
-+ * each header is followed by the actual item
-+ * the type field is expanded to 32 bits for alignment
-+ */
-+struct btrfs_ioctl_search_args {
-+	struct btrfs_ioctl_search_key key;
-+	char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
-+};
-+
-+#define BTRFS_INO_LOOKUP_PATH_MAX 4080
-+struct btrfs_ioctl_ino_lookup_args {
-+	__u64 treeid;
-+	__u64 objectid;
-+	char name[BTRFS_INO_LOOKUP_PATH_MAX];
-+};
-+
-+/* flags for the defrag range ioctl */
-+#define BTRFS_DEFRAG_RANGE_COMPRESS 1
-+#define BTRFS_DEFRAG_RANGE_START_IO 2
-+
-+struct btrfs_ioctl_defrag_range_args {
-+	/* start of the defrag operation */
-+	__u64 start;
-+
-+	/* number of bytes to defrag, use (u64)-1 to say all */
-+	__u64 len;
-+
-+	/*
-+	 * flags for the operation, which can include turning
-+	 * on compression for this one defrag
-+	 */
-+	__u64 flags;
-+
-+	/*
-+	 * any extent bigger than this will be considered
-+	 * already defragged.  Use 0 to take the kernel default
-+	 * Use 1 to say every single extent must be rewritten
-+	 */
-+	__u32 extent_thresh;
-+
-+	/*
-+	 * which compression method to use if turning on compression
-+	 * for this defrag operation.  If unspecified, zlib will
-+	 * be used
-+	 */
-+	__u32 compress_type;
-+
-+	/* spare for later */
-+	__u32 unused[4];
-+};
-+
-+struct btrfs_ioctl_space_info {
-+	__u64 flags;
-+	__u64 total_bytes;
-+	__u64 used_bytes;
-+};
-+
-+struct btrfs_ioctl_space_args {
-+	__u64 space_slots;
-+	__u64 total_spaces;
-+	struct btrfs_ioctl_space_info spaces[0];
-+};
-+
-+struct btrfs_data_container {
-+	__u32	bytes_left;	/* out -- bytes not needed to deliver output */
-+	__u32	bytes_missing;	/* out -- additional bytes needed for result */
-+	__u32	elem_cnt;	/* out */
-+	__u32	elem_missed;	/* out */
-+	__u64	val[0];		/* out */
-+};
-+
-+struct btrfs_ioctl_ino_path_args {
-+	__u64				inum;		/* in */
-+	__u64				size;		/* in */
-+	__u64				reserved[4];
-+	/* struct btrfs_data_container	*fspath;	   out */
-+	__u64				fspath;		/* out */
-+};
-+
-+struct btrfs_ioctl_logical_ino_args {
-+	__u64				logical;	/* in */
-+	__u64				size;		/* in */
-+	__u64				reserved[4];
-+	/* struct btrfs_data_container	*inodes;	out   */
-+	__u64				inodes;
-+};
-+
-+/* BTRFS_IOC_SNAP_CREATE is no longer used by the btrfs command */
- #define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
- 				   struct btrfs_ioctl_vol_args)
- #define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \
-@@ -56,4 +299,36 @@ struct btrfs_ioctl_vol_args {
- /* 13 is for CLONE_RANGE */
- #define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
- 				   struct btrfs_ioctl_vol_args)
-+#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
-+				   struct btrfs_ioctl_vol_args)
-+#define BTRFS_IOC_DEFRAG_RANGE _IOW(BTRFS_IOCTL_MAGIC, 16, \
-+				struct btrfs_ioctl_defrag_range_args)
-+#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
-+				   struct btrfs_ioctl_search_args)
-+#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \
-+				   struct btrfs_ioctl_ino_lookup_args)
-+#define BTRFS_IOC_DEFAULT_SUBVOL _IOW(BTRFS_IOCTL_MAGIC, 19, u64)
-+#define BTRFS_IOC_SPACE_INFO _IOWR(BTRFS_IOCTL_MAGIC, 20, \
-+				    struct btrfs_ioctl_space_args)
-+#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
-+				   struct btrfs_ioctl_vol_args_v2)
-+#define BTRFS_IOC_SCRUB _IOWR(BTRFS_IOCTL_MAGIC, 27, \
-+				struct btrfs_ioctl_scrub_args)
-+#define BTRFS_IOC_SCRUB_CANCEL _IO(BTRFS_IOCTL_MAGIC, 28)
-+#define BTRFS_IOC_SCRUB_PROGRESS _IOWR(BTRFS_IOCTL_MAGIC, 29, \
-+					struct btrfs_ioctl_scrub_args)
-+#define BTRFS_IOC_DEV_INFO _IOWR(BTRFS_IOCTL_MAGIC, 30, \
-+					struct btrfs_ioctl_dev_info_args)
-+#define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \
-+                                 struct btrfs_ioctl_fs_info_args)
-+#define BTRFS_IOC_BALANCE_V2 _IOWR(BTRFS_IOCTL_MAGIC, 32, \
-+				   struct btrfs_ioctl_balance_args)
-+#define BTRFS_IOC_BALANCE_CTL _IOW(BTRFS_IOCTL_MAGIC, 33, int)
-+#define BTRFS_IOC_BALANCE_PROGRESS _IOR(BTRFS_IOCTL_MAGIC, 34, \
-+					struct btrfs_ioctl_balance_args)
-+#define BTRFS_IOC_INO_PATHS _IOWR(BTRFS_IOCTL_MAGIC, 35, \
-+					struct btrfs_ioctl_ino_path_args)
-+#define BTRFS_IOC_LOGICAL_INO _IOWR(BTRFS_IOCTL_MAGIC, 36, \
-+					struct btrfs_ioctl_ino_path_args)
-+
- #endif
-diff --git a/kerncompat.h b/kerncompat.h
-index e4c8ce0..46236cd 100644
---- a/kerncompat.h
-+++ b/kerncompat.h
-@@ -42,7 +42,11 @@
- #define GFP_NOFS 0
- #define __read_mostly
- #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-+
-+#ifndef ULONG_MAX
- #define ULONG_MAX       (~0UL)
-+#endif
-+
- #define BUG() abort()
- #ifdef __CHECKER__
- #define __force    __attribute__((force))
-diff --git a/man/Makefile b/man/Makefile
-index 4e8893b..4a90b75 100644
---- a/man/Makefile
-+++ b/man/Makefile
-@@ -7,13 +7,16 @@ mandir = $(prefix)/man
- man8dir = $(mandir)/man8
- 
- MANPAGES = mkfs.btrfs.8.gz btrfsctl.8.gz btrfsck.8.gz btrfs-image.8.gz \
--	   btrfs-show.8.gz
-+	   btrfs-show.8.gz btrfs.8.gz
- 
- all: $(MANPAGES)
- 
- mkfs.btrfs.8.gz: mkfs.btrfs.8.in
- 	$(GZIP) -n -c mkfs.btrfs.8.in > mkfs.btrfs.8.gz
- 
-+btrfs.8.gz: btrfs.8.in
-+	$(GZIP) -n -c btrfs.8.in > btrfs.8.gz
-+
- btrfsctl.8.gz: btrfsctl.8.in
- 	$(GZIP) -n -c btrfsctl.8.in > btrfsctl.8.gz
- 
-diff --git a/man/btrfs-show.8.in b/man/btrfs-show.8.in
-index dd0b147..cb98b68 100644
---- a/man/btrfs-show.8.in
-+++ b/man/btrfs-show.8.in
-@@ -3,6 +3,9 @@
- btrfs-show \- scan the /dev directory for btrfs partitions and print results.
- .SH SYNOPSIS
- .B btrfs-show
-+.SH NOTE
-+.B btrfs-show
-+is deprecated. Please consider to switch to the btrfs utility.	
- .SH DESCRIPTION
- .B btrfs-show
- is used to scan the /dev directory for btrfs partitions and display brief
-diff --git a/man/btrfs.8.in b/man/btrfs.8.in
-new file mode 100644
-index 0000000..be478e0
---- /dev/null
-+++ b/man/btrfs.8.in
-@@ -0,0 +1,322 @@
-+.TH BTRFS 8 "" "btrfs" "btrfs"
-+.\"
-+.\" Man page written by Goffredo Baroncelli <kreijack@inwind.it> (Feb 2010)
-+.\"
-+.SH NAME
-+btrfs \- control a btrfs filesystem
-+.SH SYNOPSIS
-+\fBbtrfs\fP \fBsubvolume snapshot\fP\fI [-r] <source> [<dest>/]<name>\fP
-+.PP
-+\fBbtrfs\fP \fBsubvolume delete\fP\fI <subvolume>\fP
-+.PP
-+\fBbtrfs\fP \fBsubvolume create\fP\fI [<dest>/]<name>\fP
-+.PP
-+\fBbtrfs\fP \fBsubvolume list\fP\fI [-p] <path>\fP
-+.PP
-+\fBbtrfs\fP \fBsubvolume set-default\fP\fI <id> <path>\fP
-+.PP
-+\fBbtrfs\fP \fBsubvolume get-default\fP\fI <path>\fP
-+.PP
-+\fBbtrfs\fP \fBfilesystem sync\fP\fI <path> \fP
-+.PP
-+\fBbtrfs\fP \fBfilesystem resize\fP\fI [+/\-]<size>[gkm]|max <filesystem>\fP
-+.PP
-+\fBbtrfs\fP \fBfilesystem label\fP\fI <dev> [newlabel]\fP
-+.PP
-+\fBbtrfs\fP \fBfilesystem defrag\fP\fI [options] <file>|<dir> [<file>|<dir>...]\fP
-+.PP
-+\fBbtrfs\fP \fBsubvolume find-new\fP\fI <subvolume> <last_gen>\fP
-+.PP
-+\fBbtrfs\fP \fBfilesystem balance\fP\fI <path> \fP
-+.PP
-+\fBbtrfs\fP \fBfilesystem defragment\fP\fI <file>|<dir> [<file>|<dir>...]\fP
-+.PP
-+\fBbtrfs\fP \fBdevice scan\fP\fI [--all-devices|<device> [<device>...]]\fP
-+.PP
-+\fBbtrfs\fP \fBdevice show\fP\fI [--all-devices|<uuid>|<label>]\fP
-+.PP
-+\fBbtrfs\fP \fBdevice add\fP\fI <device> [<device>...] <path> \fP
-+.PP
-+\fBbtrfs\fP \fBdevice delete\fP\fI <device> [<device>...] <path> \fP
-+.PP
-+\fBbtrfs\fP \fBscrub start\fP [-Bdqru] {\fI<path>\fP|\fI<device>\fP}
-+.PP
-+\fBbtrfs\fP \fBscrub cancel\fP {\fI<path>\fP|\fI<device>\fP}
-+.PP
-+\fBbtrfs\fP \fBscrub resume\fP [-Bdqru] {\fI<path>\fP|\fI<device>\fP}
-+.PP
-+\fBbtrfs\fP \fBscrub status\fP [-d] {\fI<path>\fP|\fI<device>\fP}
-+.PP
-+\fBbtrfs\fP \fBinspect-internal inode-resolve\fP [-v] \fI<inode>\fP \fI<path>\fP
-+.PP
-+\fBbtrfs\fP \fBinspect-internal logical-resolve\fP
-+[-Pv] \fI<logical>\fP \fI<path>\fP
-+.PP
-+\fBbtrfs\fP \fBhelp|\-\-help|\-h \fP\fI\fP
-+.PP
-+\fBbtrfs\fP \fB<command> \-\-help \fP\fI\fP
-+.PP
-+.SH DESCRIPTION
-+.B btrfs
-+is used to control the filesystem and the files and directories stored. It is
-+the tool to create or destroy a snapshot or a subvolume for the
-+filesystem, to defrag a file or a directory, flush the data to the disk,
-+to resize the filesystem, to scan the device.
-+
-+It is possible to abbreviate the commands unless the commands  are ambiguous.
-+For example: it is possible to run
-+.I btrfs sub snaps
-+instead of
-+.I btrfs subvolume snapshot.
-+But
-+.I btrfs file s
-+is not allowed, because
-+.I file s
-+may be interpreted both as
-+.I filesystem show
-+and as
-+.I filesystem sync.
-+In this case
-+.I btrfs
-+returnsfilesystem sync
-+If a command is terminated by
-+.I --help
-+, the detailed help is showed. If the passed command matches more commands,
-+detailed help of all the matched commands is showed. For example
-+.I btrfs dev --help
-+shows the help of all
-+.I device*
-+commands.
-+
-+.SH COMMANDS
-+.TP
-+
-+\fBsubvolume snapshot\fR\fI [-r] <source> [<dest>/]<name>\fR
-+Create a writable/readonly snapshot of the subvolume \fI<source>\fR with the
-+name \fI<name>\fR in the \fI<dest>\fR directory. If \fI<source>\fR is not a
-+subvolume, \fBbtrfs\fR returns an error. If \fI-r\fR is given, the snapshot
-+will be readonly.
-+.TP
-+
-+\fBsubvolume delete\fR\fI <subvolume>\fR
-+Delete the subvolume \fI<subvolume>\fR. If \fI<subvolume>\fR is not a
-+subvolume, \fBbtrfs\fR returns an error.
-+.TP
-+
-+\fBsubvolume create\fR\fI [<dest>/]<name>\fR
-+Create a subvolume in \fI<dest>\fR (or in the current directory if
-+\fI<dest>\fR is omitted).
-+.TP
-+
-+\fBsubvolume list\fR\fI [-p] <path>\fR
-+List the subvolumes present in the filesystem \fI<path>\fR. For every
-+subvolume the following information is shown by default.
-+ID <ID> top level <ID> path <path>
-+where path is the relative path of the subvolume to the \fItop level\fR
-+subvolume.
-+The subvolume's ID may be used by the \fBsubvolume set-default\fR command, or
-+at mount time via the \fIsubvol=\fR option.
-+If \fI-p\fR is given, then \fIparent <ID>\fR is added to the output between ID
-+and top level. The parent's ID may be used at mount time via the
-+\fIsubvolrootid=\fR option.
-+.TP
-+
-+\fBsubvolume set-default\fR\fI <id> <path>\fR
-+Set the subvolume of the filesystem \fI<path>\fR which is mounted as 
-+\fIdefault\fR. The subvolume is identified by \fI<id>\fR, which 
-+is returned by the \fBsubvolume list\fR command.
-+.TP
-+
-+\fBsubvolume get-default\fR\fI <path>\fR
-+Get the default subvolume of the filesystem \fI<path>\fR. The output format
-+is similar to \fBsubvolume list\fR command.
-+.TP
-+
-+\fBfilesystem defragment\fP -c[zlib|lzo] [-l \fIlen\fR] [-s \fIstart\fR] [-t \fIsize\fR] -[vf] <\fIfile\fR>|<\fIdir\fR> [<\fIfile\fR>|<\fIdir\fR>...]
-+
-+Defragment file data and/or directory metadata. To defragment all files in a
-+directory you have to specify each one on its own or use your shell wildcards.
-+
-+The start position and the number of bytes to deframention can be specified by \fIstart\fR and \fIlen\fR. Any extent bigger than \fIthresh\fR will be considered already defragged. Use 0 to take the kernel default, and use 1 to say eveery single extent must be rewritten. You can also turn on compression in defragment operations.
-+
-+\fB-v\fP be verbose
-+
-+\fB-c\fP compress file contents while defragmenting
-+
-+\fB-f\fP flush filesystem after defragmenting
-+
-+\fB-s start\fP defragment only from byte \fIstart\fR onward
-+
-+\fB-l len\fP defragment only up to \fIlen\fR bytes
-+
-+\fB-t size\fP defragment only files at least \fIsize\fR bytes big
-+
-+NOTE: defragmenting with kernels up to 2.6.37 will unlink COW-ed copies of data, don't 
-+use it if you use snapshots, have de-duplicated your data or made copies with 
-+\fBcp --reflink\fP.
-+\fBsubvolume find-new\fR\fI <subvolume> <last_gen>\fR
-+List the recently modified files in a subvolume, after \fI<last_gen>\fR ID.
-+.TP
-+
-+\fBfilesystem sync\fR\fI <path> \fR
-+Force a sync for the filesystem identified by \fI<path>\fR.
-+.TP
-+
-+.\"
-+.\" Some wording are extracted by the resize2fs man page
-+.\"
-+
-+\fBfilesystem resize\fR\fI [+/\-]<size>[gkm]|max <path>\fR
-+Resize a filesystem identified by \fI<path>\fR.
-+The \fI<size>\fR parameter specifies the new size of the filesystem.
-+If the prefix \fI+\fR or \fI\-\fR is present the size is increased or decreased
-+by the quantity \fI<size>\fR.
-+If no units are specified, the unit of the \fI<size>\fR parameter defaults to
-+bytes. Optionally, the size parameter may be suffixed by one of the following
-+the units designators: 'K', 'M', or 'G', kilobytes, megabytes, or gigabytes,
-+respectively.
-+
-+If 'max' is passed, the filesystem will occupy all available space on the
-+volume(s).
-+
-+The \fBresize\fR command \fBdoes not\fR manipulate the size of underlying
-+partition.  If you wish to enlarge/reduce a filesystem, you must make sure you
-+can expand the partition before enlarging the filesystem and shrink the
-+partition after reducing the size of the filesystem.
-+.TP
-+
-+\fBbtrfs\fP \fBfilesystem label\fP\fI <dev> [newlabel]\fP
-+Show or update the label of a filesystem. \fI<dev>\fR is used to identify the
-+filesystem. 
-+If a \fInewlabel\fR optional argument is passed, the label is changed. The
-+following costraints exist for a label:
-+.IP
-+- the maximum allowable lenght shall be less or equal than 256 chars
-+.IP
-+- the label shall not  contain the '/' or '\\' characters.
-+
-+NOTE: Currently there are the following limitations:
-+.IP
-+- the filesystem has to be unmounted
-+.IP
-+- the filesystem should not have more than one device.
-+.TP
-+
-+\fBfilesystem show\fR [--all-devices|<uuid>|<label>]\fR
-+Show the btrfs filesystem with some additional info. If no \fIUUID\fP or 
-+\fIlabel\fP is passed, \fBbtrfs\fR show info of all the btrfs filesystem.
-+If \fB--all-devices\fP is passed, all the devices under /dev are scanned;
-+otherwise the devices list is extracted from the /proc/partitions file.
-+.TP
-+
-+\fBdevice balance\fR \fI<path>\fR
-+Balance the chunks of the filesystem identified by \fI<path>\fR
-+across the devices.
-+.TP
-+
-+\fBdevice add\fR\fI <dev> [<dev>..] <path>\fR
-+Add device(s) to the filesystem identified by \fI<path>\fR.
-+.TP
-+
-+\fBdevice delete\fR\fI <dev> [<dev>..] <path>\fR
-+Remove device(s) from a filesystem identified by \fI<path>\fR.
-+.TP
-+
-+\fBdevice scan\fR \fI[--all-devices|<device> [<device>...]\fR
-+If one or more devices are passed, these are scanned for a btrfs filesystem. 
-+If no devices are passed, \fBbtrfs\fR scans all the block devices listed
-+in the /proc/partitions file.
-+Finally, if \fB--all-devices\fP is passed, all the devices under /dev are 
-+scanned.
-+.TP
-+
-+\fBscrub start\fP [-Bdqru] {\fI<path>\fP|\fI<device>\fP}
-+Start a scrub on all devices of the filesystem identified by \fI<path>\fR or on
-+a single \fI<device>\fR. Without options, scrub is started as a background
-+process. Progress can be obtained with the \fBscrub status\fR command. Scrubbing
-+involves reading all data from all disks and verifying checksums. Errors are
-+corrected along the way if possible.
-+.RS
-+
-+\fIOptions\fR
-+.IP -B 5
-+Do not background and print scrub statistics when finished.
-+.IP -d 5
-+Print separate statistics for each device of the filesystem (-B only).
-+.IP -q 5
-+Quiet. Omit error messages and statistics.
-+.IP -r 5
-+Read only mode. Do not attempt to correct anything.
-+.IP -u 5
-+Scrub unused space as well. (NOT IMPLEMENTED)
-+.RE
-+.TP
-+
-+\fBscrub cancel\fP {\fI<path>\fP|\fI<device>\fP}
-+If a scrub is running on the filesystem identified by \fI<path>\fR, cancel it.
-+Progress is saved in the scrub progress file and scrubbing can be resumed later
-+using the \fBscrub resume\fR command.
-+If a \fI<device>\fR is given, the corresponding filesystem is found and
-+\fBscrub cancel\fP behaves as if it was called on that filesystem.
-+.TP
-+
-+\fBscrub resume\fP [-Bdqru] {\fI<path>\fP|\fI<device>\fP}
-+Resume a canceled or interrupted scrub cycle on the filesystem identified by
-+\fI<path>\fR or on a given \fI<device>\fR. Does not start a new scrub if the
-+last scrub finished successfully.
-+.RS
-+
-+\fIOptions\fR
-+.TP
-+see \fBscrub start\fP.
-+.RE
-+.TP
-+
-+\fBscrub status\fP [-d] {\fI<path>\fP|\fI<device>\fP}
-+Show status of a running scrub for the filesystem identified by \fI<path>\fR or
-+for the specified \fI<device>\fR.
-+If no scrub is running, show statistics of the last finished or canceled scrub
-+for that filesystem or device.
-+.RS
-+
-+\fIOptions\fR
-+.IP -d 5
-+Print separate statistics for each device of the filesystem.
-+.RE
-+.TP
-+
-+\fBinspect-internal inode-resolve\fP [-v] \fI<inode>\fP \fI<path>\fP
-+Resolves an <inode> in subvolume <path> to all filesystem paths.
-+.RS
-+
-+\fIOptions\fR
-+.IP -v 5
-+verbose mode. print count of returned paths and ioctl() return value
-+.RE
-+.TP
-+
-+\fBinspect-internal logical-resolve\fP [-Pv] \fI<logical>\fP \fI<path>\fP
-+Resolves a <logical> address in the filesystem mounted at <path> to all inodes.
-+By default, each inode is then resolved to a file system path (similar to the
-+\fBinode-resolve\fP subcommand).
-+.RS
-+
-+\fIOptions\fR
-+.IP -P 5
-+skip the path resolving and print the inodes instead
-+.IP -v 5
-+verbose mode. print count of returned paths and all ioctl() return values
-+.RE
-+
-+.SH EXIT STATUS
-+\fBbtrfs\fR returns a zero exist status if it succeeds. Non zero is returned in
-+case of failure.
-+
-+.SH AVAILABILITY
-+.B btrfs
-+is part of btrfs-progs. Btrfs filesystem is currently under heavy development,
-+and not suitable for any uses other than benchmarking and review.
-+Please refer to the btrfs wiki http://btrfs.wiki.kernel.org for
-+further details.
-+.SH SEE ALSO
-+.BR mkfs.btrfs (8)
-diff --git a/man/btrfsctl.8.in b/man/btrfsctl.8.in
-index c2d4488..8705fa6 100644
---- a/man/btrfsctl.8.in
-+++ b/man/btrfsctl.8.in
-@@ -10,6 +10,9 @@ btrfsctl \- control a btrfs filesystem
- [ \fB \-A\fP\fI device\fP ]
- [ \fB \-a\fP ]
- [ \fB \-c\fP ]
-+.SH NOTE
-+B btrfsctl
-+is deprecated. Please consider to switch to the btrfs utility.
- .SH DESCRIPTION
- .B btrfsctl
- is used to control the filesystem and the files and directories stored. It is the tool to create a new snapshot for the filesystem.
-diff --git a/man/mkfs.btrfs.8.in b/man/mkfs.btrfs.8.in
-index 1e14c6c..432db1b 100644
---- a/man/mkfs.btrfs.8.in
-+++ b/man/mkfs.btrfs.8.in
-@@ -9,6 +9,7 @@ mkfs.btrfs \- create an btrfs filesystem
- [ \fB \-l\fP\fI leafsize\fP ]
- [ \fB \-L\fP\fI label\fP ]
- [ \fB \-m\fP\fI metadata profile\fP ]
-+[ \fB \-M\fP\fI mixed data+metadata\fP ]
- [ \fB \-n\fP\fI nodesize\fP ]
- [ \fB \-s\fP\fI sectorsize\fP ]
- [ \fB \-h\fP ]
-@@ -45,6 +46,12 @@ Specify a label for the filesystem.
- Specify how metadata must be spanned across the devices specified. Valid
- values are raid0, raid1, raid10 or single.
- .TP
-+\fB\-M\fR, \fB\-\-mixed\fR
-+Mix data and metadata chunks together for more efficient space 
-+utilization.  This feature incurs a performance penalty in
-+larger filesystems.  It is recommended for use with filesystems
-+of 1 GiB or smaller.
-+.TP
- \fB\-n\fR, \fB\-\-nodesize \fIsize\fR
- Specify the nodesize. By default the value is set to the pagesize.
- .TP
-diff --git a/mkfs.c b/mkfs.c
-index 2e99b95..c531ef2 100644
---- a/mkfs.c
-+++ b/mkfs.c
-@@ -29,12 +29,14 @@
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/stat.h>
-+#include <sys/dir.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <getopt.h>
- #include <uuid/uuid.h>
- #include <linux/fs.h>
- #include <ctype.h>
-+#include <attr/xattr.h>
- #include "kerncompat.h"
- #include "ctree.h"
- #include "disk-io.h"
-@@ -43,11 +45,23 @@
- #include "utils.h"
- #include "version.h"
- 
-+static u64 index_cnt = 2;
-+
-+struct directory_name_entry {
-+	char *dir_name;
-+	char *path;
-+	ino_t inum;
-+	struct list_head list;
-+};
-+
- static u64 parse_size(char *s)
- {
- 	int len = strlen(s);
- 	char c;
- 	u64 mult = 1;
-+	u64 ret;
-+
-+	s = strdup(s);
- 
- 	if (!isdigit(s[len - 1])) {
- 		c = tolower(s[len - 1]);
-@@ -66,10 +80,12 @@ static u64 parse_size(char *s)
- 		}
- 		s[len - 1] = '\0';
- 	}
--	return atol(s) * mult;
-+	ret = atol(s) * mult;
-+	free(s);
-+	return ret;
- }
- 
--static int make_root_dir(struct btrfs_root *root)
-+static int make_root_dir(struct btrfs_root *root, int mixed)
- {
- 	struct btrfs_trans_handle *trans;
- 	struct btrfs_key location;
-@@ -88,30 +104,47 @@ static int make_root_dir(struct btrfs_root *root)
- 				     0, BTRFS_MKFS_SYSTEM_GROUP_SIZE);
- 	BUG_ON(ret);
- 
--	ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
--				&chunk_start, &chunk_size,
--				BTRFS_BLOCK_GROUP_METADATA);
--	BUG_ON(ret);
--	ret = btrfs_make_block_group(trans, root, 0,
--				     BTRFS_BLOCK_GROUP_METADATA,
--				     BTRFS_FIRST_CHUNK_TREE_OBJECTID,
--				     chunk_start, chunk_size);
--	BUG_ON(ret);
-+	if (mixed) {
-+		ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
-+					&chunk_start, &chunk_size,
-+					BTRFS_BLOCK_GROUP_METADATA |
-+					BTRFS_BLOCK_GROUP_DATA);
-+		BUG_ON(ret);
-+		ret = btrfs_make_block_group(trans, root, 0,
-+					     BTRFS_BLOCK_GROUP_METADATA |
-+					     BTRFS_BLOCK_GROUP_DATA,
-+					     BTRFS_FIRST_CHUNK_TREE_OBJECTID,
-+					     chunk_start, chunk_size);
-+		BUG_ON(ret);
-+		printf("Created a data/metadata chunk of size %llu\n", chunk_size);
-+	} else {
-+		ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
-+					&chunk_start, &chunk_size,
-+					BTRFS_BLOCK_GROUP_METADATA);
-+		BUG_ON(ret);
-+		ret = btrfs_make_block_group(trans, root, 0,
-+					     BTRFS_BLOCK_GROUP_METADATA,
-+					     BTRFS_FIRST_CHUNK_TREE_OBJECTID,
-+					     chunk_start, chunk_size);
-+		BUG_ON(ret);
-+	}
- 
- 	root->fs_info->system_allocs = 0;
- 	btrfs_commit_transaction(trans, root);
- 	trans = btrfs_start_transaction(root, 1);
- 	BUG_ON(!trans);
- 
--	ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
--				&chunk_start, &chunk_size,
--				BTRFS_BLOCK_GROUP_DATA);
--	BUG_ON(ret);
--	ret = btrfs_make_block_group(trans, root, 0,
--				     BTRFS_BLOCK_GROUP_DATA,
--				     BTRFS_FIRST_CHUNK_TREE_OBJECTID,
--				     chunk_start, chunk_size);
--	BUG_ON(ret);
-+	if (!mixed) {
-+		ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
-+					&chunk_start, &chunk_size,
-+					BTRFS_BLOCK_GROUP_DATA);
-+		BUG_ON(ret);
-+		ret = btrfs_make_block_group(trans, root, 0,
-+					     BTRFS_BLOCK_GROUP_DATA,
-+					     BTRFS_FIRST_CHUNK_TREE_OBJECTID,
-+					     chunk_start, chunk_size);
-+		BUG_ON(ret);
-+	}
- 
- 	ret = btrfs_make_root_dir(trans, root->fs_info->tree_root,
- 			      BTRFS_ROOT_TREE_DIR_OBJECTID);
-@@ -200,12 +233,26 @@ static int create_one_raid_group(struct btrfs_trans_handle *trans,
- 
- static int create_raid_groups(struct btrfs_trans_handle *trans,
- 			      struct btrfs_root *root, u64 data_profile,
--			      u64 metadata_profile)
-+			      int data_profile_opt, u64 metadata_profile,
-+			      int metadata_profile_opt, int mixed)
- {
- 	u64 num_devices = btrfs_super_num_devices(&root->fs_info->super_copy);
- 	u64 allowed;
- 	int ret;
- 
-+	/*
-+	 * Set default profiles according to number of added devices.
-+	 * For mixed groups defaults are single/single.
-+	 */
-+	if (!metadata_profile_opt && !mixed) {
-+		metadata_profile = (num_devices > 1) ?
-+			BTRFS_BLOCK_GROUP_RAID1 : BTRFS_BLOCK_GROUP_DUP;
-+	}
-+	if (!data_profile_opt && !mixed) {
-+		data_profile = (num_devices > 1) ?
-+			BTRFS_BLOCK_GROUP_RAID0 : 0; /* raid0 or single */
-+	}
-+
- 	if (num_devices == 1)
- 		allowed = BTRFS_BLOCK_GROUP_DUP;
- 	else if (num_devices >= 4) {
-@@ -214,21 +261,38 @@ static int create_raid_groups(struct btrfs_trans_handle *trans,
- 	} else
- 		allowed = BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1;
- 
-+	if (metadata_profile & ~allowed) {
-+		fprintf(stderr,	"unable to create FS with metadata "
-+			"profile %llu (%llu devices)\n", metadata_profile,
-+			num_devices);
-+		exit(1);
-+	}
-+	if (data_profile & ~allowed) {
-+		fprintf(stderr, "unable to create FS with data "
-+			"profile %llu (%llu devices)\n", data_profile,
-+			num_devices);
-+		exit(1);
-+	}
-+
- 	if (allowed & metadata_profile) {
-+		u64 meta_flags = BTRFS_BLOCK_GROUP_METADATA;
-+
- 		ret = create_one_raid_group(trans, root,
- 					    BTRFS_BLOCK_GROUP_SYSTEM |
- 					    (allowed & metadata_profile));
- 		BUG_ON(ret);
- 
--		ret = create_one_raid_group(trans, root,
--					    BTRFS_BLOCK_GROUP_METADATA |
-+		if (mixed)
-+			meta_flags |= BTRFS_BLOCK_GROUP_DATA;
-+
-+		ret = create_one_raid_group(trans, root, meta_flags |
- 					    (allowed & metadata_profile));
- 		BUG_ON(ret);
- 
- 		ret = recow_roots(trans, root);
- 		BUG_ON(ret);
- 	}
--	if (num_devices > 1 && (allowed & data_profile)) {
-+	if (!mixed && num_devices > 1 && (allowed & data_profile)) {
- 		ret = create_one_raid_group(trans, root,
- 					    BTRFS_BLOCK_GROUP_DATA |
- 					    (allowed & data_profile));
-@@ -274,8 +338,10 @@ static void print_usage(void)
- 	fprintf(stderr, "\t -l --leafsize size of btree leaves\n");
- 	fprintf(stderr, "\t -L --label set a label\n");
- 	fprintf(stderr, "\t -m --metadata metadata profile, values like data profile\n");
-+	fprintf(stderr, "\t -M --mixed mix metadata and data together\n");
- 	fprintf(stderr, "\t -n --nodesize size of btree nodes\n");
- 	fprintf(stderr, "\t -s --sectorsize min block allocation\n");
-+	fprintf(stderr, "\t -r --rootdir the source directory\n");
- 	fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION);
- 	exit(1);
- }
-@@ -291,15 +357,16 @@ static u64 parse_profile(char *s)
- 	if (strcmp(s, "raid0") == 0) {
- 		return BTRFS_BLOCK_GROUP_RAID0;
- 	} else if (strcmp(s, "raid1") == 0) {
--		return BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_DUP;
-+		return BTRFS_BLOCK_GROUP_RAID1;
- 	} else if (strcmp(s, "raid10") == 0) {
--		return BTRFS_BLOCK_GROUP_RAID10 | BTRFS_BLOCK_GROUP_DUP;
-+		return BTRFS_BLOCK_GROUP_RAID10;
- 	} else if (strcmp(s, "single") == 0) {
- 		return 0;
- 	} else {
- 		fprintf(stderr, "Unknown option %s\n", s);
- 		print_usage();
- 	}
-+	/* not reached */
- 	return 0;
- }
- 
-@@ -308,9 +375,9 @@ static char *parse_label(char *input)
- 	int i;
- 	int len = strlen(input);
- 
--	if (len > BTRFS_LABEL_SIZE) {
-+	if (len >= BTRFS_LABEL_SIZE) {
- 		fprintf(stderr, "Label %s is too long (max %d)\n", input,
--			BTRFS_LABEL_SIZE);
-+			BTRFS_LABEL_SIZE - 1);
- 		exit(1);
- 	}
- 	for (i = 0; i < len; i++) {
-@@ -328,13 +395,803 @@ static struct option long_options[] = {
- 	{ "leafsize", 1, NULL, 'l' },
- 	{ "label", 1, NULL, 'L'},
- 	{ "metadata", 1, NULL, 'm' },
-+	{ "mixed", 0, NULL, 'M' },
- 	{ "nodesize", 1, NULL, 'n' },
- 	{ "sectorsize", 1, NULL, 's' },
- 	{ "data", 1, NULL, 'd' },
- 	{ "version", 0, NULL, 'V' },
-+	{ "rootdir", 1, NULL, 'r' },
- 	{ 0, 0, 0, 0}
- };
- 
-+static int add_directory_items(struct btrfs_trans_handle *trans,
-+			       struct btrfs_root *root, u64 objectid,
-+			       ino_t parent_inum, const char *name,
-+			       struct stat *st, int *dir_index_cnt)
-+{
-+	int ret;
-+	int name_len;
-+	struct btrfs_key location;
-+	u8 filetype = 0;
-+
-+	name_len = strlen(name);
-+
-+	location.objectid = objectid;
-+	location.offset = 0;
-+	btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY);
-+
-+	if (S_ISDIR(st->st_mode))
-+		filetype = BTRFS_FT_DIR;
-+	if (S_ISREG(st->st_mode))
-+		filetype = BTRFS_FT_REG_FILE;
-+	if (S_ISLNK(st->st_mode))
-+		filetype = BTRFS_FT_SYMLINK;
-+
-+	ret = btrfs_insert_dir_item(trans, root, name, name_len,
-+				    parent_inum, &location,
-+				    filetype, index_cnt);
-+
-+	*dir_index_cnt = index_cnt;
-+	index_cnt++;
-+
-+	return ret;
-+}
-+
-+static int fill_inode_item(struct btrfs_trans_handle *trans,
-+			   struct btrfs_root *root,
-+			   struct btrfs_inode_item *dst, struct stat *src)
-+{
-+	u64 blocks = 0;
-+	u64 sectorsize = root->sectorsize;
-+
-+	/*
-+	 * btrfs_inode_item has some reserved fields
-+	 * and represents on-disk inode entry, so
-+	 * zero everything to prevent information leak
-+	 */
-+	memset(dst, 0, sizeof (*dst));
-+
-+	btrfs_set_stack_inode_generation(dst, trans->transid);
-+	btrfs_set_stack_inode_size(dst, src->st_size);
-+	btrfs_set_stack_inode_nbytes(dst, 0);
-+	btrfs_set_stack_inode_block_group(dst, 0);
-+	btrfs_set_stack_inode_nlink(dst, src->st_nlink);
-+	btrfs_set_stack_inode_uid(dst, src->st_uid);
-+	btrfs_set_stack_inode_gid(dst, src->st_gid);
-+	btrfs_set_stack_inode_mode(dst, src->st_mode);
-+	btrfs_set_stack_inode_rdev(dst, 0);
-+	btrfs_set_stack_inode_flags(dst, 0);
-+	btrfs_set_stack_timespec_sec(&dst->atime, src->st_atime);
-+	btrfs_set_stack_timespec_nsec(&dst->atime, 0);
-+	btrfs_set_stack_timespec_sec(&dst->ctime, src->st_ctime);
-+	btrfs_set_stack_timespec_nsec(&dst->ctime, 0);
-+	btrfs_set_stack_timespec_sec(&dst->mtime, src->st_mtime);
-+	btrfs_set_stack_timespec_nsec(&dst->mtime, 0);
-+	btrfs_set_stack_timespec_sec(&dst->otime, 0);
-+	btrfs_set_stack_timespec_nsec(&dst->otime, 0);
-+
-+	if (S_ISDIR(src->st_mode)) {
-+		btrfs_set_stack_inode_size(dst, 0);
-+		btrfs_set_stack_inode_nlink(dst, 1);
-+	}
-+	if (S_ISREG(src->st_mode)) {
-+		btrfs_set_stack_inode_size(dst, (u64)src->st_size);
-+		if (src->st_size <= BTRFS_MAX_INLINE_DATA_SIZE(root))
-+			btrfs_set_stack_inode_nbytes(dst, src->st_size);
-+		else {
-+			blocks = src->st_size / sectorsize;
-+			if (src->st_size % sectorsize)
-+				blocks += 1;
-+			blocks *= sectorsize;
-+			btrfs_set_stack_inode_nbytes(dst, blocks);
-+		}
-+	}
-+	if (S_ISLNK(src->st_mode))
-+		btrfs_set_stack_inode_nbytes(dst, src->st_size + 1);
-+
-+	return 0;
-+}
-+
-+static int directory_select(const struct direct *entry)
-+{
-+	if ((strncmp(entry->d_name, ".", entry->d_reclen) == 0) ||
-+		(strncmp(entry->d_name, "..", entry->d_reclen) == 0))
-+		return 0;
-+	else
-+		return 1;
-+}
-+
-+static void free_namelist(struct direct **files, int count)
-+{
-+	int i;
-+
-+	if (count < 0)
-+		return;
-+
-+	for (i = 0; i < count; ++i)
-+		free(files[i]);
-+	free(files);
-+}
-+
-+static u64 calculate_dir_inode_size(char *dirname)
-+{
-+	int count, i;
-+	struct direct **files, *cur_file;
-+	u64 dir_inode_size = 0;
-+
-+	count = scandir(dirname, &files, directory_select, NULL);
-+
-+	for (i = 0; i < count; i++) {
-+		cur_file = files[i];
-+		dir_inode_size += strlen(cur_file->d_name);
-+	}
-+
-+	free_namelist(files, count);
-+
-+	dir_inode_size *= 2;
-+	return dir_inode_size;
-+}
-+
-+static int add_inode_items(struct btrfs_trans_handle *trans,
-+			   struct btrfs_root *root,
-+			   struct stat *st, char *name,
-+			   u64 self_objectid, ino_t parent_inum,
-+			   int dir_index_cnt, struct btrfs_inode_item *inode_ret)
-+{
-+	int ret;
-+	struct btrfs_key inode_key;
-+	struct btrfs_inode_item btrfs_inode;
-+	u64 objectid;
-+	u64 inode_size = 0;
-+	int name_len;
-+
-+	name_len = strlen(name);
-+	fill_inode_item(trans, root, &btrfs_inode, st);
-+	objectid = self_objectid;
-+
-+	if (S_ISDIR(st->st_mode)) {
-+		inode_size = calculate_dir_inode_size(name);
-+		btrfs_set_stack_inode_size(&btrfs_inode, inode_size);
-+	}
-+
-+	inode_key.objectid = objectid;
-+	inode_key.offset = 0;
-+	btrfs_set_key_type(&inode_key, BTRFS_INODE_ITEM_KEY);
-+
-+	ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode);
-+	if (ret)
-+		goto fail;
-+
-+	ret = btrfs_insert_inode_ref(trans, root, name, name_len,
-+				     objectid, parent_inum, dir_index_cnt);
-+	if (ret)
-+		goto fail;
-+
-+	*inode_ret = btrfs_inode;
-+fail:
-+	return ret;
-+}
-+
-+static int add_xattr_item(struct btrfs_trans_handle *trans,
-+			  struct btrfs_root *root, u64 objectid,
-+			  const char *file_name)
-+{
-+	int ret;
-+	int cur_name_len;
-+	char xattr_list[XATTR_LIST_MAX];
-+	char *cur_name;
-+	char cur_value[XATTR_SIZE_MAX];
-+	char delimiter = '\0';
-+	char *next_location = xattr_list;
-+
-+	ret = llistxattr(file_name, xattr_list, XATTR_LIST_MAX);
-+	if (ret < 0) {
-+		if(errno == ENOTSUP)
-+			return 0;
-+		fprintf(stderr, "get a list of xattr failed for %s\n",
-+			file_name);
-+		return ret;
-+	}
-+	if (ret == 0)
-+		return ret;
-+
-+	cur_name = strtok(xattr_list, &delimiter);
-+	while (cur_name != NULL) {
-+		cur_name_len = strlen(cur_name);
-+		next_location += cur_name_len + 1;
-+
-+		ret = getxattr(file_name, cur_name, cur_value, XATTR_SIZE_MAX);
-+		if (ret < 0) {
-+			if(errno == ENOTSUP)
-+				return 0;
-+			fprintf(stderr, "get a xattr value failed for %s attr %s\n",
-+				file_name, cur_name);
-+			return ret;
-+		}
-+
-+		ret = btrfs_insert_xattr_item(trans, root, cur_name,
-+					      cur_name_len, cur_value,
-+					      ret, objectid);
-+		if (ret) {
-+			fprintf(stderr, "insert a xattr item failed for %s\n",
-+				file_name);
-+		}
-+
-+		cur_name = strtok(next_location, &delimiter);
-+	}
-+
-+	return ret;
-+}
-+static int custom_alloc_extent(struct btrfs_root *root, u64 num_bytes,
-+			       u64 hint_byte, struct btrfs_key *ins)
-+{
-+	u64 start;
-+	u64 end;
-+	u64 last = hint_byte;
-+	int ret;
-+	int wrapped = 0;
-+	struct btrfs_block_group_cache *cache;
-+
-+	while (1) {
-+		ret = find_first_extent_bit(&root->fs_info->free_space_cache,
-+					    last, &start, &end, EXTENT_DIRTY);
-+		if (ret) {
-+			if (wrapped++ == 0) {
-+				last = 0;
-+				continue;
-+			} else {
-+				goto fail;
-+			}
-+		}
-+
-+		start = max(last, start);
-+		last = end + 1;
-+		if (last - start < num_bytes)
-+			continue;
-+
-+		last = start + num_bytes;
-+		if (test_range_bit(&root->fs_info->pinned_extents,
-+				   start, last - 1, EXTENT_DIRTY, 0))
-+			continue;
-+
-+		cache = btrfs_lookup_block_group(root->fs_info, start);
-+		BUG_ON(!cache);
-+		if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM ||
-+		    last > cache->key.objectid + cache->key.offset) {
-+			last = cache->key.objectid + cache->key.offset;
-+			continue;
-+		}
-+
-+		if (cache->flags & (BTRFS_BLOCK_GROUP_SYSTEM |
-+			    BTRFS_BLOCK_GROUP_METADATA)) {
-+			last = cache->key.objectid + cache->key.offset;
-+			continue;
-+		}
-+
-+		clear_extent_dirty(&root->fs_info->free_space_cache,
-+				   start, start + num_bytes - 1, 0);
-+
-+		ins->objectid = start;
-+		ins->offset = num_bytes;
-+		ins->type = BTRFS_EXTENT_ITEM_KEY;
-+		return 0;
-+	}
-+fail:
-+	fprintf(stderr, "not enough free space\n");
-+	return -ENOSPC;
-+}
-+
-+static int record_file_extent(struct btrfs_trans_handle *trans,
-+			      struct btrfs_root *root, u64 objectid,
-+			      struct btrfs_inode_item *inode,
-+			      u64 file_pos, u64 disk_bytenr,
-+			      u64 num_bytes)
-+{
-+	int ret;
-+	struct btrfs_fs_info *info = root->fs_info;
-+	struct btrfs_root *extent_root = info->extent_root;
-+	struct extent_buffer *leaf;
-+	struct btrfs_file_extent_item *fi;
-+	struct btrfs_key ins_key;
-+	struct btrfs_path path;
-+	struct btrfs_extent_item *ei;
-+
-+	btrfs_init_path(&path);
-+
-+	ins_key.objectid = objectid;
-+	ins_key.offset = 0;
-+	btrfs_set_key_type(&ins_key, BTRFS_EXTENT_DATA_KEY);
-+	ret = btrfs_insert_empty_item(trans, root, &path, &ins_key,
-+				      sizeof(*fi));
-+	if (ret)
-+		goto fail;
-+	leaf = path.nodes[0];
-+	fi = btrfs_item_ptr(leaf, path.slots[0],
-+			    struct btrfs_file_extent_item);
-+	btrfs_set_file_extent_generation(leaf, fi, trans->transid);
-+	btrfs_set_file_extent_type(leaf, fi, BTRFS_FILE_EXTENT_REG);
-+	btrfs_set_file_extent_disk_bytenr(leaf, fi, disk_bytenr);
-+	btrfs_set_file_extent_disk_num_bytes(leaf, fi, num_bytes);
-+	btrfs_set_file_extent_offset(leaf, fi, 0);
-+	btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes);
-+	btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes);
-+	btrfs_set_file_extent_compression(leaf, fi, 0);
-+	btrfs_set_file_extent_encryption(leaf, fi, 0);
-+	btrfs_set_file_extent_other_encoding(leaf, fi, 0);
-+	btrfs_mark_buffer_dirty(leaf);
-+
-+	btrfs_release_path(root, &path);
-+
-+	ins_key.objectid = disk_bytenr;
-+	ins_key.offset = num_bytes;
-+	ins_key.type = BTRFS_EXTENT_ITEM_KEY;
-+
-+	ret = btrfs_insert_empty_item(trans, extent_root, &path,
-+				&ins_key, sizeof(*ei));
-+	if (ret == 0) {
-+		leaf = path.nodes[0];
-+		ei = btrfs_item_ptr(leaf, path.slots[0],
-+				    struct btrfs_extent_item);
-+
-+		btrfs_set_extent_refs(leaf, ei, 0);
-+		btrfs_set_extent_generation(leaf, ei, trans->transid);
-+		btrfs_set_extent_flags(leaf, ei, BTRFS_EXTENT_FLAG_DATA);
-+
-+		btrfs_mark_buffer_dirty(leaf);
-+		ret = btrfs_update_block_group(trans, root, disk_bytenr,
-+					       num_bytes, 1, 0);
-+		if (ret)
-+			goto fail;
-+	} else if (ret != -EEXIST) {
-+		goto fail;
-+	}
-+
-+	ret = btrfs_inc_extent_ref(trans, root, disk_bytenr, num_bytes, 0,
-+				   root->root_key.objectid,
-+				   objectid, 0);
-+fail:
-+	btrfs_release_path(root, &path);
-+	return ret;
-+}
-+
-+static int add_symbolic_link(struct btrfs_trans_handle *trans,
-+			     struct btrfs_root *root,
-+			     u64 objectid, const char *path_name)
-+{
-+	int ret;
-+	u64 sectorsize = root->sectorsize;
-+	char *buf = malloc(sectorsize);
-+
-+	ret = readlink(path_name, buf, sectorsize);
-+	if (ret <= 0) {
-+		fprintf(stderr, "readlink failed for %s\n", path_name);
-+		goto fail;
-+	}
-+	if (ret >= sectorsize) {
-+		fprintf(stderr, "symlink too long for %s", path_name);
-+		ret = -1;
-+		goto fail;
-+	}
-+
-+	buf[ret] = '\0'; /* readlink does not do it for us */
-+	ret = btrfs_insert_inline_extent(trans, root, objectid, 0,
-+					 buf, ret + 1);
-+fail:
-+	free(buf);
-+	return ret;
-+}
-+
-+static int add_file_items(struct btrfs_trans_handle *trans,
-+			  struct btrfs_root *root,
-+			  struct btrfs_inode_item *btrfs_inode, u64 objectid,
-+			  ino_t parent_inum, struct stat *st,
-+			  const char *path_name, int out_fd)
-+{
-+	int ret = -1;
-+	ssize_t ret_read;
-+	u64 bytes_read = 0;
-+	char *buffer = NULL;
-+	struct btrfs_key key;
-+	int blocks;
-+	u32 sectorsize = root->sectorsize;
-+	u64 first_block = 0;
-+	u64 num_blocks = 0;
-+	int fd;
-+
-+	fd = open(path_name, O_RDONLY);
-+	if (fd == -1) {
-+		fprintf(stderr, "%s open failed\n", path_name);
-+		goto end;
-+	}
-+
-+	blocks = st->st_size / sectorsize;
-+	if (st->st_size % sectorsize)
-+		blocks += 1;
-+
-+	if (st->st_size <= BTRFS_MAX_INLINE_DATA_SIZE(root)) {
-+		buffer = malloc(st->st_size);
-+		ret_read = pread64(fd, buffer, st->st_size, bytes_read);
-+		if (ret_read == -1) {
-+			fprintf(stderr, "%s read failed\n", path_name);
-+			goto end;
-+		}
-+
-+		ret = btrfs_insert_inline_extent(trans, root, objectid, 0,
-+						 buffer, st->st_size);
-+		goto end;
-+	}
-+
-+	ret = custom_alloc_extent(root, blocks * sectorsize, 0, &key);
-+	if (ret)
-+		goto end;
-+
-+	first_block = key.objectid;
-+	bytes_read = 0;
-+	buffer = malloc(sectorsize);
-+
-+	do {
-+		memset(buffer, 0, sectorsize);
-+		ret_read = pread64(fd, buffer, sectorsize, bytes_read);
-+		if (ret_read == -1) {
-+			fprintf(stderr, "%s read failed\n", path_name);
-+			goto end;
-+		}
-+
-+		ret = pwrite64(out_fd, buffer, sectorsize,
-+			       first_block + bytes_read);
-+		if (ret != sectorsize) {
-+			fprintf(stderr, "output file write failed\n");
-+			goto end;
-+		}
-+
-+		/* checksum for file data */
-+		ret = btrfs_csum_file_block(trans, root->fs_info->csum_root,
-+				first_block + (blocks * sectorsize),
-+				first_block + bytes_read,
-+				buffer, sectorsize);
-+		if (ret) {
-+			fprintf(stderr, "%s checksum failed\n", path_name);
-+			goto end;
-+		}
-+
-+		bytes_read += ret_read;
-+		num_blocks++;
-+	} while (ret_read == sectorsize);
-+
-+	if (num_blocks > 0) {
-+		ret = record_file_extent(trans, root, objectid, btrfs_inode,
-+					 first_block, first_block,
-+					 blocks * sectorsize);
-+		if (ret)
-+			goto end;
-+	}
-+
-+end:
-+	if (buffer)
-+		free(buffer);
-+	close(fd);
-+	return ret;
-+}
-+
-+static char *make_path(char *dir, char *name)
-+{
-+	char *path;
-+
-+	path = malloc(strlen(dir) + strlen(name) + 2);
-+	if (!path)
-+		return NULL;
-+	strcpy(path, dir);
-+	if (dir[strlen(dir) - 1] != '/')
-+		strcat(path, "/");
-+	strcat(path, name);
-+	return path;
-+}
-+
-+static int traverse_directory(struct btrfs_trans_handle *trans,
-+			      struct btrfs_root *root, char *dir_name,
-+			      struct directory_name_entry *dir_head, int out_fd)
-+{
-+	int ret = 0;
-+
-+	struct btrfs_inode_item cur_inode;
-+	struct btrfs_inode_item *inode_item;
-+	int count, i, dir_index_cnt;
-+	struct direct **files;
-+	struct stat st;
-+	struct directory_name_entry *dir_entry, *parent_dir_entry;
-+	struct direct *cur_file;
-+	ino_t parent_inum, cur_inum;
-+	ino_t highest_inum = 0;
-+	char *parent_dir_name;
-+	struct btrfs_path path;
-+	struct extent_buffer *leaf;
-+	struct btrfs_key root_dir_key;
-+	u64 root_dir_inode_size = 0;
-+
-+	/* Add list for source directory */
-+	dir_entry = malloc(sizeof(struct directory_name_entry));
-+	dir_entry->dir_name = dir_name;
-+	dir_entry->path = malloc(strlen(dir_name) + 1);
-+	strcpy(dir_entry->path, dir_name);
-+
-+	parent_inum = highest_inum + BTRFS_FIRST_FREE_OBJECTID;
-+	dir_entry->inum = parent_inum;
-+	list_add_tail(&dir_entry->list, &dir_head->list);
-+
-+	btrfs_init_path(&path);
-+
-+	root_dir_key.objectid = btrfs_root_dirid(&root->root_item);
-+	root_dir_key.offset = 0;
-+	btrfs_set_key_type(&root_dir_key, BTRFS_INODE_ITEM_KEY);
-+	ret = btrfs_lookup_inode(trans, root, &path, &root_dir_key, 1);
-+	if (ret) {
-+		fprintf(stderr, "root dir lookup error\n");
-+		return -1;
-+	}
-+
-+	leaf = path.nodes[0];
-+	inode_item = btrfs_item_ptr(leaf, path.slots[0],
-+				    struct btrfs_inode_item);
-+
-+	root_dir_inode_size = calculate_dir_inode_size(dir_name);
-+	btrfs_set_inode_size(leaf, inode_item, root_dir_inode_size);
-+	btrfs_mark_buffer_dirty(leaf);
-+
-+	btrfs_release_path(root, &path);
-+
-+	do {
-+		parent_dir_entry = list_entry(dir_head->list.next,
-+					      struct directory_name_entry,
-+					      list);
-+		list_del(&parent_dir_entry->list);
-+
-+		parent_inum = parent_dir_entry->inum;
-+		parent_dir_name = parent_dir_entry->dir_name;
-+		if (chdir(parent_dir_entry->path)) {
-+			fprintf(stderr, "chdir error for %s\n",
-+				parent_dir_name);
-+			goto fail_no_files;
-+		}
-+
-+		count = scandir(parent_dir_entry->path, &files,
-+				directory_select, NULL);
-+		if (count == -1)
-+		{
-+			fprintf(stderr, "scandir for %s failed: %s\n",
-+				parent_dir_name, strerror (errno));
-+			goto fail;
-+		}
-+
-+		for (i = 0; i < count; i++) {
-+			cur_file = files[i];
-+
-+			if (lstat(cur_file->d_name, &st) == -1) {
-+				fprintf(stderr, "lstat failed for file %s\n",
-+					cur_file->d_name);
-+				goto fail;
-+			}
-+
-+			cur_inum = ++highest_inum + BTRFS_FIRST_FREE_OBJECTID;
-+			ret = add_directory_items(trans, root,
-+						  cur_inum, parent_inum,
-+						  cur_file->d_name,
-+						  &st, &dir_index_cnt);
-+			if (ret) {
-+				fprintf(stderr, "add_directory_items failed\n");
-+				goto fail;
-+			}
-+
-+			ret = add_inode_items(trans, root, &st,
-+					      cur_file->d_name, cur_inum,
-+					      parent_inum, dir_index_cnt,
-+					      &cur_inode);
-+			if (ret) {
-+				fprintf(stderr, "add_inode_items failed\n");
-+				goto fail;
-+			}
-+
-+			ret = add_xattr_item(trans, root,
-+					     cur_inum, cur_file->d_name);
-+			if (ret) {
-+				fprintf(stderr, "add_xattr_item failed\n");
-+				if(ret != -ENOTSUP)
-+					goto fail;
-+			}
-+
-+			if (S_ISDIR(st.st_mode)) {
-+				dir_entry = malloc(sizeof(struct directory_name_entry));
-+				dir_entry->dir_name = cur_file->d_name;
-+				dir_entry->path = make_path(parent_dir_entry->path,
-+							    cur_file->d_name);
-+				dir_entry->inum = cur_inum;
-+				list_add_tail(&dir_entry->list,	&dir_head->list);
-+			} else if (S_ISREG(st.st_mode)) {
-+				ret = add_file_items(trans, root, &cur_inode,
-+						     cur_inum, parent_inum, &st,
-+						     cur_file->d_name, out_fd);
-+				if (ret) {
-+					fprintf(stderr, "add_file_items failed\n");
-+					goto fail;
-+				}
-+			} else if (S_ISLNK(st.st_mode)) {
-+				ret = add_symbolic_link(trans, root,
-+						        cur_inum, cur_file->d_name);
-+				if (ret) {
-+					fprintf(stderr, "add_symbolic_link failed\n");
-+					goto fail;
-+				}
-+			}
-+		}
-+
-+		free_namelist(files, count);
-+		free(parent_dir_entry->path);
-+		free(parent_dir_entry);
-+
-+		index_cnt = 2;
-+
-+	} while (!list_empty(&dir_head->list));
-+
-+	return 0;
-+fail:
-+	free_namelist(files, count);
-+fail_no_files:
-+	free(parent_dir_entry->path);
-+	free(parent_dir_entry);
-+	return -1;
-+}
-+
-+static int open_target(char *output_name)
-+{
-+	int output_fd;
-+	output_fd = open(output_name, O_CREAT | O_RDWR | O_TRUNC,
-+		         S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
-+
-+	return output_fd;
-+}
-+
-+static int create_chunks(struct btrfs_trans_handle *trans,
-+			 struct btrfs_root *root, u64 num_of_meta_chunks,
-+			 u64 size_of_data)
-+{
-+	u64 chunk_start;
-+	u64 chunk_size;
-+	u64 meta_type = BTRFS_BLOCK_GROUP_METADATA;
-+	u64 data_type = BTRFS_BLOCK_GROUP_DATA;
-+	u64 minimum_data_chunk_size = 8 * 1024 * 1024;
-+	u64 i;
-+	int ret;
-+
-+	for (i = 0; i < num_of_meta_chunks; i++) {
-+		ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
-+					&chunk_start, &chunk_size, meta_type);
-+		BUG_ON(ret);
-+		ret = btrfs_make_block_group(trans, root->fs_info->extent_root, 0,
-+					     meta_type, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
-+					     chunk_start, chunk_size);
-+		BUG_ON(ret);
-+		set_extent_dirty(&root->fs_info->free_space_cache,
-+				 chunk_start, chunk_start + chunk_size - 1, 0);
-+	}
-+
-+	if (size_of_data < minimum_data_chunk_size)
-+		size_of_data = minimum_data_chunk_size;
-+	ret = btrfs_alloc_data_chunk(trans, root->fs_info->extent_root,
-+				     &chunk_start, size_of_data, data_type);
-+	BUG_ON(ret);
-+	ret = btrfs_make_block_group(trans, root->fs_info->extent_root, 0,
-+				     data_type, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
-+				     chunk_start, size_of_data);
-+	BUG_ON(ret);
-+	set_extent_dirty(&root->fs_info->free_space_cache,
-+			 chunk_start, chunk_start + size_of_data - 1, 0);
-+	return ret;
-+}
-+
-+static int make_image(char *source_dir, struct btrfs_root *root, int out_fd)
-+{
-+	int ret;
-+	struct btrfs_trans_handle *trans;
-+
-+	struct stat root_st;
-+
-+	struct directory_name_entry dir_head;
-+
-+	ret = lstat(source_dir, &root_st);
-+	if (ret) {
-+		fprintf(stderr, "unable to lstat the %s\n", source_dir);
-+		goto fail;
-+	}
-+
-+	INIT_LIST_HEAD(&dir_head.list);
-+
-+	trans = btrfs_start_transaction(root, 1);
-+	ret = traverse_directory(trans, root, source_dir, &dir_head, out_fd);
-+	if (ret) {
-+		fprintf(stderr, "unable to traverse_directory\n");
-+		goto fail;
-+	}
-+	btrfs_commit_transaction(trans, root);
-+
-+	printf("Making image is completed.\n");
-+	return 0;
-+fail:
-+	fprintf(stderr, "Making image is aborted.\n");
-+	return -1;
-+}
-+
-+static u64 size_sourcedir(char *dir_name, u64 sectorsize,
-+			  u64 *num_of_meta_chunks_ret, u64 *size_of_data_ret)
-+{
-+	u64 dir_size = 0;
-+	u64 total_size = 0;
-+	int ret;
-+	char command[1024];
-+	char path[512];
-+	char *file_name = "temp_file";
-+	FILE *file;
-+	u64 default_chunk_size = 8 * 1024 * 1024;	/* 8MB */
-+	u64 allocated_meta_size = 8 * 1024 * 1024;	/* 8MB */
-+	u64 allocated_total_size = 20 * 1024 * 1024;	/* 20MB */
-+	u64 num_of_meta_chunks = 0;
-+	u64 num_of_allocated_meta_chunks =
-+			allocated_meta_size / default_chunk_size;
-+
-+	ret = sprintf(command, "du -B 4096 -s ");
-+	if (ret < 0) {
-+		fprintf(stderr, "error executing sprintf for du command\n");
-+		return -1;
-+	}
-+	strcat(command, dir_name);
-+	strcat(command, " > ");
-+	strcat(command, file_name);
-+	ret = system(command);
-+
-+	file = fopen(file_name, "r");
-+	ret = fscanf(file, "%lld %s\n", &dir_size, path);
-+	fclose(file);
-+	remove(file_name);
-+
-+	dir_size *= sectorsize;
-+	*size_of_data_ret = dir_size;
-+
-+	num_of_meta_chunks = (dir_size / 2) / default_chunk_size;
-+	if (((dir_size / 2) % default_chunk_size) != 0)
-+		num_of_meta_chunks++;
-+	if (num_of_meta_chunks <= num_of_allocated_meta_chunks)
-+		num_of_meta_chunks = 0;
-+	else
-+		num_of_meta_chunks -= num_of_allocated_meta_chunks;
-+
-+	total_size = allocated_total_size + dir_size +
-+		     (num_of_meta_chunks * default_chunk_size);
-+
-+	*num_of_meta_chunks_ret = num_of_meta_chunks;
-+
-+	return total_size;
-+}
-+
-+static int zero_output_file(int out_fd, u64 size, u32 sectorsize)
-+{
-+	int len = sectorsize;
-+	int loop_num = size / sectorsize;
-+	u64 location = 0;
-+	char *buf = malloc(len);
-+	int ret = 0, i;
-+	ssize_t written;
-+
-+	if (!buf)
-+		return -ENOMEM;
-+	memset(buf, 0, len);
-+	for (i = 0; i < loop_num; i++) {
-+		written = pwrite64(out_fd, buf, len, location);
-+		if (written != len)
-+			ret = -EIO;
-+		location += sectorsize;
-+	}
-+	free(buf);
-+	return ret;
-+}
-+
- int main(int ac, char **av)
- {
- 	char *file;
-@@ -346,8 +1203,8 @@ int main(int ac, char **av)
- 	u64 dev_block_count = 0;
- 	u64 blocks[7];
- 	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 sectorsize = 4096;
- 	u32 nodesize = leafsize;
-@@ -355,13 +1212,22 @@ int main(int ac, char **av)
- 	int zero_end = 1;
- 	int option_index = 0;
- 	int fd;
--	int first_fd;
- 	int ret;
- 	int i;
-+	int mixed = 0;
-+	int data_profile_opt = 0;
-+	int metadata_profile_opt = 0;
-+
-+	char *source_dir = NULL;
-+	int source_dir_set = 0;
-+	u64 num_of_meta_chunks = 0;
-+	u64 size_of_data = 0;
-+	u64 source_dir_size = 0;
-+	char *pretty_buf;
- 
- 	while(1) {
- 		int c;
--		c = getopt_long(ac, av, "A:b:l:n:s:m:d:L:V", long_options,
-+		c = getopt_long(ac, av, "A:b:l:n:s:m:d:L:r:VM", long_options,
- 				&option_index);
- 		if (c < 0)
- 			break;
-@@ -371,8 +1237,11 @@ int main(int ac, char **av)
- 				break;
- 			case 'd':
- 				data_profile = parse_profile(optarg);
-+				data_profile_opt = 1;
- 				break;
- 			case 'l':
-+			case 'n':
-+				nodesize = parse_size(optarg);
- 				leafsize = parse_size(optarg);
- 				break;
- 			case 'L':
-@@ -380,27 +1249,30 @@ int main(int ac, char **av)
- 				break;
- 			case 'm':
- 				metadata_profile = parse_profile(optarg);
-+				metadata_profile_opt = 1;
- 				break;
--			case 'n':
--				nodesize = parse_size(optarg);
-+			case 'M':
-+				mixed = 1;
- 				break;
- 			case 's':
- 				sectorsize = parse_size(optarg);
- 				break;
- 			case 'b':
- 				block_count = parse_size(optarg);
--				if (block_count < 256*1024*1024) {
--					fprintf(stderr, "File system size "
--						"%llu bytes is too small, "
--						"256M is required at least\n",
--						(unsigned long long)block_count);
--					exit(1);
-+				if (block_count <= 1024*1024*1024) {
-+					printf("SMALL VOLUME: forcing mixed "
-+					       "metadata/data groups\n");
-+					mixed = 1;
- 				}
- 				zero_end = 0;
- 				break;
- 			case 'V':
- 				print_version();
- 				break;
-+			case 'r':
-+				source_dir = optarg;
-+				source_dir_set = 1;
-+				break;
- 			default:
- 				print_usage();
- 		}
-@@ -421,27 +1293,54 @@ int main(int ac, char **av)
- 	printf("\nWARNING! - %s IS EXPERIMENTAL\n", BTRFS_BUILD_VERSION);
- 	printf("WARNING! - see http://btrfs.wiki.kernel.org before using\n\n");
- 
--	file = av[optind++];
--	ret = check_mounted(file);
--	if (ret < 0) {
--		fprintf(stderr, "error checking %s mount status\n", file);
--		exit(1);
--	}
--	if (ret == 1) {
--		fprintf(stderr, "%s is mounted\n", file);
--		exit(1);
-+	if (source_dir == 0) {
-+		file = av[optind++];
-+		ret = check_mounted(file);
-+		if (ret < 0) {
-+			fprintf(stderr, "error checking %s mount status\n", file);
-+			exit(1);
-+		}
-+		if (ret == 1) {
-+			fprintf(stderr, "%s is mounted\n", file);
-+			exit(1);
-+		}
-+		ac--;
-+		fd = open(file, O_RDWR);
-+		if (fd < 0) {
-+			fprintf(stderr, "unable to open %s\n", file);
-+			exit(1);
-+		}
-+		first_file = file;
-+		ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count, &mixed);
-+		if (block_count == 0)
-+			block_count = dev_block_count;
-+	} else {
-+		ac = 0;
-+		file = av[optind++];
-+		fd = open_target(file);
-+		if (fd < 0) {
-+			fprintf(stderr, "unable to open the %s\n", file);
-+			exit(1);
-+		}
-+
-+		first_file = file;
-+		source_dir_size = size_sourcedir(source_dir, sectorsize,
-+					     &num_of_meta_chunks, &size_of_data);
-+		if(block_count < source_dir_size)
-+			block_count = source_dir_size;
-+		ret = zero_output_file(fd, block_count, sectorsize);
-+		if (ret) {
-+			fprintf(stderr, "unable to zero the output file\n");
-+			exit(1);
-+		}
- 	}
--	ac--;
--	fd = open(file, O_RDWR);
--	if (fd < 0) {
--		fprintf(stderr, "unable to open %s\n", file);
--		exit(1);
-+	if (mixed) {
-+		if (metadata_profile != data_profile) {
-+			fprintf(stderr, "With mixed block groups data and metadata "
-+				"profiles must be the same\n");
-+			exit(1);
-+		}
- 	}
--	first_fd = fd;
--	first_file = file;
--	ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count);
--	if (block_count == 0)
--		block_count = dev_block_count;
- 
- 	blocks[0] = BTRFS_SUPER_INFO_OFFSET;
- 	for (i = 1; i < 7; i++) {
-@@ -456,10 +1355,15 @@ int main(int ac, char **av)
- 		fprintf(stderr, "error during mkfs %d\n", ret);
- 		exit(1);
- 	}
-+
- 	root = open_ctree(file, 0, O_RDWR);
-+	if (!root) {
-+		fprintf(stderr, "ctree init failed\n");
-+		exit(1);
-+	}
- 	root->fs_info->alloc_start = alloc_start;
- 
--	ret = make_root_dir(root);
-+	ret = make_root_dir(root, mixed);
- 	if (ret) {
- 		fprintf(stderr, "failed to setup the root directory\n");
- 		exit(1);
-@@ -471,13 +1375,11 @@ int main(int ac, char **av)
- 		goto raid_groups;
- 
- 	btrfs_register_one_device(file);
--	if (!root) {
--		fprintf(stderr, "ctree init failed\n");
--		return -1;
--	}
- 
- 	zero_end = 1;
- 	while(ac-- > 0) {
-+		int old_mixed = mixed;
-+
- 		file = av[optind++];
- 		ret = check_mounted(file);
- 		if (ret < 0) {
-@@ -503,8 +1405,8 @@ int main(int ac, char **av)
- 			continue;
- 		}
- 		ret = btrfs_prepare_device(fd, file, zero_end,
--					   &dev_block_count);
--
-+					   &dev_block_count, &mixed);
-+		mixed = old_mixed;
- 		BUG_ON(ret);
- 
- 		ret = btrfs_add_to_fsid(trans, root, fd, file, dev_block_count,
-@@ -514,20 +1416,44 @@ int main(int ac, char **av)
- 	}
- 
- raid_groups:
--	ret = create_raid_groups(trans, root, data_profile,
--				 metadata_profile);
--	BUG_ON(ret);
-+	if (!source_dir_set) {
-+		ret = create_raid_groups(trans, root, data_profile,
-+				 data_profile_opt, metadata_profile,
-+				 metadata_profile_opt, mixed);
-+		BUG_ON(ret);
-+	}
- 
- 	ret = create_data_reloc_tree(trans, root);
- 	BUG_ON(ret);
- 
-+	if (mixed) {
-+		struct btrfs_super_block *super = &root->fs_info->super_copy;
-+		u64 flags = btrfs_super_incompat_flags(super);
-+
-+		flags |= BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS;
-+		btrfs_set_super_incompat_flags(super, flags);
-+	}
-+
- 	printf("fs created label %s on %s\n\tnodesize %u leafsize %u "
- 	    "sectorsize %u size %s\n",
- 	    label, first_file, nodesize, leafsize, sectorsize,
--	    pretty_sizes(btrfs_super_total_bytes(&root->fs_info->super_copy)));
-+	    pretty_buf = pretty_sizes(btrfs_super_total_bytes(&root->fs_info->super_copy)));
-+	free(pretty_buf);
- 
- 	printf("%s\n", BTRFS_BUILD_VERSION);
- 	btrfs_commit_transaction(trans, root);
-+
-+	if (source_dir_set) {
-+		trans = btrfs_start_transaction(root, 1);
-+		ret = create_chunks(trans, root,
-+				    num_of_meta_chunks, size_of_data);
-+		BUG_ON(ret);
-+		btrfs_commit_transaction(trans, root);
-+
-+		ret = make_image(source_dir, root, fd);
-+		BUG_ON(ret);
-+	}
-+
- 	ret = close_ctree(root);
- 	BUG_ON(ret);
- 
-diff --git a/print-tree.c b/print-tree.c
-index 59f4358..fc134c0 100644
---- a/print-tree.c
-+++ b/print-tree.c
-@@ -239,7 +239,7 @@ static void print_extent_item(struct extent_buffer *eb, int slot)
- 			       btrfs_shared_data_ref_count(eb, sref));
- 			break;
- 		default:
--			BUG();
-+			return;
- 		}
- 		ptr += btrfs_extent_inline_ref_size(type);
- 	}
-@@ -351,6 +351,9 @@ static void print_key_type(u8 type)
- 	case BTRFS_DEV_EXTENT_KEY:
- 		printf("DEV_EXTENT");
- 		break;
-+	case BTRFS_BALANCE_ITEM_KEY:
-+		printf("BALANCE_ITEM");
-+		break;
- 	case BTRFS_STRING_ITEM_KEY:
- 		printf("STRING_ITEM");
- 		break;
-@@ -391,6 +394,9 @@ static void print_objectid(unsigned long long objectid, u8 type)
- 	case BTRFS_CSUM_TREE_OBJECTID:
- 		printf("CSUM_TREE");
- 		break;
-+	case BTRFS_BALANCE_OBJECTID:
-+		printf("BALANCE");
-+		break;
- 	case BTRFS_ORPHAN_OBJECTID:
- 		printf("ORPHAN");
- 		break;
-@@ -413,8 +419,11 @@ static void print_objectid(unsigned long long objectid, u8 type)
- 		printf("MULTIPLE");
- 		break;
- 	case BTRFS_FIRST_CHUNK_TREE_OBJECTID:
--		printf("FIRST_CHUNK_TREE");
--		break;
-+		if (type == BTRFS_CHUNK_ITEM_KEY) {
-+			printf("FIRST_CHUNK_TREE");
-+			break;
-+		}
-+		/* fall-thru */
- 	default:
- 		printf("%llu", objectid);
- 	}
-@@ -441,7 +450,6 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
- 	struct btrfs_dir_item *di;
- 	struct btrfs_inode_item *ii;
- 	struct btrfs_file_extent_item *fi;
--	struct btrfs_csum_item *ci;
- 	struct btrfs_block_group_item *bi;
- 	struct btrfs_extent_data_ref *dref;
- 	struct btrfs_shared_data_ref *sref;
-@@ -494,7 +502,7 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
- 		case BTRFS_DIR_LOG_ITEM_KEY:
- 			dlog = btrfs_item_ptr(l, i, struct btrfs_dir_log_item);
- 			printf("\t\tdir log end %Lu\n",
--			       btrfs_dir_log_end(l, dlog));
-+			       (unsigned long long)btrfs_dir_log_end(l, dlog));
- 		       break;
- 		case BTRFS_ORPHAN_ITEM_KEY:
- 			printf("\t\torphan item\n");
-@@ -502,11 +510,12 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
- 		case BTRFS_ROOT_ITEM_KEY:
- 			ri = btrfs_item_ptr(l, i, struct btrfs_root_item);
- 			read_extent_buffer(l, &root_item, (unsigned long)ri, sizeof(root_item));
--			printf("\t\troot data bytenr %llu level %d dirid %llu refs %u\n",
-+			printf("\t\troot data bytenr %llu level %d dirid %llu refs %u gen %llu\n",
- 				(unsigned long long)btrfs_root_bytenr(&root_item),
- 				btrfs_root_level(&root_item),
- 				(unsigned long long)btrfs_root_dirid(&root_item),
--				btrfs_root_refs(&root_item));
-+				btrfs_root_refs(&root_item),
-+				(unsigned long long)btrfs_root_generation(&root_item));
- 			if (btrfs_root_refs(&root_item) == 0) {
- 				struct btrfs_key drop_key;
- 				btrfs_disk_key_to_cpu(&drop_key,
-@@ -553,11 +562,9 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
- #endif
- 			break;
- 		case BTRFS_CSUM_ITEM_KEY:
--			ci = btrfs_item_ptr(l, i, struct btrfs_csum_item);
- 			printf("\t\tcsum item\n");
- 			break;
- 		case BTRFS_EXTENT_CSUM_KEY:
--			ci = btrfs_item_ptr(l, i, struct btrfs_csum_item);
- 			printf("\t\textent csum item\n");
- 			break;
- 		case BTRFS_EXTENT_DATA_KEY:
-@@ -607,7 +614,7 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
- 	}
- }
- 
--void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb)
-+void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int follow)
- {
- 	int i;
- 	u32 nr;
-@@ -643,6 +650,9 @@ void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb)
- 		       (unsigned long long)btrfs_node_ptr_generation(eb, i));
- 		fflush(stdout);
- 	}
-+	if (!follow)
-+		return;
-+
- 	for (i = 0; i < nr; i++) {
- 		struct extent_buffer *next = read_tree_block(root,
- 					     btrfs_node_blockptr(eb, i),
-@@ -660,8 +670,7 @@ void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb)
- 		if (btrfs_header_level(next) !=
- 			btrfs_header_level(eb) - 1)
- 			BUG();
--		btrfs_print_tree(root, next);
-+		btrfs_print_tree(root, next, 1);
- 		free_extent_buffer(next);
- 	}
- }
--
-diff --git a/print-tree.h b/print-tree.h
-index 4d1a01a..495b81a 100644
---- a/print-tree.h
-+++ b/print-tree.h
-@@ -19,6 +19,6 @@
- #ifndef __PRINT_TREE_
- #define __PRINT_TREE_
- void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l);
--void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *t);
-+void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *t, int follow);
- void btrfs_print_key(struct btrfs_disk_key *disk_key);
- #endif
-diff --git a/quick-test.c b/quick-test.c
-index 351c706..fa6fd83 100644
---- a/quick-test.c
-+++ b/quick-test.c
-@@ -85,7 +85,7 @@ int main(int ac, char **av) {
- 			fprintf(stderr, "search %d:%d\n", num, i);
- 		ret = btrfs_search_slot(NULL, root, &ins, &path, 0, 0);
- 		if (ret) {
--			btrfs_print_tree(root, root->node);
-+			btrfs_print_tree(root, root->node, 1);
- 			printf("unable to find %d\n", num);
- 			exit(1);
- 		}
-@@ -148,7 +148,7 @@ int main(int ac, char **av) {
- 			fprintf(stderr, "search %d:%d\n", num, i);
- 		ret = btrfs_search_slot(NULL, root, &ins, &path, 0, 0);
- 		if (ret) {
--			btrfs_print_tree(root, root->node);
-+			btrfs_print_tree(root, root->node, 1);
- 			printf("unable to find %d\n", num);
- 			exit(1);
- 		}
-@@ -196,7 +196,7 @@ int main(int ac, char **av) {
- 	btrfs_commit_transaction(trans, root);
- 	printf("tree size is now %d\n", tree_size);
- 	printf("root %p commit root %p\n", root->node, root->commit_root);
--	btrfs_print_tree(root, root->node);
-+	btrfs_print_tree(root, root->node, 1);
- 	close_ctree(root);
- 	return 0;
- }
-diff --git a/random-test.c b/random-test.c
-index 571735d..0003236 100644
---- a/random-test.c
-+++ b/random-test.c
-@@ -404,7 +404,7 @@ int main(int ac, char **av)
- 			if (ret) {
- 				fprintf(stderr, "op %d failed %d:%d\n",
- 					op, i, iterations);
--				btrfs_print_tree(root, root->node);
-+				btrfs_print_tree(root, root->node, 1);
- 				fprintf(stderr, "op %d failed %d:%d\n",
- 					op, i, iterations);
- 				err = ret;
-diff --git a/repair.c b/repair.c
-new file mode 100644
-index 0000000..e640465
---- /dev/null
-+++ b/repair.c
-@@ -0,0 +1,50 @@
-+/*
-+ * Copyright (C) 2012 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.
-+ */
-+
-+#include "ctree.h"
-+#include "extent-cache.h"
-+#include "utils.h"
-+#include "repair.h"
-+
-+int btrfs_add_corrupt_extent_record(struct btrfs_fs_info *info,
-+				    struct btrfs_key *first_key,
-+				    u64 start, u64 len, int level)
-+
-+{
-+	int ret = 0;
-+	struct btrfs_corrupt_block *corrupt;
-+
-+	if (!info->corrupt_blocks)
-+		return 0;
-+
-+	corrupt = malloc(sizeof(*corrupt));
-+	if (!corrupt)
-+		return -ENOMEM;
-+
-+	memcpy(&corrupt->key, first_key, sizeof(*first_key));
-+	corrupt->cache.start = start;
-+	corrupt->cache.size = len;
-+	corrupt->level = level;
-+
-+	ret = insert_existing_cache_extent(info->corrupt_blocks, &corrupt->cache);
-+	if (ret)
-+		free(corrupt);
-+	BUG_ON(ret && ret != -EEXIST);
-+	return ret;
-+}
-+
-diff --git a/repair.h b/repair.h
-new file mode 100644
-index 0000000..3d0dcb9
---- /dev/null
-+++ b/repair.h
-@@ -0,0 +1,32 @@
-+/*
-+ * Copyright (C) 2012 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.
-+ */
-+
-+#ifndef __BTRFS_REPAIR__
-+#define __BTRFS_REPAIR__
-+
-+struct btrfs_corrupt_block {
-+	struct cache_extent cache;
-+	struct btrfs_key key;
-+	int level;
-+};
-+
-+int btrfs_add_corrupt_extent_record(struct btrfs_fs_info *info,
-+				    struct btrfs_key *first_key,
-+				    u64 start, u64 len, int level);
-+
-+#endif
-diff --git a/restore.c b/restore.c
-new file mode 100644
-index 0000000..250c9d3
---- /dev/null
-+++ b/restore.c
-@@ -0,0 +1,872 @@
-+/*
-+ * Copyright (C) 2011 Red Hat.  All rights reserved.
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public
-+ * License v2 as published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+ * General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public
-+ * License along with this program; if not, write to the
-+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-+ * Boston, MA 021110-1307, USA.
-+ */
-+
-+#define _XOPEN_SOURCE 500
-+#define _GNU_SOURCE 1
-+#include <ctype.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <fcntl.h>
-+#include <sys/stat.h>
-+#include <zlib.h>
-+#include "kerncompat.h"
-+#include "ctree.h"
-+#include "disk-io.h"
-+#include "print-tree.h"
-+#include "transaction.h"
-+#include "list.h"
-+#include "version.h"
-+#include "volumes.h"
-+#include "utils.h"
-+
-+static char path_name[4096];
-+static int get_snaps = 0;
-+static int verbose = 0;
-+static int ignore_errors = 0;
-+static int overwrite = 0;
-+
-+static int decompress(char *inbuf, char *outbuf, u64 compress_len,
-+		      u64 decompress_len)
-+{
-+	z_stream strm;
-+	int ret;
-+
-+	memset(&strm, 0, sizeof(strm));
-+	ret = inflateInit(&strm);
-+	if (ret != Z_OK) {
-+		fprintf(stderr, "inflate init returnd %d\n", ret);
-+		return -1;
-+	}
-+
-+	strm.avail_in = compress_len;
-+	strm.next_in = (unsigned char *)inbuf;
-+	strm.avail_out = decompress_len;
-+	strm.next_out = (unsigned char *)outbuf;
-+	ret = inflate(&strm, Z_NO_FLUSH);
-+	if (ret != Z_STREAM_END) {
-+		(void)inflateEnd(&strm);
-+		fprintf(stderr, "ret is %d\n", ret);
-+		return -1;
-+	}
-+
-+	(void)inflateEnd(&strm);
-+	return 0;
-+}
-+
-+int next_leaf(struct btrfs_root *root, struct btrfs_path *path)
-+{
-+	int slot;
-+	int level = 1;
-+	struct extent_buffer *c;
-+	struct extent_buffer *next = NULL;
-+
-+	for (; level < BTRFS_MAX_LEVEL; level++) {
-+		if (path->nodes[level])
-+			break;
-+	}
-+
-+	if (level == BTRFS_MAX_LEVEL)
-+		return 1;
-+
-+	slot = path->slots[level] + 1;
-+
-+	while(level < BTRFS_MAX_LEVEL) {
-+		if (!path->nodes[level])
-+			return 1;
-+
-+		slot = path->slots[level] + 1;
-+		c = path->nodes[level];
-+		if (slot >= btrfs_header_nritems(c)) {
-+			level++;
-+			if (level == BTRFS_MAX_LEVEL)
-+				return 1;
-+			continue;
-+		}
-+
-+		if (next)
-+			free_extent_buffer(next);
-+
-+		if (path->reada)
-+			reada_for_search(root, path, level, slot, 0);
-+
-+		next = read_node_slot(root, c, slot);
-+		break;
-+	}
-+	path->slots[level] = slot;
-+	while(1) {
-+		level--;
-+		c = path->nodes[level];
-+		free_extent_buffer(c);
-+		path->nodes[level] = next;
-+		path->slots[level] = 0;
-+		if (!level)
-+			break;
-+		if (path->reada)
-+			reada_for_search(root, path, level, 0, 0);
-+		next = read_node_slot(root, next, 0);
-+	}
-+	return 0;
-+}
-+
-+static int copy_one_inline(int fd, struct btrfs_path *path, u64 pos)
-+{
-+	struct extent_buffer *leaf = path->nodes[0];
-+	struct btrfs_file_extent_item *fi;
-+	char buf[4096];
-+	char *outbuf;
-+	ssize_t done;
-+	unsigned long ptr;
-+	int ret;
-+	int len;
-+	int ram_size;
-+	int compress;
-+
-+	fi = btrfs_item_ptr(leaf, path->slots[0],
-+			    struct btrfs_file_extent_item);
-+	ptr = btrfs_file_extent_inline_start(fi);
-+	len = btrfs_file_extent_inline_item_len(leaf,
-+					btrfs_item_nr(leaf, path->slots[0]));
-+	read_extent_buffer(leaf, buf, ptr, len);
-+
-+	compress = btrfs_file_extent_compression(leaf, fi);
-+	if (compress == BTRFS_COMPRESS_NONE) {
-+		done = pwrite(fd, buf, len, pos);
-+		if (done < len) {
-+			fprintf(stderr, "Short inline write, wanted %d, did "
-+				"%zd: %d\n", len, done, errno);
-+			return -1;
-+		}
-+		return 0;
-+	}
-+
-+	ram_size = btrfs_file_extent_ram_bytes(leaf, fi);
-+	outbuf = malloc(ram_size);
-+	if (!outbuf) {
-+		fprintf(stderr, "No memory\n");
-+		return -1;
-+	}
-+
-+	ret = decompress(buf, outbuf, len, ram_size);
-+	if (ret) {
-+		free(outbuf);
-+		return ret;
-+	}
-+
-+	done = pwrite(fd, outbuf, ram_size, pos);
-+	free(outbuf);
-+	if (done < len) {
-+		fprintf(stderr, "Short compressed inline write, wanted %d, "
-+			"did %zd: %d\n", ram_size, done, errno);
-+		return -1;
-+	}
-+
-+	return 0;
-+}
-+
-+static int copy_one_extent(struct btrfs_root *root, int fd,
-+			   struct extent_buffer *leaf,
-+			   struct btrfs_file_extent_item *fi, u64 pos)
-+{
-+	struct btrfs_multi_bio *multi = NULL;
-+	struct btrfs_device *device;
-+	char *inbuf, *outbuf = NULL;
-+	ssize_t done, total = 0;
-+	u64 bytenr;
-+	u64 ram_size;
-+	u64 disk_size;
-+	u64 length;
-+	u64 size_left;
-+	u64 dev_bytenr;
-+	u64 count = 0;
-+	int compress;
-+	int ret;
-+	int dev_fd;
-+
-+	compress = btrfs_file_extent_compression(leaf, fi);
-+	bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
-+	disk_size = btrfs_file_extent_disk_num_bytes(leaf, fi);
-+	ram_size = btrfs_file_extent_ram_bytes(leaf, fi);
-+	size_left = disk_size;
-+
-+	/* we found a hole */
-+	if (disk_size == 0)
-+		return 0;
-+
-+	inbuf = malloc(disk_size);
-+	if (!inbuf) {
-+		fprintf(stderr, "No memory\n");
-+		return -1;
-+	}
-+
-+	if (compress != BTRFS_COMPRESS_NONE) {
-+		outbuf = malloc(ram_size);
-+		if (!outbuf) {
-+			fprintf(stderr, "No memory\n");
-+			free(inbuf);
-+			return -1;
-+		}
-+	}
-+again:
-+	length = size_left;
-+	ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
-+			      bytenr, &length, &multi, 0);
-+	if (ret) {
-+		free(inbuf);
-+		free(outbuf);
-+		fprintf(stderr, "Error mapping block %d\n", ret);
-+		return ret;
-+	}
-+	device = multi->stripes[0].dev;
-+	dev_fd = device->fd;
-+	device->total_ios++;
-+	dev_bytenr = multi->stripes[0].physical;
-+	kfree(multi);
-+
-+	if (size_left < length)
-+		length = size_left;
-+	size_left -= length;
-+
-+	done = pread(dev_fd, inbuf+count, length, dev_bytenr);
-+	if (done < length) {
-+		free(inbuf);
-+		free(outbuf);
-+		fprintf(stderr, "Short read %d\n", errno);
-+		return -1;
-+	}
-+
-+	count += length;
-+	bytenr += length;
-+	if (size_left)
-+		goto again;
-+
-+
-+	if (compress == BTRFS_COMPRESS_NONE) {
-+		while (total < ram_size) {
-+			done = pwrite(fd, inbuf+total, ram_size-total,
-+				      pos+total);
-+			if (done < 0) {
-+				free(inbuf);
-+				fprintf(stderr, "Error writing: %d %s\n", errno, strerror(errno));
-+				return -1;
-+			}
-+			total += done;
-+		}
-+		free(inbuf);
-+		return 0;
-+	}
-+
-+	ret = decompress(inbuf, outbuf, disk_size, ram_size);
-+	free(inbuf);
-+	if (ret) {
-+		free(outbuf);
-+		return ret;
-+	}
-+
-+	while (total < ram_size) {
-+		done = pwrite(fd, outbuf+total, ram_size-total, pos+total);
-+		if (done < 0) {
-+			free(outbuf);
-+			fprintf(stderr, "Error writing: %d %s\n", errno, strerror(errno));
-+			return -1;
-+		}
-+		total += done;
-+	}
-+	free(outbuf);
-+
-+	return 0;
-+}
-+
-+static int ask_to_continue(const char *file)
-+{
-+	char buf[2];
-+	char *ret;
-+
-+	printf("We seem to be looping a lot on %s, do you want to keep going "
-+	       "on ? (y/N): ", file);
-+again:
-+	ret = fgets(buf, 2, stdin);
-+	if (*ret == '\n' || tolower(*ret) == 'n')
-+		return 1;
-+	if (tolower(*ret) != 'y') {
-+		printf("Please enter either 'y' or 'n': ");
-+		goto again;
-+	}
-+
-+	return 0;
-+}
-+
-+
-+static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
-+		     const char *file)
-+{
-+	struct extent_buffer *leaf;
-+	struct btrfs_path *path;
-+	struct btrfs_file_extent_item *fi;
-+	struct btrfs_inode_item *inode_item;
-+	struct btrfs_key found_key;
-+	int ret;
-+	int extent_type;
-+	int compression;
-+	int loops = 0;
-+	u64 found_size = 0;
-+
-+	path = btrfs_alloc_path();
-+	if (!path) {
-+		fprintf(stderr, "Ran out of memory\n");
-+		return -1;
-+	}
-+	path->skip_locking = 1;
-+
-+	ret = btrfs_lookup_inode(NULL, root, path, key, 0);
-+	if (ret == 0) {
-+		inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
-+				    struct btrfs_inode_item);
-+		found_size = btrfs_inode_size(path->nodes[0], inode_item);
-+	}
-+	btrfs_release_path(root, path);
-+
-+	key->offset = 0;
-+	key->type = BTRFS_EXTENT_DATA_KEY;
-+
-+	ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
-+	if (ret < 0) {
-+		fprintf(stderr, "Error searching %d\n", ret);
-+		btrfs_free_path(path);
-+		return ret;
-+	}
-+
-+	leaf = path->nodes[0];
-+	while (!leaf) {
-+		ret = next_leaf(root, path);
-+		if (ret < 0) {
-+			fprintf(stderr, "Error getting next leaf %d\n",
-+				ret);
-+			btrfs_free_path(path);
-+			return ret;
-+		} else if (ret > 0) {
-+			/* No more leaves to search */
-+			btrfs_free_path(path);
-+			return 0;
-+		}
-+		leaf = path->nodes[0];
-+	}
-+
-+	while (1) {
-+		if (loops++ >= 1024) {
-+			ret = ask_to_continue(file);
-+			if (ret)
-+				break;
-+			loops = 0;
-+		}
-+		if (path->slots[0] >= btrfs_header_nritems(leaf)) {
-+			do {
-+				ret = next_leaf(root, path);
-+				if (ret < 0) {
-+					fprintf(stderr, "Error searching %d\n", ret);
-+					btrfs_free_path(path);
-+					return ret;
-+				} else if (ret) {
-+					/* No more leaves to search */
-+					btrfs_free_path(path);
-+					goto set_size;
-+					return 0;
-+				}
-+				leaf = path->nodes[0];
-+			} while (!leaf);
-+			continue;
-+		}
-+		btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
-+		if (found_key.objectid != key->objectid)
-+			break;
-+		if (found_key.type != key->type)
-+			break;
-+		fi = btrfs_item_ptr(leaf, path->slots[0],
-+				    struct btrfs_file_extent_item);
-+		extent_type = btrfs_file_extent_type(leaf, fi);
-+		compression = btrfs_file_extent_compression(leaf, fi);
-+		if (compression >= BTRFS_COMPRESS_LAST) {
-+			fprintf(stderr, "Don't support compression yet %d\n",
-+				compression);
-+			btrfs_free_path(path);
-+			return -1;
-+		}
-+
-+		if (extent_type == BTRFS_FILE_EXTENT_PREALLOC)
-+			goto next;
-+		if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
-+			ret = copy_one_inline(fd, path, found_key.offset);
-+			if (ret) {
-+				btrfs_free_path(path);
-+				return -1;
-+			}
-+		} else if (extent_type == BTRFS_FILE_EXTENT_REG) {
-+			ret = copy_one_extent(root, fd, leaf, fi,
-+					      found_key.offset);
-+			if (ret) {
-+				btrfs_free_path(path);
-+				return ret;
-+			}
-+		} else {
-+			printf("Weird extent type %d\n", extent_type);
-+		}
-+next:
-+		path->slots[0]++;
-+	}
-+
-+	btrfs_free_path(path);
-+set_size:
-+	if (found_size)
-+		ftruncate(fd, (loff_t)found_size);
-+	return 0;
-+}
-+
-+static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
-+		      const char *dir)
-+{
-+	struct btrfs_path *path;
-+	struct extent_buffer *leaf;
-+	struct btrfs_dir_item *dir_item;
-+	struct btrfs_key found_key, location;
-+	char filename[BTRFS_NAME_LEN + 1];
-+	unsigned long name_ptr;
-+	int name_len;
-+	int ret;
-+	int fd;
-+	int loops = 0;
-+	u8 type;
-+
-+	path = btrfs_alloc_path();
-+	if (!path) {
-+		fprintf(stderr, "Ran out of memory\n");
-+		return -1;
-+	}
-+	path->skip_locking = 1;
-+
-+	key->offset = 0;
-+	key->type = BTRFS_DIR_INDEX_KEY;
-+
-+	ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
-+	if (ret < 0) {
-+		fprintf(stderr, "Error searching %d\n", ret);
-+		btrfs_free_path(path);
-+		return ret;
-+	}
-+
-+	leaf = path->nodes[0];
-+	while (!leaf) {
-+		if (verbose > 1)
-+			printf("No leaf after search, looking for the next "
-+			       "leaf\n");
-+		ret = next_leaf(root, path);
-+		if (ret < 0) {
-+			fprintf(stderr, "Error getting next leaf %d\n",
-+				ret);
-+			btrfs_free_path(path);
-+			return ret;
-+		} else if (ret > 0) {
-+			/* No more leaves to search */
-+			if (verbose)
-+				printf("Reached the end of the tree looking "
-+				       "for the directory\n");
-+			btrfs_free_path(path);
-+			return 0;
-+		}
-+		leaf = path->nodes[0];
-+	}
-+
-+	while (leaf) {
-+		if (loops++ >= 1024) {
-+			printf("We have looped trying to restore files in %s "
-+			       "too many times to be making progress, "
-+			       "stopping\n", dir);
-+			break;
-+		}
-+
-+		if (path->slots[0] >= btrfs_header_nritems(leaf)) {
-+			do {
-+				ret = next_leaf(root, path);
-+				if (ret < 0) {
-+					fprintf(stderr, "Error searching %d\n",
-+						ret);
-+					btrfs_free_path(path);
-+					return ret;
-+				} else if (ret > 0) {
-+					/* No more leaves to search */
-+					if (verbose)
-+						printf("Reached the end of "
-+						       "the tree searching the"
-+						       " directory\n");
-+					btrfs_free_path(path);
-+					return 0;
-+				}
-+				leaf = path->nodes[0];
-+			} while (!leaf);
-+			continue;
-+		}
-+		btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
-+		if (found_key.objectid != key->objectid) {
-+			if (verbose > 1)
-+				printf("Found objectid=%Lu, key=%Lu\n",
-+				       found_key.objectid, key->objectid);
-+			break;
-+		}
-+		if (found_key.type != key->type) {
-+			if (verbose > 1)
-+				printf("Found type=%u, want=%u\n",
-+				       found_key.type, key->type);
-+			break;
-+		}
-+		dir_item = btrfs_item_ptr(leaf, path->slots[0],
-+					  struct btrfs_dir_item);
-+		name_ptr = (unsigned long)(dir_item + 1);
-+		name_len = btrfs_dir_name_len(leaf, dir_item);
-+		read_extent_buffer(leaf, filename, name_ptr, name_len);
-+		filename[name_len] = '\0';
-+		type = btrfs_dir_type(leaf, dir_item);
-+		btrfs_dir_item_key_to_cpu(leaf, dir_item, &location);
-+
-+		snprintf(path_name, 4096, "%s/%s", dir, filename);
-+
-+
-+		/*
-+		 * At this point we're only going to restore directories and
-+		 * files, no symlinks or anything else.
-+		 */
-+		if (type == BTRFS_FT_REG_FILE) {
-+			if (!overwrite) {
-+				static int warn = 0;
-+				struct stat st;
-+
-+				ret = stat(path_name, &st);
-+				if (!ret) {
-+					loops = 0;
-+					if (verbose || !warn)
-+						printf("Skipping existing file"
-+						       " %s\n", path_name);
-+					if (warn)
-+						goto next;
-+					printf("If you wish to overwrite use "
-+					       "the -o option to overwrite\n");
-+					warn = 1;
-+					goto next;
-+				}
-+				ret = 0;
-+			}
-+			if (verbose)
-+				printf("Restoring %s\n", path_name);
-+			fd = open(path_name, O_CREAT|O_WRONLY, 0644);
-+			if (fd < 0) {
-+				fprintf(stderr, "Error creating %s: %d\n",
-+					path_name, errno);
-+				if (ignore_errors)
-+					goto next;
-+				btrfs_free_path(path);
-+				return -1;
-+			}
-+			loops = 0;
-+			ret = copy_file(root, fd, &location, path_name);
-+			close(fd);
-+			if (ret) {
-+				if (ignore_errors)
-+					goto next;
-+				btrfs_free_path(path);
-+				return ret;
-+			}
-+		} else if (type == BTRFS_FT_DIR) {
-+			struct btrfs_root *search_root = root;
-+			char *dir = strdup(path_name);
-+
-+			if (!dir) {
-+				fprintf(stderr, "Ran out of memory\n");
-+				btrfs_free_path(path);
-+				return -1;
-+			}
-+
-+			if (location.type == BTRFS_ROOT_ITEM_KEY) {
-+				/*
-+				 * If we are a snapshot and this is the index
-+				 * object to ourselves just skip it.
-+				 */
-+				if (location.objectid ==
-+				    root->root_key.objectid) {
-+					free(dir);
-+					goto next;
-+				}
-+
-+				search_root = btrfs_read_fs_root(root->fs_info,
-+								 &location);
-+				if (IS_ERR(search_root)) {
-+					free(dir);
-+					fprintf(stderr, "Error reading "
-+						"subvolume %s: %lu\n",
-+						path_name,
-+						PTR_ERR(search_root));
-+					if (ignore_errors)
-+						goto next;
-+					return PTR_ERR(search_root);
-+				}
-+
-+				/*
-+				 * A subvolume will have a key.offset of 0, a
-+				 * snapshot will have key.offset of a transid.
-+				 */
-+				if (search_root->root_key.offset != 0 &&
-+				    get_snaps == 0) {
-+					free(dir);
-+					printf("Skipping snapshot %s\n",
-+					       filename);
-+					goto next;
-+				}
-+				location.objectid = BTRFS_FIRST_FREE_OBJECTID;
-+			}
-+
-+			if (verbose)
-+				printf("Restoring %s\n", path_name);
-+
-+			errno = 0;
-+			ret = mkdir(path_name, 0755);
-+			if (ret && errno != EEXIST) {
-+				free(dir);
-+				fprintf(stderr, "Error mkdiring %s: %d\n",
-+					path_name, errno);
-+				if (ignore_errors)
-+					goto next;
-+				btrfs_free_path(path);
-+				return -1;
-+			}
-+			loops = 0;
-+			ret = search_dir(search_root, &location, dir);
-+			free(dir);
-+			if (ret) {
-+				if (ignore_errors)
-+					goto next;
-+				btrfs_free_path(path);
-+				return ret;
-+			}
-+		}
-+next:
-+		path->slots[0]++;
-+	}
-+
-+	if (verbose)
-+		printf("Done searching %s\n", dir);
-+	btrfs_free_path(path);
-+	return 0;
-+}
-+
-+static void usage()
-+{
-+	fprintf(stderr, "Usage: restore [-svio] [-t disk offset] <device> "
-+		"<directory>\n");
-+}
-+
-+static struct btrfs_root *open_fs(const char *dev, u64 root_location, int super_mirror)
-+{
-+	struct btrfs_root *root;
-+	u64 bytenr;
-+	int i;
-+
-+	for (i = super_mirror; i < BTRFS_SUPER_MIRROR_MAX; i++) {
-+		bytenr = btrfs_sb_offset(i);
-+		root = open_ctree_recovery(dev, bytenr, root_location);
-+		if (root)
-+			return root;
-+		fprintf(stderr, "Could not open root, trying backup super\n");
-+	}
-+
-+	return NULL;
-+}
-+
-+static int find_first_dir(struct btrfs_root *root, u64 *objectid)
-+{
-+	struct btrfs_path *path;
-+	struct btrfs_key found_key;
-+	struct btrfs_key key;
-+	int ret = -1;
-+	int i;
-+
-+	key.objectid = 0;
-+	key.type = BTRFS_DIR_INDEX_KEY;
-+	key.offset = 0;
-+
-+	path = btrfs_alloc_path();
-+	if (!path) {
-+		fprintf(stderr, "Ran out of memory\n");
-+		goto out;
-+	}
-+
-+	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
-+	if (ret < 0) {
-+		fprintf(stderr, "Error searching %d\n", ret);
-+		goto out;
-+	}
-+
-+	if (!path->nodes[0]) {
-+		fprintf(stderr, "No leaf!\n");
-+		goto out;
-+	}
-+again:
-+	for (i = path->slots[0];
-+	     i < btrfs_header_nritems(path->nodes[0]); i++) {
-+		btrfs_item_key_to_cpu(path->nodes[0], &found_key, i);
-+		if (found_key.type != key.type)
-+			continue;
-+
-+		printf("Using objectid %Lu for first dir\n",
-+		       found_key.objectid);
-+		*objectid = found_key.objectid;
-+		ret = 0;
-+		goto out;
-+	}
-+	do {
-+		ret = next_leaf(root, path);
-+		if (ret < 0) {
-+			fprintf(stderr, "Error getting next leaf %d\n",
-+				ret);
-+			goto out;
-+		} else if (ret > 0) {
-+			fprintf(stderr, "No more leaves\n");
-+			goto out;
-+		}
-+	} while (!path->nodes[0]);
-+	if (path->nodes[0])
-+		goto again;
-+	printf("Couldn't find a dir index item\n");
-+out:
-+	btrfs_free_path(path);
-+	return ret;
-+}
-+
-+int main(int argc, char **argv)
-+{
-+	struct btrfs_root *root;
-+	struct btrfs_key key;
-+	char dir_name[128];
-+	u64 tree_location = 0;
-+	u64 fs_location = 0;
-+	int len;
-+	int ret;
-+	int opt;
-+	int super_mirror = 0;
-+	int find_dir = 0;
-+
-+	while ((opt = getopt(argc, argv, "sviot:u:df:")) != -1) {
-+		switch (opt) {
-+			case 's':
-+				get_snaps = 1;
-+				break;
-+			case 'v':
-+				verbose++;
-+				break;
-+			case 'i':
-+				ignore_errors = 1;
-+				break;
-+			case 'o':
-+				overwrite = 1;
-+				break;
-+			case 't':
-+				errno = 0;
-+				tree_location = (u64)strtoll(optarg, NULL, 10);
-+				if (errno != 0) {
-+					fprintf(stderr, "Tree location not valid\n");
-+					exit(1);
-+				}
-+				break;
-+			case 'f':
-+				errno = 0;
-+				fs_location = (u64)strtoll(optarg, NULL, 10);
-+				if (errno != 0) {
-+					fprintf(stderr, "Fs location not valid\n");
-+					exit(1);
-+				}
-+				break;
-+			case 'u':
-+				errno = 0;
-+				super_mirror = (int)strtol(optarg, NULL, 10);
-+				if (errno != 0 ||
-+				    super_mirror >= BTRFS_SUPER_MIRROR_MAX) {
-+					fprintf(stderr, "Super mirror not "
-+						"valid\n");
-+					exit(1);
-+				}
-+				break;
-+			case 'd':
-+				find_dir = 1;
-+				break;
-+			default:
-+				usage();
-+				exit(1);
-+		}
-+	}
-+
-+	if (optind + 1 >= argc) {
-+		usage();
-+		exit(1);
-+	}
-+
-+	if ((ret = check_mounted(argv[optind])) < 0) {
-+		fprintf(stderr, "Could not check mount status: %s\n",
-+			strerror(ret));
-+		return ret;
-+	} else if (ret) {
-+		fprintf(stderr, "%s is currently mounted.  Aborting.\n", argv[optind + 1]);
-+		return -EBUSY;
-+	}
-+
-+	root = open_fs(argv[optind], tree_location, super_mirror);
-+	if (root == NULL)
-+		return 1;
-+
-+	if (fs_location != 0) {
-+		free_extent_buffer(root->node);
-+		root->node = read_tree_block(root, fs_location, 4096, 0);
-+		if (!root->node) {
-+			fprintf(stderr, "Failed to read fs location\n");
-+			goto out;
-+		}
-+	}
-+
-+	printf("Root objectid is %Lu\n", root->objectid);
-+
-+	memset(path_name, 0, 4096);
-+
-+	strncpy(dir_name, argv[optind + 1], 128);
-+
-+	/* Strip the trailing / on the dir name */
-+	while (1) {
-+		len = strlen(dir_name);
-+		if (dir_name[len - 1] != '/')
-+			break;
-+		dir_name[len - 1] = '\0';
-+	}
-+
-+	if (find_dir) {
-+		ret = find_first_dir(root, &key.objectid);
-+		if (ret)
-+			goto out;
-+	} else {
-+		key.objectid = BTRFS_FIRST_FREE_OBJECTID;
-+	}
-+
-+	ret = search_dir(root->fs_info->fs_root, &key, dir_name);
-+
-+out:
-+	close_ctree(root);
-+	return ret;
-+}
-diff --git a/utils.c b/utils.c
-index 2f4c6e1..ee7fa1b 100644
---- a/utils.c
-+++ b/utils.c
-@@ -31,6 +31,10 @@
- #include <fcntl.h>
- #include <unistd.h>
- #include <mntent.h>
-+#include <linux/loop.h>
-+#include <linux/major.h>
-+#include <linux/kdev_t.h>
-+#include <limits.h>
- #include "kerncompat.h"
- #include "radix-tree.h"
- #include "ctree.h"
-@@ -46,6 +50,20 @@
- static inline int ioctl(int fd, int define, u64 *size) { return 0; }
- #endif
- 
-+#ifndef BLKDISCARD
-+#define BLKDISCARD	_IO(0x12,119)
-+#endif
-+
-+static int
-+discard_blocks(int fd, u64 start, u64 len)
-+{
-+	u64 range[2] = { start, len };
-+
-+	if (ioctl(fd, BLKDISCARD, &range) < 0)
-+		return errno;
-+	return 0;
-+}
-+
- static u64 reference_root_table[] = {
- 	[1] =	BTRFS_ROOT_TREE_OBJECTID,
- 	[2] =	BTRFS_EXTENT_TREE_OBJECTID,
-@@ -103,8 +121,9 @@ int make_btrfs(int fd, const char *device, const char *label,
- 	btrfs_set_super_stripesize(&super, stripesize);
- 	btrfs_set_super_csum_type(&super, BTRFS_CSUM_TYPE_CRC32);
- 	btrfs_set_super_chunk_root_generation(&super, 1);
-+	btrfs_set_super_cache_generation(&super, -1);
- 	if (label)
--		strcpy(super.label, label);
-+		strncpy(super.label, label, BTRFS_LABEL_SIZE - 1);
- 
- 	buf = malloc(sizeof(*buf) + max(sectorsize, leafsize));
- 
-@@ -193,6 +212,8 @@ int make_btrfs(int fd, const char *device, const char *label,
- 	BUG_ON(ret != leafsize);
- 
- 	/* create the items for the extent tree */
-+	memset(buf->data+sizeof(struct btrfs_header), 0,
-+		leafsize-sizeof(struct btrfs_header));
- 	nritems = 0;
- 	itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize);
- 	for (i = 1; i < 7; i++) {
-@@ -238,6 +259,8 @@ int make_btrfs(int fd, const char *device, const char *label,
- 	BUG_ON(ret != leafsize);
- 
- 	/* create the chunk tree */
-+	memset(buf->data+sizeof(struct btrfs_header), 0,
-+		leafsize-sizeof(struct btrfs_header));
- 	nritems = 0;
- 	item_size = sizeof(*dev_item);
- 	itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize) - item_size;
-@@ -319,6 +342,8 @@ int make_btrfs(int fd, const char *device, const char *label,
- 	ret = pwrite(fd, buf->data, leafsize, blocks[3]);
- 
- 	/* create the device tree */
-+	memset(buf->data+sizeof(struct btrfs_header), 0,
-+		leafsize-sizeof(struct btrfs_header));
- 	nritems = 0;
- 	itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize) -
- 		sizeof(struct btrfs_dev_extent);
-@@ -352,6 +377,8 @@ int make_btrfs(int fd, const char *device, const char *label,
- 	ret = pwrite(fd, buf->data, leafsize, blocks[4]);
- 
- 	/* create the FS root */
-+	memset(buf->data+sizeof(struct btrfs_header), 0,
-+		leafsize-sizeof(struct btrfs_header));
- 	btrfs_set_header_bytenr(buf, blocks[5]);
- 	btrfs_set_header_owner(buf, BTRFS_FS_TREE_OBJECTID);
- 	btrfs_set_header_nritems(buf, 0);
-@@ -360,6 +387,8 @@ int make_btrfs(int fd, const char *device, const char *label,
- 	BUG_ON(ret != leafsize);
- 
- 	/* finally create the csum root */
-+	memset(buf->data+sizeof(struct btrfs_header), 0,
-+		leafsize-sizeof(struct btrfs_header));
- 	btrfs_set_header_bytenr(buf, blocks[6]);
- 	btrfs_set_header_owner(buf, BTRFS_CSUM_TREE_OBJECTID);
- 	btrfs_set_header_nritems(buf, 0);
-@@ -507,7 +536,8 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
- 	return 0;
- }
- 
--int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret)
-+int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
-+			 int *mixed)
- {
- 	u64 block_count;
- 	u64 bytenr;
-@@ -527,11 +557,17 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret)
- 	}
- 	zero_end = 1;
- 
--	if (block_count < 256 * 1024 * 1024) {
--		fprintf(stderr, "device %s is too small "
--		        "(must be at least 256 MB)\n", file);
--		exit(1);
-+	if (block_count < 1024 * 1024 * 1024 && !(*mixed)) {
-+		printf("SMALL VOLUME: forcing mixed metadata/data groups\n");
-+		*mixed = 1;
- 	}
-+
-+	/*
-+	 * We intentionally ignore errors from the discard ioctl.  It is
-+	 * not necessary for the mkfs functionality but just an optimization.
-+	 */
-+	discard_blocks(fd, 0, block_count);
-+
- 	ret = zero_dev_start(fd);
- 	if (ret) {
- 		fprintf(stderr, "failed to zero device start %d\n", ret);
-@@ -561,6 +597,7 @@ int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
- {
- 	int ret;
- 	struct btrfs_inode_item inode_item;
-+	time_t now = time(NULL);
- 
- 	memset(&inode_item, 0, sizeof(inode_item));
- 	btrfs_set_stack_inode_generation(&inode_item, trans->transid);
-@@ -568,6 +605,14 @@ int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
- 	btrfs_set_stack_inode_nlink(&inode_item, 1);
- 	btrfs_set_stack_inode_nbytes(&inode_item, root->leafsize);
- 	btrfs_set_stack_inode_mode(&inode_item, S_IFDIR | 0555);
-+	btrfs_set_stack_timespec_sec(&inode_item.atime, now);
-+	btrfs_set_stack_timespec_nsec(&inode_item.atime, 0);
-+	btrfs_set_stack_timespec_sec(&inode_item.ctime, now);
-+	btrfs_set_stack_timespec_nsec(&inode_item.ctime, 0);
-+	btrfs_set_stack_timespec_sec(&inode_item.mtime, now);
-+	btrfs_set_stack_timespec_nsec(&inode_item.mtime, 0);
-+	btrfs_set_stack_timespec_sec(&inode_item.otime, 0);
-+	btrfs_set_stack_timespec_nsec(&inode_item.otime, 0);
- 
- 	if (root->fs_info->tree_root == root)
- 		btrfs_set_super_root_dir(&root->fs_info->super_copy, objectid);
-@@ -586,58 +631,281 @@ error:
- 	return ret;
- }
- 
-+/* checks if a device is a loop device */
-+int is_loop_device (const char* device) {
-+	struct stat statbuf;
-+
-+	if(stat(device, &statbuf) < 0)
-+		return -errno;
-+
-+	return (S_ISBLK(statbuf.st_mode) &&
-+		MAJOR(statbuf.st_rdev) == LOOP_MAJOR);
-+}
-+
-+
-+/* Takes a loop device path (e.g. /dev/loop0) and returns
-+ * the associated file (e.g. /images/my_btrfs.img) */
-+int resolve_loop_device(const char* loop_dev, char* loop_file, int max_len)
-+{
-+	int loop_fd;
-+	int ret_ioctl;
-+	struct loop_info loopinfo;
-+
-+	if ((loop_fd = open(loop_dev, O_RDONLY)) < 0)
-+		return -errno;
-+
-+	ret_ioctl = ioctl(loop_fd, LOOP_GET_STATUS, &loopinfo);
-+	close(loop_fd);
-+
-+	if (ret_ioctl == 0)
-+		strncpy(loop_file, loopinfo.lo_name, max_len);
-+	else
-+		return -errno;
-+
-+	return 0;
-+}
-+
-+/* Checks whether a and b are identical or device
-+ * files associated with the same block device
-+ */
-+int is_same_blk_file(const char* a, const char* b)
-+{
-+	struct stat st_buf_a, st_buf_b;
-+	char real_a[PATH_MAX];
-+	char real_b[PATH_MAX];
-+
-+	if(!realpath(a, real_a) ||
-+	   !realpath(b, real_b))
-+	{
-+		return -errno;
-+	}
-+
-+	/* Identical path? */
-+	if(strcmp(real_a, real_b) == 0)
-+		return 1;
-+
-+	if(stat(a, &st_buf_a) < 0 ||
-+	   stat(b, &st_buf_b) < 0)
-+	{
-+		if (errno == ENOENT)
-+			return 0;
-+		return -errno;
-+	}
-+
-+	/* Same blockdevice? */
-+	if(S_ISBLK(st_buf_a.st_mode) &&
-+	   S_ISBLK(st_buf_b.st_mode) &&
-+	   st_buf_a.st_rdev == st_buf_b.st_rdev)
-+	{
-+		return 1;
-+	}
-+
-+	/* Hardlink? */
-+	if (st_buf_a.st_dev == st_buf_b.st_dev &&
-+	    st_buf_a.st_ino == st_buf_b.st_ino)
-+	{
-+		return 1;
-+	}
-+
-+	return 0;
-+}
-+
-+/* checks if a and b are identical or device
-+ * files associated with the same block device or
-+ * if one file is a loop device that uses the other
-+ * file.
-+ */
-+int is_same_loop_file(const char* a, const char* b)
-+{
-+	char res_a[PATH_MAX];
-+	char res_b[PATH_MAX];
-+	const char* final_a;
-+	const char* final_b;
-+	int ret;
-+
-+	/* Resolve a if it is a loop device */
-+	if((ret = is_loop_device(a)) < 0) {
-+		if (ret == -ENOENT)
-+			return 0;
-+		return ret;
-+	} else if (ret) {
-+		if ((ret = resolve_loop_device(a, res_a, sizeof(res_a))) < 0)
-+			return ret;
-+
-+		final_a = res_a;
-+	} else {
-+		final_a = a;
-+	}
-+
-+	/* Resolve b if it is a loop device */
-+	if ((ret = is_loop_device(b)) < 0) {
-+		if (ret == -ENOENT)
-+			return 0;
-+		return ret;
-+	} else if (ret) {
-+		if((ret = resolve_loop_device(b, res_b, sizeof(res_b))) < 0)
-+			return ret;
-+
-+		final_b = res_b;
-+	} else {
-+		final_b = b;
-+	}
-+
-+	return is_same_blk_file(final_a, final_b);
-+}
-+
-+/* Checks if a file exists and is a block or regular file*/
-+int is_existing_blk_or_reg_file(const char* filename)
-+{
-+	struct stat st_buf;
-+
-+	if(stat(filename, &st_buf) < 0) {
-+		if(errno == ENOENT)
-+			return 0;
-+		else
-+			return -errno;
-+	}
-+
-+	return (S_ISBLK(st_buf.st_mode) || S_ISREG(st_buf.st_mode));
-+}
-+
-+/* Checks if a file is used (directly or indirectly via a loop device)
-+ * by a device in fs_devices
-+ */
-+int blk_file_in_dev_list(struct btrfs_fs_devices* fs_devices, const char* file)
-+{
-+	int ret;
-+	struct list_head *head;
-+	struct list_head *cur;
-+	struct btrfs_device *device;
-+
-+	head = &fs_devices->devices;
-+	list_for_each(cur, head) {
-+		device = list_entry(cur, struct btrfs_device, dev_list);
-+
-+		if((ret = is_same_loop_file(device->name, file)))
-+			return ret;
-+	}
-+
-+	return 0;
-+}
-+
- /*
-  * returns 1 if the device was mounted, < 0 on error or 0 if everything
-- * is safe to continue.  TODO, this should also scan multi-device filesystems
-+ * is safe to continue.
-  */
--int check_mounted(char *file)
-+int check_mounted(const char* file)
- {
--	struct mntent *mnt;
--	struct stat st_buf;
--	dev_t file_dev = 0;
--	dev_t file_rdev = 0;
--	ino_t file_ino = 0;
-+	int fd;
-+	int ret;
-+
-+	fd = open(file, O_RDONLY);
-+	if (fd < 0) {
-+		fprintf (stderr, "check_mounted(): Could not open %s\n", file);
-+		return -errno;
-+	}
-+
-+	ret =  check_mounted_where(fd, file, NULL, 0, NULL);
-+	close(fd);
-+
-+	return ret;
-+}
-+
-+int check_mounted_where(int fd, const char *file, char *where, int size,
-+			struct btrfs_fs_devices **fs_dev_ret)
-+{
-+	int ret;
-+	u64 total_devs = 1;
-+	int is_btrfs;
-+	struct btrfs_fs_devices *fs_devices_mnt = NULL;
- 	FILE *f;
--	int ret = 0;
-+	struct mntent *mnt;
-+
-+	/* scan the initial device */
-+	ret = btrfs_scan_one_device(fd, file, &fs_devices_mnt,
-+				    &total_devs, BTRFS_SUPER_INFO_OFFSET);
-+	is_btrfs = (ret >= 0);
- 
-+	/* scan other devices */
-+	if (is_btrfs && total_devs > 1) {
-+		if((ret = btrfs_scan_for_fsid(fs_devices_mnt, total_devs, 1)))
-+			return ret;
-+	}
-+
-+	/* iterate over the list of currently mountes filesystems */
- 	if ((f = setmntent ("/proc/mounts", "r")) == NULL)
- 		return -errno;
- 
--	if (stat(file, &st_buf) < 0) {
--		return -errno;
--	} else {
--		if (S_ISBLK(st_buf.st_mode)) {
--			file_rdev = st_buf.st_rdev;
-+	while ((mnt = getmntent (f)) != NULL) {
-+		if(is_btrfs) {
-+			if(strcmp(mnt->mnt_type, "btrfs") != 0)
-+				continue;
-+
-+			ret = blk_file_in_dev_list(fs_devices_mnt, mnt->mnt_fsname);
- 		} else {
--			file_dev = st_buf.st_dev;
--			file_ino = st_buf.st_ino;
-+			/* ignore entries in the mount table that are not
-+			   associated with a file*/
-+			if((ret = is_existing_blk_or_reg_file(mnt->mnt_fsname)) < 0)
-+				goto out_mntloop_err;
-+			else if(!ret)
-+				continue;
-+
-+			ret = is_same_loop_file(file, mnt->mnt_fsname);
- 		}
--	}
- 
--	while ((mnt = getmntent (f)) != NULL) {
--		if (strcmp(file, mnt->mnt_fsname) == 0)
-+		if(ret < 0)
-+			goto out_mntloop_err;
-+		else if(ret)
- 			break;
--
--		if (stat(mnt->mnt_fsname, &st_buf) == 0) {
--			if (S_ISBLK(st_buf.st_mode)) {
--				if (file_rdev && (file_rdev == st_buf.st_rdev))
--					break;
--			} else if (file_dev && ((file_dev == st_buf.st_dev) &&
--						(file_ino == st_buf.st_ino))) {
--					break;
--			}
--		}
- 	}
- 
--	if (mnt) {
--		/* found an entry in mnt table */
--		ret = 1;
--	}
-+	/* Did we find an entry in mnt table? */
-+	if (mnt && size && where)
-+		strncpy(where, mnt->mnt_dir, size);
-+	if (fs_dev_ret)
-+		*fs_dev_ret = fs_devices_mnt;
-+
-+	ret = (mnt != NULL);
- 
-+out_mntloop_err:
- 	endmntent (f);
-+
- 	return ret;
- }
- 
-+/* Gets the mount point of btrfs filesystem that is using the specified device.
-+ * Returns 0 is everything is good, <0 if we have an error.
-+ * TODO: Fix this fucntion and check_mounted to work with multiple drive BTRFS
-+ * setups.
-+ */
-+int get_mountpt(char *dev, char *mntpt, size_t size)
-+{
-+       struct mntent *mnt;
-+       FILE *f;
-+       int ret = 0;
-+
-+       f = setmntent("/proc/mounts", "r");
-+       if (f == NULL)
-+               return -errno;
-+
-+       while ((mnt = getmntent(f)) != NULL )
-+       {
-+               if (strcmp(dev, mnt->mnt_fsname) == 0)
-+               {
-+                       strncpy(mntpt, mnt->mnt_dir, size);
-+                       break;
-+               }
-+       }
-+
-+       if (mnt == NULL)
-+       {
-+               /* We didn't find an entry so lets report an error */
-+               ret = -1;
-+       }
-+
-+       return ret;
-+}
-+
- struct pending_dir {
- 	struct list_head list;
- 	char name[256];
-@@ -648,6 +916,7 @@ void btrfs_register_one_device(char *fname)
- 	struct btrfs_ioctl_vol_args args;
- 	int fd;
- 	int ret;
-+	int e;
- 
- 	fd = open("/dev/btrfs-control", O_RDONLY);
- 	if (fd < 0) {
-@@ -655,8 +924,13 @@ void btrfs_register_one_device(char *fname)
- 			"skipping device registration\n");
- 		return;
- 	}
--	strcpy(args.name, fname);
-+	strncpy(args.name, fname, BTRFS_PATH_NAME_MAX);
- 	ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args);
-+	e = errno;
-+	if(ret<0){
-+		fprintf(stderr, "ERROR: unable to scan the device '%s' - %s\n",
-+			fname, strerror(e));
-+	}
- 	close(fd);
- }
- 
-@@ -694,7 +968,7 @@ again:
- 	}
- 	dirp = opendir(dirname);
- 	if (!dirp) {
--		fprintf(stderr, "Unable to open /sys/block for scanning\n");
-+		fprintf(stderr, "Unable to open %s for scanning\n", dirname);
- 		return -ENOENT;
- 	}
- 	while(1) {
-@@ -729,7 +1003,8 @@ again:
- 		}
- 		fd = open(fullpath, O_RDONLY);
- 		if (fd < 0) {
--			fprintf(stderr, "failed to read %s\n", fullpath);
-+			fprintf(stderr, "failed to read %s: %s\n", fullpath,
-+					strerror(errno));
- 			continue;
- 		}
- 		ret = btrfs_scan_one_device(fd, fullpath, &tmp_devices,
-@@ -759,7 +1034,12 @@ fail:
- int btrfs_scan_for_fsid(struct btrfs_fs_devices *fs_devices, u64 total_devs,
- 			int run_ioctls)
- {
--	return btrfs_scan_one_dir("/dev", run_ioctls);
-+	int ret;
-+
-+	ret = btrfs_scan_block_devices(run_ioctls);
-+	if (ret)
-+		ret = btrfs_scan_one_dir("/dev", run_ioctls);
-+	return ret;
- }
- 
- int btrfs_device_already_in_root(struct btrfs_root *root, int fd,
-@@ -798,6 +1078,7 @@ static char *size_strs[] = { "", "KB", "MB", "GB", "TB",
- char *pretty_sizes(u64 size)
- {
- 	int num_divs = 0;
-+        int pretty_len = 16;
- 	u64 last_size = size;
- 	u64 fract_size = size;
- 	float fraction;
-@@ -815,8 +1096,113 @@ char *pretty_sizes(u64 size)
- 		return NULL;
- 
- 	fraction = (float)fract_size / 1024;
--	pretty = malloc(16);
--	sprintf(pretty, "%.2f%s", fraction, size_strs[num_divs-1]);
-+	pretty = malloc(pretty_len);
-+	snprintf(pretty, pretty_len, "%.2f%s", fraction, size_strs[num_divs-1]);
- 	return pretty;
- }
- 
-+/*
-+ * Checks to make sure that the label matches our requirements.
-+ * Returns:
-+       0    if everything is safe and usable
-+      -1    if the label is too long
-+      -2    if the label contains an invalid character
-+ */
-+int check_label(char *input)
-+{
-+       int i;
-+       int len = strlen(input);
-+
-+       if (len > BTRFS_LABEL_SIZE) {
-+               return -1;
-+       }
-+
-+       for (i = 0; i < len; i++) {
-+               if (input[i] == '/' || input[i] == '\\') {
-+                       return -2;
-+               }
-+       }
-+
-+       return 0;
-+}
-+
-+int btrfs_scan_block_devices(int run_ioctl)
-+{
-+
-+	struct stat st;
-+	int ret;
-+	int fd;
-+	struct btrfs_fs_devices *tmp_devices;
-+	u64 num_devices;
-+	FILE *proc_partitions;
-+	int i;
-+	char buf[1024];
-+	char fullpath[110];
-+	int scans = 0;
-+	int special;
-+
-+scan_again:
-+	proc_partitions = fopen("/proc/partitions","r");
-+	if (!proc_partitions) {
-+		fprintf(stderr, "Unable to open '/proc/partitions' for scanning\n");
-+		return -ENOENT;
-+	}
-+	/* skip the header */
-+	for(i=0; i < 2 ; i++)
-+		if(!fgets(buf, 1023, proc_partitions)){
-+		fprintf(stderr, "Unable to read '/proc/partitions' for scanning\n");
-+		fclose(proc_partitions);
-+		return -ENOENT;
-+	}
-+
-+	strcpy(fullpath,"/dev/");
-+	while(fgets(buf, 1023, proc_partitions)) {
-+		i = sscanf(buf," %*d %*d %*d %99s", fullpath+5);
-+
-+		/*
-+		 * multipath and MD devices may register as a btrfs filesystem
-+		 * both through the original block device and through
-+		 * the special (/dev/mapper or /dev/mdX) entry.
-+		 * This scans the special entries last
-+		 */
-+		special = strncmp(fullpath, "/dev/dm-", strlen("/dev/dm-")) == 0;
-+		if (!special)
-+			special = strncmp(fullpath, "/dev/md", strlen("/dev/md")) == 0;
-+
-+		if (scans == 0 && special)
-+			continue;
-+		if (scans > 0 && !special)
-+			continue;
-+
-+		ret = lstat(fullpath, &st);
-+		if (ret < 0) {
-+			fprintf(stderr, "failed to stat %s\n", fullpath);
-+			continue;
-+		}
-+		if (!S_ISBLK(st.st_mode)) {
-+			continue;
-+		}
-+
-+		fd = open(fullpath, O_RDONLY);
-+		if (fd < 0) {
-+			fprintf(stderr, "failed to read %s\n", fullpath);
-+			continue;
-+		}
-+		ret = btrfs_scan_one_device(fd, fullpath, &tmp_devices,
-+					    &num_devices,
-+					    BTRFS_SUPER_INFO_OFFSET);
-+		if (ret == 0 && run_ioctl > 0) {
-+			btrfs_register_one_device(fullpath);
-+		}
-+		close(fd);
-+	}
-+
-+	fclose(proc_partitions);
-+
-+	if (scans == 0) {
-+		scans++;
-+		goto scan_again;
-+	}
-+	return 0;
-+}
-+
-diff --git a/utils.h b/utils.h
-index 7ff542b..c5f55e1 100644
---- a/utils.h
-+++ b/utils.h
-@@ -27,7 +27,7 @@ int make_btrfs(int fd, const char *device, const char *label,
- int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
- 			struct btrfs_root *root, u64 objectid);
- int btrfs_prepare_device(int fd, char *file, int zero_end,
--			 u64 *block_count_ret);
-+			 u64 *block_count_ret, int *mixed);
- int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
- 		      struct btrfs_root *root, int fd, char *path,
- 		      u64 block_count, u32 io_width, u32 io_align,
-@@ -36,8 +36,14 @@ int btrfs_scan_for_fsid(struct btrfs_fs_devices *fs_devices, u64 total_devs,
- 			int run_ioctls);
- void btrfs_register_one_device(char *fname);
- int btrfs_scan_one_dir(char *dirname, int run_ioctl);
--int check_mounted(char *devicename);
-+int check_mounted(const char *devicename);
-+int check_mounted_where(int fd, const char *file, char *where, int size,
-+			struct btrfs_fs_devices **fs_devices_mnt);
- int btrfs_device_already_in_root(struct btrfs_root *root, int fd,
- 				 int super_offset);
- char *pretty_sizes(u64 size);
-+int check_label(char *input);
-+int get_mountpt(char *dev, char *mntpt, size_t size);
-+
-+int btrfs_scan_block_devices(int run_ioctl);
- #endif
-diff --git a/volumes.c b/volumes.c
-index 7671855..8dca5e1 100644
---- a/volumes.c
-+++ b/volumes.c
-@@ -35,18 +35,6 @@ struct stripe {
- 	u64 physical;
- };
- 
--struct map_lookup {
--	struct cache_extent ce;
--	u64 type;
--	int io_align;
--	int io_width;
--	int stripe_len;
--	int sector_size;
--	int num_stripes;
--	int sub_stripes;
--	struct btrfs_bio_stripe stripes[];
--};
--
- #define map_lookup_size(n) (sizeof(struct map_lookup) + \
- 			    (sizeof(struct btrfs_bio_stripe) * (n)))
- 
-@@ -128,7 +116,14 @@ static int device_list_add(const char *path,
- 			btrfs_stack_device_bytes_used(&disk_super->dev_item);
- 		list_add(&device->dev_list, &fs_devices->devices);
- 		device->fs_devices = fs_devices;
--	}
-+	} else if (!device->name || strcmp(device->name, path)) {
-+		char *name = strdup(path);
-+                if (!name)
-+                        return -ENOMEM;
-+                kfree(device->name);
-+                device->name = name;
-+        }
-+
- 
- 	if (found_transid > fs_devices->latest_trans) {
- 		fs_devices->latest_devid = devid;
-@@ -643,7 +638,6 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
- 	struct list_head *cur;
- 	struct map_lookup *map;
- 	int min_stripe_size = 1 * 1024 * 1024;
--	u64 physical;
- 	u64 calc_size = 8 * 1024 * 1024;
- 	u64 min_free;
- 	u64 max_chunk_size = 4 * calc_size;
-@@ -811,7 +805,6 @@ again:
- 		btrfs_set_stack_stripe_devid(stripe, device->devid);
- 		btrfs_set_stack_stripe_offset(stripe, dev_offset);
- 		memcpy(stripe->dev_uuid, device->uuid, BTRFS_UUID_SIZE);
--		physical = dev_offset;
- 		index++;
- 	}
- 	BUG_ON(!list_empty(&private_devs));
-@@ -857,6 +850,108 @@ again:
- 	return ret;
- }
- 
-+int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
-+			   struct btrfs_root *extent_root, u64 *start,
-+			   u64 num_bytes, u64 type)
-+{
-+	u64 dev_offset;
-+	struct btrfs_fs_info *info = extent_root->fs_info;
-+	struct btrfs_root *chunk_root = extent_root->fs_info->chunk_root;
-+	struct btrfs_stripe *stripes;
-+	struct btrfs_device *device = NULL;
-+	struct btrfs_chunk *chunk;
-+	struct list_head *dev_list = &extent_root->fs_info->fs_devices->devices;
-+	struct list_head *cur;
-+	struct map_lookup *map;
-+	u64 calc_size = 8 * 1024 * 1024;
-+	int num_stripes = 1;
-+	int sub_stripes = 0;
-+	int ret;
-+	int index;
-+	int stripe_len = 64 * 1024;
-+	struct btrfs_key key;
-+
-+	key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
-+	key.type = BTRFS_CHUNK_ITEM_KEY;
-+	ret = find_next_chunk(chunk_root, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
-+			      &key.offset);
-+	if (ret)
-+		return ret;
-+
-+	chunk = kmalloc(btrfs_chunk_item_size(num_stripes), GFP_NOFS);
-+	if (!chunk)
-+		return -ENOMEM;
-+
-+	map = kmalloc(map_lookup_size(num_stripes), GFP_NOFS);
-+	if (!map) {
-+		kfree(chunk);
-+		return -ENOMEM;
-+	}
-+
-+	stripes = &chunk->stripe;
-+	calc_size = num_bytes;
-+
-+	index = 0;
-+	cur = dev_list->next;
-+	device = list_entry(cur, struct btrfs_device, dev_list);
-+
-+	while (index < num_stripes) {
-+		struct btrfs_stripe *stripe;
-+
-+		ret = btrfs_alloc_dev_extent(trans, device,
-+			     info->chunk_root->root_key.objectid,
-+			     BTRFS_FIRST_CHUNK_TREE_OBJECTID, key.offset,
-+			     calc_size, &dev_offset);
-+		BUG_ON(ret);
-+
-+		device->bytes_used += calc_size;
-+		ret = btrfs_update_device(trans, device);
-+		BUG_ON(ret);
-+
-+		map->stripes[index].dev = device;
-+		map->stripes[index].physical = dev_offset;
-+		stripe = stripes + index;
-+		btrfs_set_stack_stripe_devid(stripe, device->devid);
-+		btrfs_set_stack_stripe_offset(stripe, dev_offset);
-+		memcpy(stripe->dev_uuid, device->uuid, BTRFS_UUID_SIZE);
-+		index++;
-+	}
-+
-+	/* key was set above */
-+	btrfs_set_stack_chunk_length(chunk, num_bytes);
-+	btrfs_set_stack_chunk_owner(chunk, extent_root->root_key.objectid);
-+	btrfs_set_stack_chunk_stripe_len(chunk, stripe_len);
-+	btrfs_set_stack_chunk_type(chunk, type);
-+	btrfs_set_stack_chunk_num_stripes(chunk, num_stripes);
-+	btrfs_set_stack_chunk_io_align(chunk, stripe_len);
-+	btrfs_set_stack_chunk_io_width(chunk, stripe_len);
-+	btrfs_set_stack_chunk_sector_size(chunk, extent_root->sectorsize);
-+	btrfs_set_stack_chunk_sub_stripes(chunk, sub_stripes);
-+	map->sector_size = extent_root->sectorsize;
-+	map->stripe_len = stripe_len;
-+	map->io_align = stripe_len;
-+	map->io_width = stripe_len;
-+	map->type = type;
-+	map->num_stripes = num_stripes;
-+	map->sub_stripes = sub_stripes;
-+
-+	ret = btrfs_insert_item(trans, chunk_root, &key, chunk,
-+				btrfs_chunk_item_size(num_stripes));
-+	BUG_ON(ret);
-+	*start = key.offset;
-+
-+	map->ce.start = key.offset;
-+	map->ce.size = num_bytes;
-+
-+	ret = insert_existing_cache_extent(
-+			   &extent_root->fs_info->mapping_tree.cache_tree,
-+			   &map->ce);
-+	BUG_ON(ret);
-+
-+	kfree(chunk);
-+	return ret;
-+}
-+
- void btrfs_mapping_init(struct btrfs_mapping_tree *tree)
- {
- 	cache_tree_init(&tree->cache_tree);
-@@ -867,14 +962,12 @@ int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len)
- 	struct cache_extent *ce;
- 	struct map_lookup *map;
- 	int ret;
--	u64 offset;
- 
- 	ce = find_first_cache_extent(&map_tree->cache_tree, logical);
- 	BUG_ON(!ce);
- 	BUG_ON(ce->start > logical || ce->start + ce->size < logical);
- 	map = container_of(ce, struct map_lookup, ce);
- 
--	offset = logical - ce->start;
- 	if (map->type & (BTRFS_BLOCK_GROUP_DUP | BTRFS_BLOCK_GROUP_RAID1))
- 		ret = map->num_stripes;
- 	else if (map->type & BTRFS_BLOCK_GROUP_RAID10)
-@@ -884,6 +977,30 @@ int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len)
- 	return ret;
- }
- 
-+int btrfs_next_metadata(struct btrfs_mapping_tree *map_tree, u64 *logical,
-+			u64 *size)
-+{
-+	struct cache_extent *ce;
-+	struct map_lookup *map;
-+
-+	ce = find_first_cache_extent(&map_tree->cache_tree, *logical);
-+
-+	while (ce) {
-+		ce = next_cache_extent(ce);
-+		if (!ce)
-+			return -ENOENT;
-+
-+		map = container_of(ce, struct map_lookup, ce);
-+		if (map->type & BTRFS_BLOCK_GROUP_METADATA) {
-+			*logical = ce->start;
-+			*size = ce->size;
-+			return 0;
-+		}
-+	}
-+
-+	return -ENOENT;
-+}
-+
- int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree,
- 		     u64 chunk_start, u64 physical, u64 devid,
- 		     u64 **logical, int *naddrs, int *stripe_len)
-@@ -944,6 +1061,14 @@ int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
- 		    u64 logical, u64 *length,
- 		    struct btrfs_multi_bio **multi_ret, int mirror_num)
- {
-+	return __btrfs_map_block(map_tree, rw, logical, length, NULL,
-+				 multi_ret, mirror_num);
-+}
-+
-+int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
-+		    u64 logical, u64 *length, u64 *type,
-+		    struct btrfs_multi_bio **multi_ret, int mirror_num)
-+{
- 	struct cache_extent *ce;
- 	struct map_lookup *map;
- 	u64 offset;
-@@ -959,16 +1084,24 @@ int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
- 		stripes_allocated = 1;
- 	}
- again:
-+	ce = find_first_cache_extent(&map_tree->cache_tree, logical);
-+	if (!ce) {
-+		if (multi)
-+			kfree(multi);
-+		return -ENOENT;
-+	}
-+	if (ce->start > logical || ce->start + ce->size < logical) {
-+		if (multi)
-+			kfree(multi);
-+		return -ENOENT;
-+	}
-+
- 	if (multi_ret) {
- 		multi = kzalloc(btrfs_multi_bio_size(stripes_allocated),
- 				GFP_NOFS);
- 		if (!multi)
- 			return -ENOMEM;
- 	}
--
--	ce = find_first_cache_extent(&map_tree->cache_tree, logical);
--	BUG_ON(!ce);
--	BUG_ON(ce->start > logical || ce->start + ce->size < logical);
- 	map = container_of(ce, struct map_lookup, ce);
- 	offset = logical - ce->start;
- 
-@@ -1032,8 +1165,6 @@ again:
- 			multi->num_stripes = map->sub_stripes;
- 		else if (mirror_num)
- 			stripe_index += mirror_num - 1;
--		else
--			stripe_index = stripe_nr % map->sub_stripes;
- 
- 		stripe_nr = stripe_nr / factor;
- 	} else if (map->type & BTRFS_BLOCK_GROUP_DUP) {
-@@ -1060,6 +1191,8 @@ again:
- 		stripe_index++;
- 	}
- 	*multi_ret = multi;
-+	if (type)
-+		*type = map->type;
- out:
- 	return 0;
- }
-@@ -1159,6 +1292,16 @@ int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset)
- 	return readonly;
- }
- 
-+static struct btrfs_device *fill_missing_device(u64 devid)
-+{
-+	struct btrfs_device *device;
-+
-+	device = kzalloc(sizeof(*device), GFP_NOFS);
-+	device->devid = devid;
-+	device->fd = -1;
-+	return device;
-+}
-+
- static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
- 			  struct extent_buffer *leaf,
- 			  struct btrfs_chunk *chunk)
-@@ -1209,8 +1352,9 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
- 		map->stripes[i].dev = btrfs_find_device(root, devid, uuid,
- 							NULL);
- 		if (!map->stripes[i].dev) {
--			kfree(map);
--			return -EIO;
-+			map->stripes[i].dev = fill_missing_device(devid);
-+			printf("warning, device %llu is missing\n",
-+			       (unsigned long long)devid);
- 		}
- 
- 	}
-@@ -1333,7 +1477,7 @@ int btrfs_read_sys_array(struct btrfs_root *root)
- 	u8 *ptr;
- 	unsigned long sb_ptr;
- 	u32 cur;
--	int ret;
-+	int ret = 0;
- 
- 	sb = btrfs_find_create_tree_block(root, BTRFS_SUPER_INFO_OFFSET,
- 					  BTRFS_SUPER_INFO_SIZE);
-@@ -1364,7 +1508,8 @@ int btrfs_read_sys_array(struct btrfs_root *root)
- 		if (key.type == BTRFS_CHUNK_ITEM_KEY) {
- 			chunk = (struct btrfs_chunk *)sb_ptr;
- 			ret = read_one_chunk(root, &key, sb, chunk);
--			BUG_ON(ret);
-+			if (ret)
-+				break;
- 			num_stripes = btrfs_chunk_num_stripes(sb, chunk);
- 			len = btrfs_chunk_item_size(num_stripes);
- 		} else {
-@@ -1375,7 +1520,7 @@ int btrfs_read_sys_array(struct btrfs_root *root)
- 		cur += len;
- 	}
- 	free_extent_buffer(sb);
--	return 0;
-+	return ret;
- }
- 
- int btrfs_read_chunk_tree(struct btrfs_root *root)
-diff --git a/volumes.h b/volumes.h
-index bb78751..9ff6182 100644
---- a/volumes.h
-+++ b/volumes.h
-@@ -18,6 +18,7 @@
- 
- #ifndef __BTRFS_VOLUMES_
- #define __BTRFS_VOLUMES_
-+
- struct btrfs_device {
- 	struct list_head dev_list;
- 	struct btrfs_root *dev_root;
-@@ -88,17 +89,65 @@ struct btrfs_multi_bio {
- 	struct btrfs_bio_stripe stripes[];
- };
- 
-+struct map_lookup {
-+	struct cache_extent ce;
-+	u64 type;
-+	int io_align;
-+	int io_width;
-+	int stripe_len;
-+	int sector_size;
-+	int num_stripes;
-+	int sub_stripes;
-+	struct btrfs_bio_stripe stripes[];
-+};
-+
- #define btrfs_multi_bio_size(n) (sizeof(struct btrfs_multi_bio) + \
- 			    (sizeof(struct btrfs_bio_stripe) * (n)))
- 
-+/*
-+ * Restriper's general type filter
-+ */
-+#define BTRFS_BALANCE_DATA		(1ULL << 0)
-+#define BTRFS_BALANCE_SYSTEM		(1ULL << 1)
-+#define BTRFS_BALANCE_METADATA		(1ULL << 2)
-+
-+#define BTRFS_BALANCE_TYPE_MASK		(BTRFS_BALANCE_DATA |	    \
-+					 BTRFS_BALANCE_SYSTEM |	    \
-+					 BTRFS_BALANCE_METADATA)
-+
-+#define BTRFS_BALANCE_FORCE		(1ULL << 3)
-+#define BTRFS_BALANCE_RESUME		(1ULL << 4)
-+
-+/*
-+ * Balance filters
-+ */
-+#define BTRFS_BALANCE_ARGS_PROFILES	(1ULL << 0)
-+#define BTRFS_BALANCE_ARGS_USAGE	(1ULL << 1)
-+#define BTRFS_BALANCE_ARGS_DEVID	(1ULL << 2)
-+#define BTRFS_BALANCE_ARGS_DRANGE	(1ULL << 3)
-+#define BTRFS_BALANCE_ARGS_VRANGE	(1ULL << 4)
-+
-+/*
-+ * Profile changing flags.  When SOFT is set we won't relocate chunk if
-+ * it already has the target profile (even though it may be
-+ * half-filled).
-+ */
-+#define BTRFS_BALANCE_ARGS_CONVERT	(1ULL << 8)
-+#define BTRFS_BALANCE_ARGS_SOFT		(1ULL << 9)
-+
- int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
- 			   struct btrfs_device *device,
- 			   u64 chunk_tree, u64 chunk_objectid,
- 			   u64 chunk_offset,
- 			   u64 num_bytes, u64 *start);
-+int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
-+		      u64 logical, u64 *length, u64 *type,
-+		      struct btrfs_multi_bio **multi_ret, int mirror_num);
- int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
- 		    u64 logical, u64 *length,
- 		    struct btrfs_multi_bio **multi_ret, int mirror_num);
-+int btrfs_next_metadata(struct btrfs_mapping_tree *map_tree, u64 *logical,
-+			u64 *size);
- int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree,
- 		     u64 chunk_start, u64 physical, u64 devid,
- 		     u64 **logical, int *naddrs, int *stripe_len);
-@@ -107,6 +156,9 @@ int btrfs_read_chunk_tree(struct btrfs_root *root);
- int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
- 		      struct btrfs_root *extent_root, u64 *start,
- 		      u64 *num_bytes, u64 type);
-+int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
-+			   struct btrfs_root *extent_root, u64 *start,
-+			   u64 num_bytes, u64 type);
- int btrfs_read_super_device(struct btrfs_root *root, struct extent_buffer *buf);
- int btrfs_add_device(struct btrfs_trans_handle *trans,
- 		     struct btrfs_root *root,
diff --git a/btrfs-progs.spec b/btrfs-progs.spec
index 1e551fb..936ac20 100644
--- a/btrfs-progs.spec
+++ b/btrfs-progs.spec
@@ -1,20 +1,23 @@
 Name:           btrfs-progs
-Version:        0.19
-Release:        20%{?dist}
+Version:        0.19.20120817git043a639
+Release:        1%{?dist}
 Summary:        Userspace programs for btrfs
 
 Group:          System Environment/Base
 License:        GPLv2
 URL:            http://btrfs.wiki.kernel.org/index.php/Main_Page
-Source0:        http://www.kernel.org/pub/linux/kernel/people/mason/btrfs/%{name}-%{version}.tar.bz2
-Patch0: btrfs-progs-upstream.patch
-Patch1: btrfs-progs-fix-labels.patch
-Patch2: btrfs-progs-valgrind.patch
-Patch3: btrfs-progs-build-fixes.patch
-Patch4: Btrfs-progs-make-btrfs-filesystem-show-uuid-actually.patch
+Source0:	%{name}-%{version}.tar.bz2
+Patch0: btrfs-progs-fix-labels.patch
+Patch1: btrfs-progs-valgrind.patch
+Patch2: btrfs-progs-build-fixes.patch
+Patch3: Btrfs-progs-add-btrfs-device-ready-command.patch
+Patch4: Btrfs-progs-detect-if-the-disk-we-are-formatting-is-.patch
+Patch5: Btrfs-progs-only-enforce-a-maximum-size-if-we-specif.patch
+Patch6: btrfs-init-dev-list.patch
+
 BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
-BuildRequires:  e2fsprogs-devel, libuuid-devel, zlib-devel, libacl-devel
+BuildRequires:  e2fsprogs-devel, libuuid-devel, zlib-devel, libacl-devel, libblkid-devel
 
 %define _root_sbindir /sbin
 
@@ -29,6 +32,8 @@ check, modify and correct any inconsistencies in the btrfs filesystem.
 %patch2 -p1
 %patch3 -p1
 %patch4 -p1
+%patch5 -p1
+%patch6 -p1
 
 %build
 make CFLAGS="$RPM_OPT_FLAGS" %{?_smp_mflags}
@@ -65,6 +70,9 @@ rm -rf $RPM_BUILD_ROOT
 %{_mandir}/man8/btrfs.8.gz
 
 %changelog
+* Fri Aug 17 2012 Josef Bacik <josef@toxicpanda.com> 0.19.20120817git043a639-1
+- update to latest btrfs-progs
+
 * Wed Jul 18 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.19-20
 - Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
 
diff --git a/sources b/sources
index 491a067..dd0e90c 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-5854728d080cc76f21a83bdc99b6ddaa  btrfs-progs-0.19.tar.bz2
+97159994f7f5af4ac159825639bc88fc  btrfs-progs-0.19.20120817git043a639.tar.bz2