[btrfs-progs] - update to the latest btrfs-progs
Josef Bacik
josef at fedoraproject.org
Wed Apr 11 15:17:04 UTC 2012
commit 8bffa86e0b80d32e53eeee7efcd259534d5b36cd
Author: Josef Bacik <josef at redhat.com>
Date: Wed Apr 11 11:16:51 2012 -0400
- update to the latest btrfs-progs
btrfs-progs-build-fixes.patch | 24 +-
btrfs-progs-fix-labels.patch | 15 +-
btrfs-progs-upstream.patch |18574 ++++++++++++++++++++++++++++++++++-------
btrfs-progs-valgrind.patch | 94 +-
btrfs-progs.spec | 40 +-
5 files changed, 15415 insertions(+), 3332 deletions(-)
---
diff --git a/btrfs-progs-build-fixes.patch b/btrfs-progs-build-fixes.patch
index a78b451..136ac62 100644
--- a/btrfs-progs-build-fixes.patch
+++ b/btrfs-progs-build-fixes.patch
@@ -1,7 +1,6 @@
-diff --git a/btrfsck.c b/btrfsck.c
-index 63e44d1..1e040c4 100644
---- a/btrfsck.c
-+++ b/btrfsck.c
+diff -up btrfs-progs-0.19/btrfsck.c.orig btrfs-progs-0.19/btrfsck.c
+--- btrfs-progs-0.19/btrfsck.c.orig 2012-04-11 10:44:44.000000000 -0400
++++ btrfs-progs-0.19/btrfsck.c 2012-04-11 10:45:35.769767880 -0400
@@ -22,7 +22,9 @@
#include <stdlib.h>
#include <unistd.h>
@@ -9,23 +8,22 @@ index 63e44d1..1e040c4 100644
+#include <sys/types.h>
#include <sys/stat.h>
+#include <unistd.h>
+ #include <getopt.h>
#include "kerncompat.h"
#include "ctree.h"
- #include "disk-io.h"
-diff --git a/mkfs.c b/mkfs.c
-index 2e99b95..638f4c2 100644
---- a/mkfs.c
-+++ b/mkfs.c
-@@ -348,7 +348,7 @@ int main(int ac, char **av)
+diff -up btrfs-progs-0.19/mkfs.c.orig btrfs-progs-0.19/mkfs.c
+--- btrfs-progs-0.19/mkfs.c.orig 2012-04-11 10:44:44.652047854 -0400
++++ btrfs-progs-0.19/mkfs.c 2012-04-11 10:46:03.727553072 -0400
+@@ -1198,7 +1198,7 @@ int main(int ac, char **av)
u64 alloc_start = 0;
- u64 metadata_profile = BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_DUP;
- u64 data_profile = BTRFS_BLOCK_GROUP_RAID0;
+ u64 metadata_profile = 0;
+ u64 data_profile = 0;
- u32 leafsize = getpagesize();
+ u32 leafsize = sysconf(_SC_PAGESIZE);
u32 sectorsize = 4096;
u32 nodesize = leafsize;
u32 stripesize = 4096;
-@@ -405,7 +405,7 @@ int main(int ac, char **av)
+@@ -1270,7 +1270,7 @@ int main(int ac, char **av)
print_usage();
}
}
diff --git a/btrfs-progs-fix-labels.patch b/btrfs-progs-fix-labels.patch
index 92a52e4..c9b0c32 100644
--- a/btrfs-progs-fix-labels.patch
+++ b/btrfs-progs-fix-labels.patch
@@ -1,17 +1,16 @@
-diff --git a/mkfs.c b/mkfs.c
-index d664254..138bcc9 100644
---- a/mkfs.c
-+++ b/mkfs.c
-@@ -294,7 +294,6 @@ static u64 parse_profile(char *s)
+diff -up btrfs-progs-0.19/mkfs.c.orig btrfs-progs-0.19/mkfs.c
+--- btrfs-progs-0.19/mkfs.c.orig 2012-04-11 10:38:26.825973948 -0400
++++ btrfs-progs-0.19/mkfs.c 2012-04-11 10:39:46.031360540 -0400
+@@ -372,7 +372,6 @@ static u64 parse_profile(char *s)
static char *parse_label(char *input)
{
- int i;
int len = strlen(input);
- if (len > BTRFS_LABEL_SIZE) {
-@@ -302,12 +301,6 @@ static char *parse_label(char *input)
- BTRFS_LABEL_SIZE);
+ if (len >= BTRFS_LABEL_SIZE) {
+@@ -380,12 +379,6 @@ static char *parse_label(char *input)
+ BTRFS_LABEL_SIZE - 1);
exit(1);
}
- for (i = 0; i < len; i++) {
diff --git a/btrfs-progs-upstream.patch b/btrfs-progs-upstream.patch
index e81558f..83e06a0 100644
--- a/btrfs-progs-upstream.patch
+++ b/btrfs-progs-upstream.patch
@@ -1,74 +1,251 @@
+diff --git a/.gitignore b/.gitignore
+new file mode 100644
+index 0000000..0e560d5
+--- /dev/null
++++ b/.gitignore
+@@ -0,0 +1,15 @@
++*.o
++.*.o.d
++version.h
++man/*.gz
++btrfs
++btrfs-debug-tree
++btrfs-map-logical
++btrfs-show
++btrfs-vol
++btrfsck
++btrfsctl
++find-root
++mkfs.btrfs
++repair
++restore
+diff --git a/INSTALL b/INSTALL
+index 16b45a5..6afbd90 100644
+--- a/INSTALL
++++ b/INSTALL
+@@ -22,27 +22,38 @@ in the e2fsprogs sources, and is usually available as libuuid or
+ e2fsprogs-devel from various distros.
+
+ Building the utilities is just make ; make install. The programs go
+-into /usr/local/bin. The commands available are:
++into /usr/local/bin. The mains commands available are:
+
+ mkfs.btrfs: create a filesystem
+
+-btrfsctl: control program to create snapshots and subvolumes:
+-
++btrfs: control program to create snapshots and subvolumes:
++ # mount a btrfs filesystem
+ mount /dev/sda2 /mnt
+- btrfsctl -s new_subvol_name /mnt
+- btrfsctl -s snapshot_of_default /mnt/default
+- btrfsctl -s snapshot_of_new_subvol /mnt/new_subvol_name
+- btrfsctl -s snapshot_of_a_snapshot /mnt/snapshot_of_new_subvol
++
++ # create a subvolume
++ btrfs subvolume create /mnt/new_subvol_name
++
++ # snapshot of a subvolume
++ btrfs subvolume snapshot /mnt/default /mnt/snapshot_of_default
++ btrfs subvolume snapshot /mnt/snapshot_of_default \
++ /mnt/snapshot_of_a_snapshot
++
++ # list of the subvolumes
+ ls /mnt
+ default snapshot_of_a_snapshot snapshot_of_new_subvol
+ new_subvol_name snapshot_of_default
+
+- Snapshots and subvolumes cannot be deleted right now, but you can
+- rm -rf all the files and directories inside them.
++ # removal of a subvolume or a snapshot
++ btrfs subvolume delete /mn/snapshot_of_a_snapshot
++
++ # look a the btrfs man page for further information
++ man btrfs
+
+ btrfsck: do a limited check of the FS extent trees.</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..6e6f6c6 100644
+index 8097b5a..79818e6 100644
--- a/Makefile
+++ b/Makefile
-@@ -4,7 +4,7 @@ CFLAGS = -g -Werror -Os
+@@ -1,28 +1,32 @@
+-CC=gcc
++CC = gcc
+ AM_CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2
+-CFLAGS = -g -Werror -Os
++CFLAGS = -g -O0
objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
root-tree.o dir-item.o file-item.o inode-item.o \
inode-map.o crc32c.o rbtree.o extent-cache.o extent_io.o \
- volumes.o utils.o
-+ volumes.o utils.o btrfs-list.o
++ volumes.o utils.o btrfs-list.o btrfslabel.o repair.o
++cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \
++ cmds-inspect.o cmds-balance.o
- #
- CHECKFLAGS=-D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \
-@@ -16,7 +16,9 @@ prefix ?= /usr/local
+-#
+-CHECKFLAGS=-D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \
+- -Wuninitialized -Wshadow -Wundef
++CHECKFLAGS= -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \
++ -Wuninitialized -Wshadow -Wundef
+ DEPFLAGS = -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@
+
+-INSTALL= install
++INSTALL = install
+ prefix ?= /usr/local
bindir = $(prefix)/bin
LIBS=-luuid
++RESTORE_LIBS=-lz
-progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck
+progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \
-+ btrfs \
-+ btrfs-map-logical
++ btrfs btrfs-map-logical btrfs-image btrfs-zero-log btrfs-convert \
++ btrfs-find-root btrfs-restore btrfstune
# make C=1 to enable sparse
ifdef C
-@@ -35,6 +37,10 @@ all: version $(progs) manpages
+- check=sparse $(CHECKFLAGS)
++ check = sparse $(CHECKFLAGS)
+ else
+- check=ls
++ check = ls
+ endif
+
+ .c.o:
+@@ -35,38 +39,66 @@ all: version $(progs) manpages
version:
bash version.sh
-+btrfs: $(objects) btrfs.o btrfs_cmds.o
-+ gcc $(CFLAGS) -o btrfs btrfs.o btrfs_cmds.o \
-+ $(objects) $(LDFLAGS) $(LIBS)
++btrfs: $(objects) btrfs.o help.o common.o $(cmds_objects)
++ $(CC) $(CFLAGS) -o btrfs btrfs.o help.o common.o $(cmds_objects) \
++ $(objects) $(LDFLAGS) $(LIBS) -lpthread
++
++calc-size: $(objects) calc-size.o
++ gcc $(CFLAGS) -o calc-size calc-size.o $(objects) $(LDFLAGS) $(LIBS)
++
++btrfs-find-root: $(objects) find-root.o
++ gcc $(CFLAGS) -o btrfs-find-root find-root.o $(objects) $(LDFLAGS) $(LIBS)
++
++btrfs-restore: $(objects) restore.o
++ gcc $(CFLAGS) -o btrfs-restore restore.o $(objects) $(LDFLAGS) $(LIBS) $(RESTORE_LIBS)
+
btrfsctl: $(objects) btrfsctl.o
- gcc $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS)
+- gcc $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS)
++ $(CC) $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS)
-@@ -53,9 +59,15 @@ mkfs.btrfs: $(objects) mkfs.o
- btrfs-debug-tree: $(objects) debug-tree.o
- gcc $(CFLAGS) -o btrfs-debug-tree $(objects) debug-tree.o $(LDFLAGS) $(LIBS)
+ btrfs-vol: $(objects) btrfs-vol.o
+- gcc $(CFLAGS) -o btrfs-vol btrfs-vol.o $(objects) $(LDFLAGS) $(LIBS)
++ $(CC) $(CFLAGS) -o btrfs-vol btrfs-vol.o $(objects) $(LDFLAGS) $(LIBS)
+ btrfs-show: $(objects) btrfs-show.o
+- gcc $(CFLAGS) -o btrfs-show btrfs-show.o $(objects) $(LDFLAGS) $(LIBS)
++ $(CC) $(CFLAGS) -o btrfs-show btrfs-show.o $(objects) $(LDFLAGS) $(LIBS)
+
+ btrfsck: $(objects) btrfsck.o
+- gcc $(CFLAGS) -o btrfsck btrfsck.o $(objects) $(LDFLAGS) $(LIBS)
++ $(CC) $(CFLAGS) -o btrfsck btrfsck.o $(objects) $(LDFLAGS) $(LIBS)
+
+ mkfs.btrfs: $(objects) mkfs.o
+- gcc $(CFLAGS) -o mkfs.btrfs $(objects) mkfs.o $(LDFLAGS) $(LIBS)
++ $(CC) $(CFLAGS) -o mkfs.btrfs $(objects) mkfs.o $(LDFLAGS) $(LIBS)
+
+ btrfs-debug-tree: $(objects) debug-tree.o
+- gcc $(CFLAGS) -o btrfs-debug-tree $(objects) debug-tree.o $(LDFLAGS) $(LIBS)
++ $(CC) $(CFLAGS) -o btrfs-debug-tree $(objects) debug-tree.o $(LDFLAGS) $(LIBS)
++
+btrfs-zero-log: $(objects) btrfs-zero-log.o
-+ gcc $(CFLAGS) -o btrfs-zero-log $(objects) btrfs-zero-log.o $(LDFLAGS) $(LIBS)
++ $(CC) $(CFLAGS) -o btrfs-zero-log $(objects) btrfs-zero-log.o $(LDFLAGS) $(LIBS)
+
- btrfstune: $(objects) btrfstune.o
- gcc $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS)
++btrfs-select-super: $(objects) btrfs-select-super.o
++ $(CC) $(CFLAGS) -o btrfs-select-super $(objects) btrfs-select-super.o $(LDFLAGS) $(LIBS)
+ btrfstune: $(objects) btrfstune.o
+- gcc $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS)
++ $(CC) $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS)
++
+btrfs-map-logical: $(objects) btrfs-map-logical.o
-+ gcc $(CFLAGS) -o btrfs-map-logical $(objects) btrfs-map-logical.o $(LDFLAGS) $(LIBS)
++ $(CC) $(CFLAGS) -o btrfs-map-logical $(objects) btrfs-map-logical.o $(LDFLAGS) $(LIBS)
+
++btrfs-corrupt-block: $(objects) btrfs-corrupt-block.o
++ $(CC) $(CFLAGS) -o btrfs-corrupt-block $(objects) btrfs-corrupt-block.o $(LDFLAGS) $(LIBS)
+
btrfs-image: $(objects) btrfs-image.o
- gcc $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS)
+- gcc $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS)
++ $(CC) $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS)
-@@ -66,7 +78,10 @@ quick-test: $(objects) quick-test.o
- gcc $(CFLAGS) -o quick-test $(objects) quick-test.o $(LDFLAGS) $(LIBS)
+ dir-test: $(objects) dir-test.o
+- gcc $(CFLAGS) -o dir-test $(objects) dir-test.o $(LDFLAGS) $(LIBS)
++ $(CC) $(CFLAGS) -o dir-test $(objects) dir-test.o $(LDFLAGS) $(LIBS)
- convert: $(objects) convert.o
-- gcc $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs $(LDFLAGS) $(LIBS)
-+ gcc $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs -lcom_err $(LDFLAGS) $(LIBS)
+ quick-test: $(objects) quick-test.o
+- gcc $(CFLAGS) -o quick-test $(objects) quick-test.o $(LDFLAGS) $(LIBS)
++ $(CC) $(CFLAGS) -o quick-test $(objects) quick-test.o $(LDFLAGS) $(LIBS)
+
++btrfs-convert: $(objects) convert.o
++ $(CC) $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs -lcom_err $(LDFLAGS) $(LIBS)
+
+-convert: $(objects) convert.o
+- gcc $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs $(LDFLAGS) $(LIBS)
+ioctl-test: $(objects) ioctl-test.o
-+ gcc $(CFLAGS) -o ioctl-test $(objects) ioctl-test.o $(LDFLAGS) $(LIBS)
++ $(CC) $(CFLAGS) -o ioctl-test $(objects) ioctl-test.o $(LDFLAGS) $(LIBS)
manpages:
cd man; make
-diff --git a/btrfs-defrag.c b/btrfs-defrag.c
+@@ -75,12 +107,12 @@ install-man:
+ cd man; make install
+
+ clean :
+- rm -f $(progs) cscope.out *.o .*.d btrfs-convert
++ rm -f $(progs) cscope.out *.o .*.d btrfs-convert btrfs-image btrfs-select-super \
++ btrfs-zero-log btrfstune dir-test ioctl-test quick-test version.h
+ cd man; make clean
+
+ install: $(progs) install-man
+ $(INSTALL) -m755 -d $(DESTDIR)$(bindir)
+ $(INSTALL) $(progs) $(DESTDIR)$(bindir)
+- if [ -e btrfs-convert ]; then $(INSTALL) btrfs-convert $(DESTDIR)$(bindir); fi
+
+ -include .*.d
+diff --git a/bcp b/bcp
+index 5729e91..e7ca641 100755
+--- a/bcp
++++ b/bcp
+@@ -136,8 +136,7 @@ for srci in xrange(0, src_args):
+ srcname = os.path.join(dirpath, x)
+ statinfo = os.lstat(srcname)
+
+- if srcname.startswith(src):
+- part = srcname[len(src) + 1:]
++ part = os.path.relpath(srcname, src)
+
+ if stat.S_ISLNK(statinfo.st_mode):
+ copylink(srcname, dst, part, statinfo, None)
+@@ -152,8 +151,7 @@ for srci in xrange(0, src_args):
+
+ for f in filenames:
+ srcname = os.path.join(dirpath, f)
+- if srcname.startswith(src):
+- part = srcname[len(src) + 1:]
++ part = os.path.relpath(srcname, src)
+
+ statinfo = os.lstat(srcname)
+ copyfile(srcname, dst, part, statinfo, None)
+diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c
new file mode 100644
-index 0000000..8f1525a
+index 0000000..7051e99
--- /dev/null
-+++ b/btrfs-defrag.c
-@@ -0,0 +1,39 @@
++++ b/btrfs-corrupt-block.c
+@@ -0,0 +1,396 @@
+/*
-+ * Copyright (C) 2010 Oracle. All rights reserved.
++ * Copyright (C) 2009 Oracle. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
@@ -85,1601 +262,1380 @@ index 0000000..8f1525a
+ * Boston, MA 021110-1307, USA.
+ */
+
-+#ifndef __CHECKER__
-+#include <sys/ioctl.h>
-+#include <sys/mount.h>
-+#include "ioctl.h"
-+#endif
++#define _XOPEN_SOURCE 500
++#define _GNU_SOURCE 1
+#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 "volumes.h"
++#include "disk-io.h"
++#include "print-tree.h"
+#include "transaction.h"
-+#include "utils.h"
++#include "list.h"
+#include "version.h"
+
-diff --git a/btrfs-list.c b/btrfs-list.c
-new file mode 100644
-index 0000000..93766a8
---- /dev/null
-+++ b/btrfs-list.c
-@@ -0,0 +1,835 @@
-+/*
-+ * Copyright (C) 2010 Oracle. All rights reserved.
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public
-+ * License v2 as published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-+ * General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public
-+ * License along with this program; if not, write to the
-+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-+ * Boston, MA 021110-1307, USA.
-+ */
-+
-+#define _GNU_SOURCE
-+#ifndef __CHECKER__
-+#include <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"
-+#include "version.h"
++struct extent_buffer *debug_corrupt_block(struct btrfs_root *root, u64 bytenr,
++ u32 blocksize, int copy)
++{
++ int ret;
++ struct extent_buffer *eb;
++ u64 length;
++ struct btrfs_multi_bio *multi = NULL;
++ struct btrfs_device *device;
++ int num_copies;
++ int mirror_num = 1;
+
-+/* we store all the roots we find in an rbtree so that we can
-+ * search for them later.
-+ */
-+struct root_lookup {
-+ struct rb_root root;
-+};
++ eb = btrfs_find_create_tree_block(root, bytenr, blocksize);
++ if (!eb)
++ return NULL;
+
-+/*
-+ * one of these for each root we find.
-+ */
-+struct root_info {
-+ struct rb_node rb_node;
++ length = blocksize;
++ while (1) {
++ ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
++ eb->start, &length, &multi, mirror_num);
++ BUG_ON(ret);
++ device = multi->stripes[0].dev;
++ eb->fd = device->fd;
++ device->total_ios++;
++ eb->dev_bytenr = multi->stripes[0].physical;
+
-+ /* this root's id */
-+ u64 root_id;
++ fprintf(stdout, "mirror %d logical %Lu physical %Lu "
++ "device %s\n", mirror_num, (unsigned long long)bytenr,
++ (unsigned long long)eb->dev_bytenr, device->name);
++ kfree(multi);
+
-+ /* the id of the root that references this one */
-+ u64 ref_tree;
++ if (!copy || mirror_num == copy) {
++ ret = read_extent_from_disk(eb);
++ printf("corrupting %llu copy %d\n", eb->start,
++ mirror_num);
++ memset(eb->data, 0, eb->len);
++ write_extent_to_disk(eb);
++ fsync(eb->fd);
++ }
+
-+ /* the dir id we're in from ref_tree */
-+ u64 dir_id;
++ num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
++ eb->start, eb->len);
++ if (num_copies == 1)
++ break;
+
-+ /* path from the subvol we live in to this root, including the
-+ * root's name. This is null until we do the extra lookup ioctl.
-+ */
-+ char *path;
++ mirror_num++;
++ if (mirror_num > num_copies)
++ break;
++ }
++ return eb;
++}
+
-+ /* the name of this root in the directory it lives in */
-+ char name[];
-+};
++static void print_usage(void)
++{
++ fprintf(stderr, "usage: btrfs-map-logical [options] mount_point\n");
++ fprintf(stderr, "\t-l Logical extent to map\n");
++ fprintf(stderr, "\t-c Copy of the extent to read (usually 1 or 2)\n");
++ fprintf(stderr, "\t-o Output file to hold the extent\n");
++ fprintf(stderr, "\t-b Number of bytes to read\n");
++ exit(1);
++}
+
-+static void root_lookup_init(struct root_lookup *tree)
++static void corrupt_keys(struct btrfs_trans_handle *trans,
++ struct btrfs_root *root,
++ struct extent_buffer *eb)
+{
-+ tree->root.rb_node = NULL;
++ int slot;
++ int bad_slot;
++ int nr;
++ struct btrfs_disk_key bad_key;;
++
++ nr = btrfs_header_nritems(eb);
++ if (nr == 0)
++ return;
++
++ slot = rand() % nr;
++ bad_slot = rand() % nr;
++
++ if (bad_slot == slot)
++ return;
++
++ fprintf(stderr, "corrupting keys in block %llu slot %d swapping with %d\n",
++ (unsigned long long)eb->start, slot, bad_slot);
++
++ if (btrfs_header_level(eb) == 0) {
++ btrfs_item_key(eb, &bad_key, bad_slot);
++ btrfs_set_item_key(eb, &bad_key, slot);
++ } else {
++ btrfs_node_key(eb, &bad_key, bad_slot);
++ btrfs_set_node_key(eb, &bad_key, slot);
++ }
++ btrfs_mark_buffer_dirty(eb);
++ if (!trans) {
++ csum_tree_block(root, eb, 0);
++ write_extent_to_disk(eb);
++ }
+}
+
-+static int comp_entry(struct root_info *entry, u64 root_id, u64 ref_tree)
++
++static int corrupt_keys_in_block(struct btrfs_root *root, u64 bytenr)
+{
-+ if (entry->root_id > root_id)
-+ return 1;
-+ if (entry->root_id < root_id)
-+ return -1;
-+ if (entry->ref_tree > ref_tree)
-+ return 1;
-+ if (entry->ref_tree < ref_tree)
-+ return -1;
++ struct extent_buffer *eb;
++
++ eb = read_tree_block(root, bytenr, root->leafsize, 0);
++ if (!eb)
++ return -EIO;;
++
++ corrupt_keys(NULL, root, eb);
++ free_extent_buffer(eb);
+ return 0;
+}
+
-+/*
-+ * insert a new root into the tree. returns the existing root entry
-+ * if one is already there. Both root_id and ref_tree are used
-+ * as the key
-+ */
-+static struct rb_node *tree_insert(struct rb_root *root, u64 root_id,
-+ u64 ref_tree, struct rb_node *node)
++static int corrupt_extent(struct btrfs_trans_handle *trans,
++ struct btrfs_root *root, u64 bytenr, int copy)
+{
-+ struct rb_node ** p = &root->rb_node;
-+ struct rb_node * parent = NULL;
-+ struct root_info *entry;
-+ int comp;
++ struct btrfs_key key;
++ struct extent_buffer *leaf;
++ u32 item_size;
++ unsigned long ptr;
++ struct btrfs_path *path;
++ int ret;
++ int slot;
++ int should_del = rand() % 3;
+
-+ while(*p) {
-+ parent = *p;
-+ entry = rb_entry(parent, struct root_info, rb_node);
++ path = btrfs_alloc_path();
+
-+ comp = comp_entry(entry, root_id, ref_tree);
++ key.objectid = bytenr;
++ key.type = (u8)-1;
++ key.offset = (u64)-1;
+
-+ if (comp < 0)
-+ p = &(*p)->rb_left;
-+ else if (comp > 0)
-+ p = &(*p)->rb_right;
-+ else
-+ return parent;
++ while(1) {
++ ret = btrfs_search_slot(trans, root->fs_info->extent_root,
++ &key, path, -1, 1);
++ if (ret < 0)
++ break;
++
++ if (ret > 0) {
++ if (path->slots[0] == 0)
++ break;
++ path->slots[0]--;
++ ret = 0;
++ }
++ leaf = path->nodes[0];
++ slot = path->slots[0];
++ btrfs_item_key_to_cpu(leaf, &key, slot);
++ if (key.objectid != bytenr)
++ break;
++
++ if (key.type != BTRFS_EXTENT_ITEM_KEY &&
++ key.type != BTRFS_TREE_BLOCK_REF_KEY &&
++ key.type != BTRFS_EXTENT_DATA_REF_KEY &&
++ key.type != BTRFS_EXTENT_REF_V0_KEY &&
++ key.type != BTRFS_SHARED_BLOCK_REF_KEY &&
++ key.type != BTRFS_SHARED_DATA_REF_KEY)
++ goto next;
++
++ if (should_del) {
++ fprintf(stderr, "deleting extent record: key %Lu %u %Lu\n",
++ key.objectid, key.type, key.offset);
++
++ if (key.type == BTRFS_EXTENT_ITEM_KEY) {
++ /* make sure this extent doesn't get
++ * reused for other purposes */
++ btrfs_pin_extent(root->fs_info,
++ key.objectid, key.offset);
++ }
++
++ btrfs_del_item(trans, root, path);
++ } else {
++ fprintf(stderr, "corrupting extent record: key %Lu %u %Lu\n",
++ key.objectid, key.type, key.offset);
++ ptr = btrfs_item_ptr_offset(leaf, slot);
++ item_size = btrfs_item_size_nr(leaf, slot);
++ memset_extent_buffer(leaf, 0, ptr, item_size);
++ btrfs_mark_buffer_dirty(leaf);
++ }
++next:
++ btrfs_release_path(NULL, path);
++
++ if (key.offset > 0)
++ key.offset--;
++ if (key.offset == 0)
++ break;
+ }
+
-+ entry = rb_entry(parent, struct root_info, rb_node);
-+ rb_link_node(node, parent, p);
-+ rb_insert_color(node, root);
-+ return NULL;
++ btrfs_free_path(path);
++ return 0;
+}
+
-+/*
-+ * find a given root id in the tree. We return the smallest one,
-+ * rb_next can be used to move forward looking for more if required
-+ */
-+static struct root_info *tree_search(struct rb_root *root, u64 root_id)
++static void btrfs_corrupt_extent_leaf(struct btrfs_trans_handle *trans,
++ struct btrfs_root *root, struct extent_buffer *eb)
+{
-+ struct rb_node * n = root->rb_node;
-+ struct root_info *entry;
++ u32 nr = btrfs_header_nritems(eb);
++ u32 victim = rand() % nr;
++ u64 objectid;
++ struct btrfs_key key;
+
-+ while(n) {
-+ entry = rb_entry(n, struct root_info, rb_node);
++ btrfs_item_key_to_cpu(eb, &key, victim);
++ objectid = key.objectid;
++ corrupt_extent(trans, root, objectid, 1);
++}
+
-+ if (entry->root_id < root_id)
-+ n = n->rb_left;
-+ else if (entry->root_id > root_id)
-+ n = n->rb_right;
-+ else {
-+ struct root_info *prev;
-+ struct rb_node *prev_n;
-+ while (1) {
-+ prev_n = rb_prev(n);
-+ if (!prev_n)
-+ break;
-+ prev = rb_entry(prev_n, struct root_info,
-+ rb_node);
-+ if (prev->root_id != root_id)
-+ break;
-+ entry = prev;
-+ n = prev_n;
-+ }
-+ return entry;
-+ }
++static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans,
++ struct btrfs_root *root, struct extent_buffer *eb)
++{
++ int i;
++ u32 nr;
++
++ if (!eb)
++ return;
++
++ nr = btrfs_header_nritems(eb);
++ if (btrfs_is_leaf(eb)) {
++ btrfs_corrupt_extent_leaf(trans, root, eb);
++ return;
+ }
-+ return NULL;
-+}
+
-+/*
-+ * this allocates a new root in the lookup tree.
-+ *
-+ * root_id should be the object id of the root
-+ *
-+ * ref_tree is the objectid of the referring root.
-+ *
-+ * dir_id is the directory in ref_tree where this root_id can be found.
-+ *
-+ * name is the name of root_id in that directory
-+ *
-+ * name_len is the length of name
-+ */
-+static int add_root(struct root_lookup *root_lookup,
-+ u64 root_id, u64 ref_tree, u64 dir_id, char *name,
-+ int name_len)
-+{
-+ struct root_info *ri;
-+ struct rb_node *ret;
-+ ri = malloc(sizeof(*ri) + name_len + 1);
-+ if (!ri) {
-+ printf("memory allocation failed\n");
-+ exit(1);
++ if (btrfs_header_level(eb) == 1 && eb != root->node) {
++ if (rand() % 5)
++ return;
+ }
-+ memset(ri, 0, sizeof(*ri) + name_len + 1);
-+ ri->path = NULL;
-+ ri->dir_id = dir_id;
-+ ri->root_id = root_id;
-+ ri->ref_tree = ref_tree;
-+ strncpy(ri->name, name, name_len);
+
-+ ret = tree_insert(&root_lookup->root, root_id, ref_tree, &ri->rb_node);
-+ if (ret) {
-+ printf("failed to insert tree %llu\n", (unsigned long long)root_id);
-+ exit(1);
++ for (i = 0; i < nr; i++) {
++ struct extent_buffer *next;
++
++ next = read_tree_block(root, btrfs_node_blockptr(eb, i),
++ root->leafsize, btrfs_node_ptr_generation(eb, i));
++ if (!next)
++ continue;
++ btrfs_corrupt_extent_tree(trans, root, next);
++ free_extent_buffer(next);
+ }
-+ return 0;
+}
+
-+/*
-+ * for a given root_info, search through the root_lookup tree to construct
-+ * the full path name to it.
-+ *
-+ * This can't be called until all the root_info->path fields are filled
-+ * in by lookup_ino_path
-+ */
-+static int resolve_root(struct root_lookup *rl, struct root_info *ri)
-+{
-+ u64 top_id;
-+ char *full_path = NULL;
-+ int len = 0;
-+ struct root_info *found;
++static struct option long_options[] = {
++ /* { "byte-count", 1, NULL, 'b' }, */
++ { "logical", 1, NULL, 'l' },
++ { "copy", 1, NULL, 'c' },
++ { "bytes", 1, NULL, 'b' },
++ { "extent-record", 0, NULL, 'e' },
++ { "extent-tree", 0, NULL, 'E' },
++ { "keys", 0, NULL, 'k' },
++ { 0, 0, 0, 0}
++};
+
-+ /*
-+ * we go backwards from the root_info object and add pathnames
-+ * from parent directories as we go.
-+ */
-+ found = ri;
-+ while (1) {
-+ char *tmp;
-+ u64 next;
-+ int add_len = strlen(found->path);
+
-+ /* room for / and for null */
-+ tmp = malloc(add_len + 2 + len);
-+ if (full_path) {
-+ memcpy(tmp + add_len + 1, full_path, len);
-+ tmp[add_len] = '/';
-+ memcpy(tmp, found->path, add_len);
-+ tmp [add_len + len + 1] = '\0';
-+ free(full_path);
-+ full_path = tmp;
-+ len += add_len + 1;
-+ } else {
-+ full_path = strdup(found->path);
-+ len = add_len;
-+ }
++int main(int ac, char **av)
++{
++ struct cache_tree root_cache;
++ struct btrfs_root *root;
++ struct extent_buffer *eb;
++ char *dev;
++ u64 logical = 0;
++ int ret = 0;
++ int option_index = 0;
++ int copy = 0;
++ u64 bytes = 4096;
++ int extent_rec = 0;
++ int extent_tree = 0;
++ int corrupt_block_keys = 0;
+
-+ next = found->ref_tree;
-+ /* if the ref_tree refers to ourselves, we're at the top */
-+ if (next == found->root_id) {
-+ top_id = next;
-+ break;
-+ }
++ srand(128);
+
-+ /*
-+ * if the ref_tree wasn't in our tree of roots, we're
-+ * at the top
-+ */
-+ found = tree_search(&rl->root, next);
-+ if (!found) {
-+ top_id = next;
++ while(1) {
++ int c;
++ c = getopt_long(ac, av, "l:c:eEk", long_options,
++ &option_index);
++ if (c < 0)
+ break;
++ switch(c) {
++ case 'l':
++ logical = atoll(optarg);
++ if (logical == 0) {
++ fprintf(stderr,
++ "invalid extent number\n");
++ print_usage();
++ }
++ break;
++ case 'c':
++ copy = atoi(optarg);
++ if (copy == 0) {
++ fprintf(stderr,
++ "invalid copy number\n");
++ print_usage();
++ }
++ break;
++ case 'b':
++ bytes = atoll(optarg);
++ if (bytes == 0) {
++ fprintf(stderr,
++ "invalid byte count\n");
++ print_usage();
++ }
++ break;
++ case 'e':
++ extent_rec = 1;
++ break;
++ case 'E':
++ extent_tree = 1;
++ break;
++ case 'k':
++ corrupt_block_keys = 1;
++ break;
++ default:
++ print_usage();
+ }
+ }
-+ printf("ID %llu top level %llu path %s\n", ri->root_id, top_id,
-+ full_path);
-+ free(full_path);
-+ return 0;
-+}
-+
-+/*
-+ * for a single root_info, ask the kernel to give us a path name
-+ * inside it's ref_root for the dir_id where it lives.
-+ *
-+ * This fills in root_info->path with the path to the directory and and
-+ * appends this root's name.
-+ */
-+static int lookup_ino_path(int fd, struct root_info *ri)
-+{
-+ struct btrfs_ioctl_ino_lookup_args args;
-+ int ret;
++ ac = ac - optind;
++ if (ac == 0)
++ print_usage();
++ if (logical == 0 && !extent_tree)
++ print_usage();
++ if (copy < 0)
++ print_usage();
+
-+ if (ri->path)
-+ return 0;
++ dev = av[optind];
+
-+ memset(&args, 0, sizeof(args));
-+ args.treeid = ri->ref_tree;
-+ args.objectid = ri->dir_id;
++ radix_tree_init();
++ cache_tree_init(&root_cache);
+
-+ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
-+ if (ret) {
-+ fprintf(stderr, "ERROR: Failed to lookup path for root %llu\n",
-+ (unsigned long long)ri->ref_tree);
-+ return ret;
++ root = open_ctree(dev, 0, 1);
++ if (!root) {
++ fprintf(stderr, "Open ctree failed\n");
++ exit(1);
++ }
++ if (extent_rec) {
++ struct btrfs_trans_handle *trans;
++ trans = btrfs_start_transaction(root, 1);
++ ret = corrupt_extent (trans, root, logical, 0);
++ btrfs_commit_transaction(trans, root);
++ goto out_close;
++ }
++ if (extent_tree) {
++ struct btrfs_trans_handle *trans;
++ trans = btrfs_start_transaction(root, 1);
++ btrfs_corrupt_extent_tree(trans, root->fs_info->extent_root,
++ root->fs_info->extent_root->node);
++ btrfs_commit_transaction(trans, root);
++ goto out_close;
+ }
+
-+ if (args.name[0]) {
-+ /*
-+ * we're in a subdirectory of ref_tree, the kernel ioctl
-+ * puts a / in there for us
-+ */
-+ ri->path = malloc(strlen(ri->name) + strlen(args.name) + 1);
-+ if (!ri->path) {
-+ perror("malloc failed");
-+ exit(1);
-+ }
-+ strcpy(ri->path, args.name);
-+ strcat(ri->path, ri->name);
-+ } else {
-+ /* we're at the root of ref_tree */
-+ ri->path = strdup(ri->name);
-+ if (!ri->path) {
-+ perror("strdup failed");
-+ exit(1);
++ if (bytes == 0)
++ bytes = root->sectorsize;
++
++ bytes = (bytes + root->sectorsize - 1) / root->sectorsize;
++ bytes *= root->sectorsize;
++
++ while (bytes > 0) {
++ if (corrupt_block_keys) {
++ corrupt_keys_in_block(root, logical);
++ } else {
++ eb = debug_corrupt_block(root, logical,
++ root->sectorsize, copy);
++ free_extent_buffer(eb);
+ }
++ logical += root->sectorsize;
++ bytes -= root->sectorsize;
+ }
-+ return 0;
++ return ret;
++out_close:
++ close_ctree(root);
++ return ret;
+}
-+
-+/* finding the generation for a given path is a two step process.
-+ * First we use the inode loookup routine to find out the root id
+diff --git a/btrfs-defrag.c b/btrfs-defrag.c
+new file mode 100644
+index 0000000..8f1525a
+--- /dev/null
++++ b/btrfs-defrag.c
+@@ -0,0 +1,39 @@
++/*
++ * Copyright (C) 2010 Oracle. All rights reserved.
+ *
-+ * Then we use the tree search ioctl to scan all the root items for a
-+ * given root id and spit out the latest generation we can find
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public
++ * License v2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public
++ * License along with this program; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
++ * Boston, MA 021110-1307, USA.
+ */
-+static u64 find_root_gen(int fd)
-+{
-+ struct btrfs_ioctl_ino_lookup_args ino_args;
-+ int ret;
-+ struct btrfs_ioctl_search_args args;
-+ struct btrfs_ioctl_search_key *sk = &args.key;
-+ struct btrfs_ioctl_search_header *sh;
-+ unsigned long off = 0;
-+ u64 max_found = 0;
-+ int i;
+
-+ memset(&ino_args, 0, sizeof(ino_args));
-+ ino_args.objectid = BTRFS_FIRST_FREE_OBJECTID;
++#ifndef __CHECKER__
++#include <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"
+
-+ /* this ioctl fills in ino_args->treeid */
-+ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args);
-+ if (ret) {
-+ fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu\n",
-+ (unsigned long long)BTRFS_FIRST_FREE_OBJECTID);
-+ return 0;
-+ }
+diff --git a/btrfs-list.c b/btrfs-list.c
+new file mode 100644
+index 0000000..5f4a9be
+--- /dev/null
++++ b/btrfs-list.c
+@@ -0,0 +1,936 @@
++/*
++ * Copyright (C) 2010 Oracle. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public
++ * License v2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public
++ * License along with this program; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
++ * Boston, MA 021110-1307, USA.
++ */
+
-+ memset(&args, 0, sizeof(args));
++#define _GNU_SOURCE
++#ifndef __CHECKER__
++#include <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"
+
-+ sk->tree_id = 1;
++/* we store all the roots we find in an rbtree so that we can
++ * search for them later.
++ */
++struct root_lookup {
++ struct rb_root root;
++};
+
-+ /*
-+ * there may be more than one ROOT_ITEM key if there are
-+ * snapshots pending deletion, we have to loop through
-+ * them.
-+ */
-+ sk->min_objectid = ino_args.treeid;
-+ sk->max_objectid = ino_args.treeid;
-+ sk->max_type = BTRFS_ROOT_ITEM_KEY;
-+ sk->min_type = BTRFS_ROOT_ITEM_KEY;
-+ sk->max_offset = (u64)-1;
-+ sk->max_transid = (u64)-1;
-+ sk->nr_items = 4096;
++/*
++ * one of these for each root we find.
++ */
++struct root_info {
++ struct rb_node rb_node;
+
-+ while (1) {
-+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
-+ if (ret < 0) {
-+ fprintf(stderr, "ERROR: can't perform the search\n");
-+ return 0;
-+ }
-+ /* the ioctl returns the number of item it found in nr_items */
-+ if (sk->nr_items == 0)
-+ break;
++ /* this root's id */
++ u64 root_id;
+
-+ off = 0;
-+ for (i = 0; i < sk->nr_items; i++) {
-+ struct btrfs_root_item *item;
-+ sh = (struct btrfs_ioctl_search_header *)(args.buf +
-+ off);
++ /* the id of the root that references this one */
++ u64 ref_tree;
+
-+ off += sizeof(*sh);
-+ item = (struct btrfs_root_item *)(args.buf + off);
-+ off += sh->len;
++ /* the dir id we're in from ref_tree */
++ u64 dir_id;
+
-+ sk->min_objectid = sh->objectid;
-+ sk->min_type = sh->type;
-+ sk->min_offset = sh->offset;
++ /* path from the subvol we live in to this root, including the
++ * root's name. This is null until we do the extra lookup ioctl.
++ */
++ char *path;
+
-+ if (sh->objectid > ino_args.treeid)
-+ break;
++ /* the name of this root in the directory it lives in */
++ char name[];
++};
+
-+ if (sh->objectid == ino_args.treeid &&
-+ sh->type == BTRFS_ROOT_ITEM_KEY) {
-+ max_found = max(max_found,
-+ btrfs_root_generation(item));
-+ }
-+ }
-+ if (sk->min_offset < (u64)-1)
-+ sk->min_offset++;
-+ else
-+ break;
++static void root_lookup_init(struct root_lookup *tree)
++{
++ tree->root.rb_node = NULL;
++}
+
-+ if (sk->min_type != BTRFS_ROOT_ITEM_KEY)
-+ break;
-+ if (sk->min_objectid != BTRFS_ROOT_ITEM_KEY)
-+ break;
-+ }
-+ return max_found;
++static int comp_entry(struct root_info *entry, u64 root_id, u64 ref_tree)
++{
++ if (entry->root_id > root_id)
++ return 1;
++ if (entry->root_id < root_id)
++ return -1;
++ if (entry->ref_tree > ref_tree)
++ return 1;
++ if (entry->ref_tree < ref_tree)
++ return -1;
++ return 0;
+}
+
-+/* pass in a directory id and this will return
-+ * the full path of the parent directory inside its
-+ * subvolume root.
-+ *
-+ * It may return NULL if it is in the root, or an ERR_PTR if things
-+ * go badly.
++/*
++ * insert a new root into the tree. returns the existing root entry
++ * if one is already there. Both root_id and ref_tree are used
++ * as the key
+ */
-+static char *__ino_resolve(int fd, u64 dirid)
++static struct rb_node *tree_insert(struct rb_root *root, u64 root_id,
++ u64 ref_tree, struct rb_node *node)
+{
-+ struct btrfs_ioctl_ino_lookup_args args;
-+ int ret;
-+ char *full;
++ struct rb_node ** p = &root->rb_node;
++ struct rb_node * parent = NULL;
++ struct root_info *entry;
++ int comp;
+
-+ memset(&args, 0, sizeof(args));
-+ args.objectid = dirid;
++ while(*p) {
++ parent = *p;
++ entry = rb_entry(parent, struct root_info, rb_node);
+
-+ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
-+ if (ret) {
-+ fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu\n",
-+ (unsigned long long)dirid);
-+ return ERR_PTR(ret);
-+ }
++ comp = comp_entry(entry, root_id, ref_tree);
+
-+ if (args.name[0]) {
-+ /*
-+ * we're in a subdirectory of ref_tree, the kernel ioctl
-+ * puts a / in there for us
-+ */
-+ full = strdup(args.name);
-+ if (!full) {
-+ perror("malloc failed");
-+ return ERR_PTR(-ENOMEM);
-+ }
-+ } else {
-+ /* we're at the root of ref_tree */
-+ full = NULL;
++ if (comp < 0)
++ p = &(*p)->rb_left;
++ else if (comp > 0)
++ p = &(*p)->rb_right;
++ else
++ return parent;
+ }
-+ return full;
++
++ entry = rb_entry(parent, struct root_info, rb_node);
++ rb_link_node(node, parent, p);
++ rb_insert_color(node, root);
++ return NULL;
+}
+
+/*
-+ * simple string builder, returning a new string with both
-+ * dirid and name
++ * find a given root id in the tree. We return the smallest one,
++ * rb_next can be used to move forward looking for more if required
+ */
-+char *build_name(char *dirid, char *name)
++static struct root_info *tree_search(struct rb_root *root, u64 root_id)
+{
-+ char *full;
-+ if (!dirid)
-+ return strdup(name);
++ struct rb_node * n = root->rb_node;
++ struct root_info *entry;
+
-+ full = malloc(strlen(dirid) + strlen(name) + 1);
-+ if (!full)
-+ return NULL;
-+ strcpy(full, dirid);
-+ strcat(full, name);
-+ return full;
++ while(n) {
++ entry = rb_entry(n, struct root_info, rb_node);
++
++ if (entry->root_id < root_id)
++ n = n->rb_left;
++ else if (entry->root_id > root_id)
++ n = n->rb_right;
++ else {
++ struct root_info *prev;
++ struct rb_node *prev_n;
++ while (1) {
++ prev_n = rb_prev(n);
++ if (!prev_n)
++ break;
++ prev = rb_entry(prev_n, struct root_info,
++ rb_node);
++ if (prev->root_id != root_id)
++ break;
++ entry = prev;
++ n = prev_n;
++ }
++ return entry;
++ }
++ }
++ return NULL;
+}
+
+/*
-+ * given an inode number, this returns the full path name inside the subvolume
-+ * to that file/directory. cache_dirid and cache_name are used to
-+ * cache the results so we can avoid tree searches if a later call goes
-+ * to the same directory or file name
++ * this allocates a new root in the lookup tree.
++ *
++ * root_id should be the object id of the root
++ *
++ * ref_tree is the objectid of the referring root.
++ *
++ * dir_id is the directory in ref_tree where this root_id can be found.
++ *
++ * name is the name of root_id in that directory
++ *
++ * name_len is the length of name
+ */
-+static char *ino_resolve(int fd, u64 ino, u64 *cache_dirid, char **cache_name)
-+
++static int add_root(struct root_lookup *root_lookup,
++ u64 root_id, u64 ref_tree, u64 dir_id, char *name,
++ int name_len)
+{
-+ u64 dirid;
-+ char *dirname;
-+ char *name;
-+ char *full;
-+ int ret;
-+ struct btrfs_ioctl_search_args args;
-+ struct btrfs_ioctl_search_key *sk = &args.key;
-+ struct btrfs_ioctl_search_header *sh;
-+ unsigned long off = 0;
-+ int namelen;
++ struct root_info *ri;
++ struct rb_node *ret;
++ ri = malloc(sizeof(*ri) + name_len + 1);
++ if (!ri) {
++ printf("memory allocation failed\n");
++ exit(1);
++ }
++ memset(ri, 0, sizeof(*ri) + name_len + 1);
++ ri->path = NULL;
++ ri->dir_id = dir_id;
++ ri->root_id = root_id;
++ ri->ref_tree = ref_tree;
++ strncpy(ri->name, name, name_len);
+
-+ memset(&args, 0, sizeof(args));
++ ret = tree_insert(&root_lookup->root, root_id, ref_tree, &ri->rb_node);
++ if (ret) {
++ printf("failed to insert tree %llu\n", (unsigned long long)root_id);
++ exit(1);
++ }
++ return 0;
++}
+
-+ sk->tree_id = 0;
++/*
++ * for a given root_info, search through the root_lookup tree to construct
++ * the full path name to it.
++ *
++ * This can't be called until all the root_info->path fields are filled
++ * in by lookup_ino_path
++ */
++static int resolve_root(struct root_lookup *rl, struct root_info *ri,
++ u64 *root_id, u64 *parent_id, u64 *top_id, char **path)
++{
++ char *full_path = NULL;
++ int len = 0;
++ struct root_info *found;
+
+ /*
-+ * step one, we search for the inode back ref. We just use the first
-+ * one
++ * we go backwards from the root_info object and add pathnames
++ * from parent directories as we go.
+ */
-+ sk->min_objectid = ino;
-+ sk->max_objectid = ino;
-+ sk->max_type = BTRFS_INODE_REF_KEY;
-+ sk->max_offset = (u64)-1;
-+ sk->min_type = BTRFS_INODE_REF_KEY;
-+ sk->max_transid = (u64)-1;
-+ sk->nr_items = 1;
++ *parent_id = 0;
++ found = ri;
++ while (1) {
++ char *tmp;
++ u64 next;
++ int add_len = strlen(found->path);
+
-+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
-+ if (ret < 0) {
-+ fprintf(stderr, "ERROR: can't perform the search\n");
-+ return NULL;
++ /* room for / and for null */
++ tmp = malloc(add_len + 2 + len);
++ if (full_path) {
++ memcpy(tmp + add_len + 1, full_path, len);
++ tmp[add_len] = '/';
++ memcpy(tmp, found->path, add_len);
++ tmp [add_len + len + 1] = '\0';
++ free(full_path);
++ full_path = tmp;
++ len += add_len + 1;
++ } else {
++ full_path = strdup(found->path);
++ len = add_len;
++ }
++
++ next = found->ref_tree;
++ /* record the first parent */
++ if (*parent_id == 0)
++ *parent_id = next;
++
++ /* if the ref_tree refers to ourselves, we're at the top */
++ if (next == found->root_id) {
++ *top_id = next;
++ break;
++ }
++
++ /*
++ * if the ref_tree wasn't in our tree of roots, we're
++ * at the top
++ */
++ found = tree_search(&rl->root, next);
++ if (!found) {
++ *top_id = next;
++ break;
++ }
+ }
-+ /* the ioctl returns the number of item it found in nr_items */
-+ if (sk->nr_items == 0)
-+ return NULL;
+
-+ off = 0;
-+ sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
++ *root_id = ri->root_id;
++ *path = full_path;
+
-+ if (sh->type == BTRFS_INODE_REF_KEY) {
-+ struct btrfs_inode_ref *ref;
-+ dirid = sh->offset;
++ return 0;
++}
+
-+ ref = (struct btrfs_inode_ref *)(sh + 1);
-+ namelen = btrfs_stack_inode_ref_name_len(ref);
++/*
++ * for a single root_info, ask the kernel to give us a path name
++ * inside it's ref_root for the dir_id where it lives.
++ *
++ * This fills in root_info->path with the path to the directory and and
++ * appends this root's name.
++ */
++static int lookup_ino_path(int fd, struct root_info *ri)
++{
++ struct btrfs_ioctl_ino_lookup_args args;
++ int ret, e;
+
-+ name = (char *)(ref + 1);
-+ name = strndup(name, namelen);
++ if (ri->path)
++ return 0;
+
-+ /* use our cached value */
-+ if (dirid == *cache_dirid && *cache_name) {
-+ dirname = *cache_name;
-+ goto build;
++ memset(&args, 0, sizeof(args));
++ args.treeid = ri->ref_tree;
++ args.objectid = ri->dir_id;
++
++ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
++ e = errno;
++ if (ret) {
++ fprintf(stderr, "ERROR: Failed to lookup path for root %llu - %s\n",
++ (unsigned long long)ri->ref_tree,
++ strerror(e));
++ return ret;
++ }
++
++ if (args.name[0]) {
++ /*
++ * we're in a subdirectory of ref_tree, the kernel ioctl
++ * puts a / in there for us
++ */
++ ri->path = malloc(strlen(ri->name) + strlen(args.name) + 1);
++ if (!ri->path) {
++ perror("malloc failed");
++ exit(1);
+ }
++ strcpy(ri->path, args.name);
++ strcat(ri->path, ri->name);
+ } else {
-+ return NULL;
++ /* we're at the root of ref_tree */
++ ri->path = strdup(ri->name);
++ if (!ri->path) {
++ perror("strdup failed");
++ exit(1);
++ }
+ }
-+ /*
-+ * the inode backref gives us the file name and the parent directory id.
-+ * From here we use __ino_resolve to get the path to the parent
-+ */
-+ dirname = __ino_resolve(fd, dirid);
-+build:
-+ full = build_name(dirname, name);
-+ if (*cache_name && dirname != *cache_name)
-+ free(*cache_name);
-+
-+ *cache_name = dirname;
-+ *cache_dirid = dirid;
-+ free(name);
-+
-+ return full;
++ return 0;
+}
+
-+int list_subvols(int fd)
++/* finding the generation for a given path is a two step process.
++ * First we use the inode loookup routine to find out the root id
++ *
++ * Then we use the tree search ioctl to scan all the root items for a
++ * given root id and spit out the latest generation we can find
++ */
++static u64 find_root_gen(int fd)
+{
-+ struct root_lookup root_lookup;
-+ struct rb_node *n;
++ struct btrfs_ioctl_ino_lookup_args ino_args;
+ int ret;
+ struct btrfs_ioctl_search_args args;
+ struct btrfs_ioctl_search_key *sk = &args.key;
+ struct btrfs_ioctl_search_header *sh;
-+ struct btrfs_root_ref *ref;
+ unsigned long off = 0;
-+ int name_len;
-+ char *name;
-+ u64 dir_id;
++ u64 max_found = 0;
+ int i;
++ int e;
+
-+ root_lookup_init(&root_lookup);
++ memset(&ino_args, 0, sizeof(ino_args));
++ ino_args.objectid = BTRFS_FIRST_FREE_OBJECTID;
++
++ /* this ioctl fills in ino_args->treeid */
++ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args);
++ e = errno;
++ if (ret) {
++ fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu - %s\n",
++ (unsigned long long)BTRFS_FIRST_FREE_OBJECTID,
++ strerror(e));
++ return 0;
++ }
+
+ memset(&args, 0, sizeof(args));
+
-+ /* search in the tree of tree roots */
+ sk->tree_id = 1;
+
+ /*
-+ * set the min and max to backref keys. The search will
-+ * only send back this type of key now.
-+ */
-+ sk->max_type = BTRFS_ROOT_BACKREF_KEY;
-+ sk->min_type = BTRFS_ROOT_BACKREF_KEY;
-+
-+ /*
-+ * set all the other params to the max, we'll take any objectid
-+ * and any trans
++ * there may be more than one ROOT_ITEM key if there are
++ * snapshots pending deletion, we have to loop through
++ * them.
+ */
-+ sk->max_objectid = (u64)-1;
++ sk->min_objectid = ino_args.treeid;
++ sk->max_objectid = ino_args.treeid;
++ sk->max_type = BTRFS_ROOT_ITEM_KEY;
++ sk->min_type = BTRFS_ROOT_ITEM_KEY;
+ sk->max_offset = (u64)-1;
+ sk->max_transid = (u64)-1;
-+
-+ /* just a big number, doesn't matter much */
+ sk->nr_items = 4096;
+
-+ while(1) {
++ while (1) {
+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
++ e = errno;
+ if (ret < 0) {
-+ fprintf(stderr, "ERROR: can't perform the search\n");
-+ return ret;
++ fprintf(stderr, "ERROR: can't perform the search - %s\n",
++ strerror(e));
++ return 0;
+ }
+ /* the ioctl returns the number of item it found in nr_items */
+ if (sk->nr_items == 0)
+ break;
+
+ off = 0;
-+
-+ /*
-+ * for each item, pull the key out of the header and then
-+ * read the root_ref item it contains
-+ */
+ for (i = 0; i < sk->nr_items; i++) {
++ struct btrfs_root_item *item;
+ sh = (struct btrfs_ioctl_search_header *)(args.buf +
+ off);
-+ off += sizeof(*sh);
-+ if (sh->type == BTRFS_ROOT_BACKREF_KEY) {
-+ ref = (struct btrfs_root_ref *)(args.buf + off);
-+ name_len = btrfs_stack_root_ref_name_len(ref);
-+ name = (char *)(ref + 1);
-+ dir_id = btrfs_stack_root_ref_dirid(ref);
-+
-+ add_root(&root_lookup, sh->objectid, sh->offset,
-+ dir_id, name, name_len);
-+ }
+
++ off += sizeof(*sh);
++ item = (struct btrfs_root_item *)(args.buf + off);
+ off += sh->len;
+
-+ /*
-+ * record the mins in sk so we can make sure the
-+ * next search doesn't repeat this root
-+ */
+ sk->min_objectid = sh->objectid;
+ sk->min_type = sh->type;
+ sk->min_offset = sh->offset;
-+ }
-+ sk->nr_items = 4096;
-+ /* this iteration is done, step forward one root for the next
-+ * ioctl
-+ */
-+ if (sk->min_objectid < (u64)-1) {
-+ sk->min_objectid++;
-+ sk->min_type = BTRFS_ROOT_BACKREF_KEY;
-+ sk->min_offset = 0;
-+ } else
++
++ if (sh->objectid > ino_args.treeid)
++ break;
++
++ if (sh->objectid == ino_args.treeid &&
++ sh->type == BTRFS_ROOT_ITEM_KEY) {
++ max_found = max(max_found,
++ btrfs_root_generation(item));
++ }
++ }
++ if (sk->min_offset < (u64)-1)
++ sk->min_offset++;
++ else
+ break;
-+ }
-+ /*
-+ * now we have an rbtree full of root_info objects, but we need to fill
-+ * in their path names within the subvol that is referencing each one.
-+ */
-+ n = rb_first(&root_lookup.root);
-+ while (n) {
-+ struct root_info *entry;
-+ int ret;
-+ entry = rb_entry(n, struct root_info, rb_node);
-+ ret = lookup_ino_path(fd, entry);
-+ if(ret < 0)
-+ return ret;
-+ n = rb_next(n);
-+ }
+
-+ /* now that we have all the subvol-relative paths filled in,
-+ * we have to string the subvols together so that we can get
-+ * a path all the way back to the FS root
-+ */
-+ n = rb_last(&root_lookup.root);
-+ while (n) {
-+ struct root_info *entry;
-+ entry = rb_entry(n, struct root_info, rb_node);
-+ resolve_root(&root_lookup, entry);
-+ n = rb_prev(n);
++ if (sk->min_type != BTRFS_ROOT_ITEM_KEY)
++ break;
++ if (sk->min_objectid != BTRFS_ROOT_ITEM_KEY)
++ break;
+ }
-+
-+ return ret;
++ return max_found;
+}
+
-+static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh,
-+ struct btrfs_file_extent_item *item,
-+ u64 found_gen, u64 *cache_dirid,
-+ char **cache_dir_name, u64 *cache_ino,
-+ char **cache_full_name)
++/* pass in a directory id and this will return
++ * the full path of the parent directory inside its
++ * subvolume root.
++ *
++ * It may return NULL if it is in the root, or an ERR_PTR if things
++ * go badly.
++ */
++static char *__ino_resolve(int fd, u64 dirid)
+{
-+ u64 len = 0;
-+ u64 disk_start = 0;
-+ u64 disk_offset = 0;
-+ u8 type;
-+ int compressed = 0;
-+ int flags = 0;
-+ char *name = NULL;
++ struct btrfs_ioctl_ino_lookup_args args;
++ int ret;
++ char *full;
++ int e;
+
-+ if (sh->objectid == *cache_ino) {
-+ name = *cache_full_name;
-+ } else if (*cache_full_name) {
-+ free(*cache_full_name);
-+ *cache_full_name = NULL;
-+ }
-+ if (!name) {
-+ name = ino_resolve(fd, sh->objectid, cache_dirid,
-+ cache_dir_name);
-+ *cache_full_name = name;
-+ *cache_ino = sh->objectid;
-+ }
-+ if (!name)
-+ return -EIO;
++ memset(&args, 0, sizeof(args));
++ args.objectid = dirid;
+
-+ type = btrfs_stack_file_extent_type(item);
-+ compressed = btrfs_stack_file_extent_compression(item);
++ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
++ e = errno;
++ if (ret) {
++ fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu - %s\n",
++ (unsigned long long)dirid, strerror(e) );
++ return ERR_PTR(ret);
++ }
+
-+ if (type == BTRFS_FILE_EXTENT_REG ||
-+ type == BTRFS_FILE_EXTENT_PREALLOC) {
-+ disk_start = btrfs_stack_file_extent_disk_bytenr(item);
-+ disk_offset = btrfs_stack_file_extent_offset(item);
-+ len = btrfs_stack_file_extent_num_bytes(item);
-+ } else if (type == BTRFS_FILE_EXTENT_INLINE) {
-+ disk_start = 0;
-+ disk_offset = 0;
-+ len = btrfs_stack_file_extent_ram_bytes(item);
++ if (args.name[0]) {
++ /*
++ * we're in a subdirectory of ref_tree, the kernel ioctl
++ * puts a / in there for us
++ */
++ full = strdup(args.name);
++ if (!full) {
++ perror("malloc failed");
++ return ERR_PTR(-ENOMEM);
++ }
+ } else {
-+ printf("unhandled extent type %d for inode %llu "
-+ "file offset %llu gen %llu\n",
-+ type,
-+ (unsigned long long)sh->objectid,
-+ (unsigned long long)sh->offset,
-+ (unsigned long long)found_gen);
-+
-+ return -EIO;
++ /* we're at the root of ref_tree */
++ full = NULL;
+ }
-+ printf("inode %llu file offset %llu len %llu disk start %llu "
-+ "offset %llu gen %llu flags ",
-+ (unsigned long long)sh->objectid,
-+ (unsigned long long)sh->offset,
-+ (unsigned long long)len,
-+ (unsigned long long)disk_start,
-+ (unsigned long long)disk_offset,
-+ (unsigned long long)found_gen);
++ return full;
++}
+
-+ if (compressed) {
-+ printf("COMPRESS");
-+ flags++;
-+ }
-+ if (type == BTRFS_FILE_EXTENT_PREALLOC) {
-+ printf("%sPREALLOC", flags ? "|" : "");
-+ flags++;
-+ }
-+ if (type == BTRFS_FILE_EXTENT_INLINE) {
-+ printf("%sINLINE", flags ? "|" : "");
-+ flags++;
-+ }
-+ if (!flags)
-+ printf("NONE");
++/*
++ * simple string builder, returning a new string with both
++ * dirid and name
++ */
++char *build_name(char *dirid, char *name)
++{
++ char *full;
++ if (!dirid)
++ return strdup(name);
+
-+ printf(" %s\n", name);
-+ return 0;
++ full = malloc(strlen(dirid) + strlen(name) + 1);
++ if (!full)
++ return NULL;
++ strcpy(full, dirid);
++ strcat(full, name);
++ return full;
+}
+
-+int find_updated_files(int fd, u64 root_id, u64 oldest_gen)
++/*
++ * given an inode number, this returns the full path name inside the subvolume
++ * to that file/directory. cache_dirid and cache_name are used to
++ * cache the results so we can avoid tree searches if a later call goes
++ * to the same directory or file name
++ */
++static char *ino_resolve(int fd, u64 ino, u64 *cache_dirid, char **cache_name)
++
+{
++ u64 dirid;
++ char *dirname;
++ char *name;
++ char *full;
+ int ret;
+ struct btrfs_ioctl_search_args args;
+ struct btrfs_ioctl_search_key *sk = &args.key;
+ struct btrfs_ioctl_search_header *sh;
-+ struct btrfs_file_extent_item *item;
+ unsigned long off = 0;
-+ u64 found_gen;
-+ u64 max_found = 0;
-+ int i;
-+ u64 cache_dirid = 0;
-+ u64 cache_ino = 0;
-+ char *cache_dir_name = NULL;
-+ char *cache_full_name = NULL;
-+ struct btrfs_file_extent_item backup;
++ int namelen;
++ int e;
+
-+ memset(&backup, 0, sizeof(backup));
+ memset(&args, 0, sizeof(args));
+
-+ sk->tree_id = root_id;
++ sk->tree_id = 0;
+
+ /*
-+ * set all the other params to the max, we'll take any objectid
-+ * and any trans
++ * step one, we search for the inode back ref. We just use the first
++ * one
+ */
-+ sk->max_objectid = (u64)-1;
++ sk->min_objectid = ino;
++ sk->max_objectid = ino;
++ sk->max_type = BTRFS_INODE_REF_KEY;
+ sk->max_offset = (u64)-1;
++ sk->min_type = BTRFS_INODE_REF_KEY;
+ sk->max_transid = (u64)-1;
-+ sk->max_type = BTRFS_EXTENT_DATA_KEY;
-+ sk->min_transid = oldest_gen;
-+ /* just a big number, doesn't matter much */
-+ sk->nr_items = 4096;
++ sk->nr_items = 1;
+
-+ max_found = find_root_gen(fd);
-+ while(1) {
-+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
-+ if (ret < 0) {
-+ fprintf(stderr, "ERROR: can't perform the search\n");
-+ return ret;
-+ }
-+ /* the ioctl returns the number of item it found in nr_items */
-+ if (sk->nr_items == 0)
-+ break;
++ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
++ e = errno;
++ if (ret < 0) {
++ fprintf(stderr, "ERROR: can't perform the search - %s\n",
++ strerror(e));
++ return NULL;
++ }
++ /* the ioctl returns the number of item it found in nr_items */
++ if (sk->nr_items == 0)
++ return NULL;
+
-+ off = 0;
++ off = 0;
++ sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
+
-+ /*
-+ * for each item, pull the key out of the header and then
-+ * read the root_ref item it contains
-+ */
-+ for (i = 0; i < sk->nr_items; i++) {
-+ sh = (struct btrfs_ioctl_search_header *)(args.buf +
-+ off);
-+ off += sizeof(*sh);
++ if (sh->type == BTRFS_INODE_REF_KEY) {
++ struct btrfs_inode_ref *ref;
++ dirid = sh->offset;
+
-+ /*
-+ * just in case the item was too big, pass something other
-+ * than garbage
-+ */
-+ if (sh->len == 0)
-+ item = &backup;
-+ else
-+ item = (struct btrfs_file_extent_item *)(args.buf +
-+ off);
-+ found_gen = btrfs_stack_file_extent_generation(item);
-+ if (sh->type == BTRFS_EXTENT_DATA_KEY &&
-+ found_gen >= oldest_gen) {
-+ print_one_extent(fd, sh, item, found_gen,
-+ &cache_dirid, &cache_dir_name,
-+ &cache_ino, &cache_full_name);
-+ }
-+ off += sh->len;
++ ref = (struct btrfs_inode_ref *)(sh + 1);
++ namelen = btrfs_stack_inode_ref_name_len(ref);
+
-+ /*
-+ * record the mins in sk so we can make sure the
-+ * next search doesn't repeat this root
-+ */
-+ sk->min_objectid = sh->objectid;
-+ sk->min_offset = sh->offset;
-+ sk->min_type = sh->type;
++ name = (char *)(ref + 1);
++ name = strndup(name, namelen);
++
++ /* use our cached value */
++ if (dirid == *cache_dirid && *cache_name) {
++ dirname = *cache_name;
++ goto build;
+ }
-+ sk->nr_items = 4096;
-+ if (sk->min_offset < (u64)-1)
-+ sk->min_offset++;
-+ else if (sk->min_objectid < (u64)-1) {
-+ sk->min_objectid++;
-+ sk->min_offset = 0;
-+ sk->min_type = 0;
-+ } else
-+ break;
++ } else {
++ return NULL;
+ }
-+ free(cache_dir_name);
-+ free(cache_full_name);
-+ printf("transid marker was %llu\n", (unsigned long long)max_found);
-+ return ret;
-+}
-diff --git a/btrfs-map-logical.c b/btrfs-map-logical.c
-new file mode 100644
-index 0000000..a109c6a
---- /dev/null
-+++ b/btrfs-map-logical.c
-@@ -0,0 +1,221 @@
-+/*
-+ * Copyright (C) 2009 Oracle. All rights reserved.
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public
-+ * License v2 as published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-+ * General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public
-+ * License along with this program; if not, write to the
-+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-+ * Boston, MA 021110-1307, USA.
-+ */
++ /*
++ * the inode backref gives us the file name and the parent directory id.
++ * From here we use __ino_resolve to get the path to the parent
++ */
++ dirname = __ino_resolve(fd, dirid);
++build:
++ full = build_name(dirname, name);
++ if (*cache_name && dirname != *cache_name)
++ free(*cache_name);
+
-+#define _XOPEN_SOURCE 500
-+#define _GNU_SOURCE 1
-+#include <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"
++ *cache_name = dirname;
++ *cache_dirid = dirid;
++ free(name);
+
-+/* we write the mirror info to stdout unless they are dumping the data
-+ * to stdout
-+ * */
-+static FILE *info_file;
++ return full;
++}
+
-+struct extent_buffer *debug_read_block(struct btrfs_root *root, u64 bytenr,
-+ u32 blocksize, int copy)
++static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
+{
+ int ret;
-+ int dev_nr;
-+ struct extent_buffer *eb;
-+ u64 length;
-+ struct btrfs_multi_bio *multi = NULL;
-+ struct btrfs_device *device;
-+ int num_copies;
-+ int mirror_num = 1;
++ struct btrfs_ioctl_search_args args;
++ struct btrfs_ioctl_search_key *sk = &args.key;
++ struct btrfs_ioctl_search_header *sh;
++ struct btrfs_root_ref *ref;
++ unsigned long off = 0;
++ int name_len;
++ char *name;
++ u64 dir_id;
++ int i;
+
-+ eb = btrfs_find_create_tree_block(root, bytenr, blocksize);
-+ if (!eb)
-+ return NULL;
++ root_lookup_init(root_lookup);
++ memset(&args, 0, sizeof(args));
+
-+ dev_nr = 0;
-+ length = blocksize;
-+ while (1) {
-+ ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
-+ eb->start, &length, &multi, mirror_num);
-+ BUG_ON(ret);
-+ device = multi->stripes[0].dev;
-+ eb->fd = device->fd;
-+ device->total_ios++;
-+ eb->dev_bytenr = multi->stripes[0].physical;
++ root_lookup_init(root_lookup);
+
-+ fprintf(info_file, "mirror %d logical %Lu physical %Lu "
-+ "device %s\n", mirror_num, bytenr, eb->dev_bytenr,
-+ device->name);
-+ kfree(multi);
++ memset(&args, 0, sizeof(args));
+
-+ if (!copy || mirror_num == copy)
-+ ret = read_extent_from_disk(eb);
++ /* search in the tree of tree roots */
++ sk->tree_id = 1;
+
-+ num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
-+ eb->start, eb->len);
-+ if (num_copies == 1)
++ /*
++ * set the min and max to backref keys. The search will
++ * only send back this type of key now.
++ */
++ sk->max_type = BTRFS_ROOT_BACKREF_KEY;
++ sk->min_type = BTRFS_ROOT_BACKREF_KEY;
++
++ /*
++ * set all the other params to the max, we'll take any objectid
++ * and any trans
++ */
++ sk->max_objectid = (u64)-1;
++ sk->max_offset = (u64)-1;
++ sk->max_transid = (u64)-1;
++
++ /* just a big number, doesn't matter much */
++ sk->nr_items = 4096;
++
++ while(1) {
++ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
++ if (ret < 0)
++ return ret;
++ /* the ioctl returns the number of item it found in nr_items */
++ if (sk->nr_items == 0)
+ break;
+
-+ mirror_num++;
-+ if (mirror_num > num_copies)
++ off = 0;
++
++ /*
++ * for each item, pull the key out of the header and then
++ * read the root_ref item it contains
++ */
++ for (i = 0; i < sk->nr_items; i++) {
++ sh = (struct btrfs_ioctl_search_header *)(args.buf +
++ off);
++ off += sizeof(*sh);
++ if (sh->type == BTRFS_ROOT_BACKREF_KEY) {
++ ref = (struct btrfs_root_ref *)(args.buf + off);
++ name_len = btrfs_stack_root_ref_name_len(ref);
++ name = (char *)(ref + 1);
++ dir_id = btrfs_stack_root_ref_dirid(ref);
++
++ add_root(root_lookup, sh->objectid, sh->offset,
++ dir_id, name, name_len);
++ }
++
++ off += sh->len;
++
++ /*
++ * record the mins in sk so we can make sure the
++ * next search doesn't repeat this root
++ */
++ sk->min_objectid = sh->objectid;
++ sk->min_type = sh->type;
++ sk->min_offset = sh->offset;
++ }
++ sk->nr_items = 4096;
++ /* this iteration is done, step forward one root for the next
++ * ioctl
++ */
++ if (sk->min_type < BTRFS_ROOT_BACKREF_KEY) {
++ sk->min_type = BTRFS_ROOT_BACKREF_KEY;
++ sk->min_offset = 0;
++ } else if (sk->min_objectid < (u64)-1) {
++ sk->min_objectid++;
++ sk->min_type = BTRFS_ROOT_BACKREF_KEY;
++ sk->min_offset = 0;
++ } else
+ break;
+ }
-+ return eb;
++
++ return 0;
+}
+
-+static void print_usage(void)
++static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
+{
-+ fprintf(stderr, "usage: btrfs-map-logical [options] mount_point\n");
-+ fprintf(stderr, "\t-l Logical extent to map\n");
-+ fprintf(stderr, "\t-c Copy of the extent to read (usually 1 or 2)\n");
-+ fprintf(stderr, "\t-o Output file to hold the extent\n");
-+ fprintf(stderr, "\t-s Number of bytes to read\n");
-+ exit(1);
-+}
++ struct rb_node *n;
+
-+static struct option long_options[] = {
-+ /* { "byte-count", 1, NULL, 'b' }, */
-+ { "logical", 1, NULL, 'l' },
-+ { "copy", 1, NULL, 'c' },
-+ { "output", 1, NULL, 'c' },
-+ { "bytes", 1, NULL, 'b' },
-+ { 0, 0, 0, 0}
-+};
++ n = rb_first(&root_lookup->root);
++ while (n) {
++ struct root_info *entry;
++ int ret;
++ entry = rb_entry(n, struct root_info, rb_node);
++ ret = lookup_ino_path(fd, entry);
++ if(ret < 0)
++ return ret;
++ n = rb_next(n);
++ }
+
-+int main(int ac, char **av)
++ return 0;
++}
++
++int list_subvols(int fd, int print_parent)
+{
-+ struct cache_tree root_cache;
-+ struct btrfs_root *root;
-+ struct extent_buffer *eb;
-+ char *dev;
-+ char *output_file = NULL;
-+ u64 logical = 0;
-+ int ret = 0;
-+ int option_index = 0;
-+ int copy = 0;
-+ u64 bytes = 0;
-+ int out_fd = 0;
-+ int err;
++ struct root_lookup root_lookup;
++ struct rb_node *n;
++ int ret;
+
-+ while(1) {
-+ int c;
-+ c = getopt_long(ac, av, "l:c:o:b:", long_options,
-+ &option_index);
-+ if (c < 0)
-+ break;
-+ switch(c) {
-+ case 'l':
-+ logical = atoll(optarg);
-+ if (logical == 0) {
-+ fprintf(stderr,
-+ "invalid extent number\n");
-+ print_usage();
-+ }
-+ break;
-+ case 'c':
-+ copy = atoi(optarg);
-+ if (copy == 0) {
-+ fprintf(stderr,
-+ "invalid copy number\n");
-+ print_usage();
-+ }
-+ break;
-+ case 'b':
-+ bytes = atoll(optarg);
-+ if (bytes == 0) {
-+ fprintf(stderr,
-+ "invalid byte count\n");
-+ print_usage();
-+ }
-+ break;
-+ case 'o':
-+ output_file = strdup(optarg);
-+ break;
-+ default:
-+ print_usage();
-+ }
++ ret = __list_subvol_search(fd, &root_lookup);
++ if (ret) {
++ fprintf(stderr, "ERROR: can't perform the search - %s\n",
++ strerror(errno));
++ return ret;
+ }
-+ ac = ac - optind;
-+ if (ac == 0)
-+ print_usage();
-+ if (logical == 0)
-+ print_usage();
-+ if (copy < 0)
-+ print_usage();
-+
-+ dev = av[optind];
+
-+ radix_tree_init();
-+ cache_tree_init(&root_cache);
-+
-+ root = open_ctree(dev, 0, 0);
-+ if (!root) {
-+ fprintf(stderr, "Open ctree failed\n");
-+ exit(1);
-+ }
++ /*
++ * now we have an rbtree full of root_info objects, but we need to fill
++ * in their path names within the subvol that is referencing each one.
++ */
++ ret = __list_subvol_fill_paths(fd, &root_lookup);
++ if (ret < 0)
++ return ret;
+
-+ if (output_file) {
-+ if (strcmp(output_file, "-") == 0) {
-+ out_fd = 1;
-+ info_file = stderr;
++ /* now that we have all the subvol-relative paths filled in,
++ * we have to string the subvols together so that we can get
++ * a path all the way back to the FS root
++ */
++ n = rb_last(&root_lookup.root);
++ while (n) {
++ struct root_info *entry;
++ u64 root_id;
++ u64 level;
++ u64 parent_id;
++ char *path;
++ entry = rb_entry(n, struct root_info, rb_node);
++ resolve_root(&root_lookup, entry, &root_id, &parent_id,
++ &level, &path);
++ if (print_parent) {
++ printf("ID %llu parent %llu top level %llu path %s\n",
++ (unsigned long long)root_id,
++ (unsigned long long)parent_id,
++ (unsigned long long)level, path);
+ } else {
-+ out_fd = open(output_file, O_RDWR | O_CREAT, 0600);
-+ if (out_fd < 0)
-+ goto close;
-+ err = ftruncate(out_fd, 0);
-+ if (err) {
-+ close(out_fd);
-+ goto close;
-+ }
-+ info_file = stdout;
++ printf("ID %llu top level %llu path %s\n",
++ (unsigned long long)root_id,
++ (unsigned long long)level, path);
+ }
++ free(path);
++ n = rb_prev(n);
+ }
+
-+ if (bytes == 0)
-+ bytes = root->sectorsize;
++ return ret;
++}
+
-+ bytes = (bytes + root->sectorsize - 1) / root->sectorsize;
-+ bytes *= root->sectorsize;
++static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh,
++ struct btrfs_file_extent_item *item,
++ u64 found_gen, u64 *cache_dirid,
++ char **cache_dir_name, u64 *cache_ino,
++ char **cache_full_name)
++{
++ u64 len = 0;
++ u64 disk_start = 0;
++ u64 disk_offset = 0;
++ u8 type;
++ int compressed = 0;
++ int flags = 0;
++ char *name = NULL;
+
-+ while (bytes > 0) {
-+ eb = debug_read_block(root, logical, root->sectorsize, copy);
-+ if (eb && output_file) {
-+ err = write(out_fd, eb->data, eb->len);
-+ if (err < 0 || err != eb->len) {
-+ fprintf(stderr, "output file write failed\n");
-+ goto out_close_fd;
-+ }
-+ }
-+ free_extent_buffer(eb);
-+ logical += root->sectorsize;
-+ bytes -= root->sectorsize;
++ if (sh->objectid == *cache_ino) {
++ name = *cache_full_name;
++ } else if (*cache_full_name) {
++ free(*cache_full_name);
++ *cache_full_name = NULL;
++ }
++ if (!name) {
++ name = ino_resolve(fd, sh->objectid, cache_dirid,
++ cache_dir_name);
++ *cache_full_name = name;
++ *cache_ino = sh->objectid;
+ }
++ if (!name)
++ return -EIO;
+
-+out_close_fd:
-+ if (output_file && out_fd != 1)
-+ close(out_fd);
-+close:
-+ close_ctree(root);
-+ return ret;
-+}
-diff --git a/btrfs-vol.c b/btrfs-vol.c
-index 8069778..4ed799d 100644
---- a/btrfs-vol.c
-+++ b/btrfs-vol.c
-@@ -108,10 +108,24 @@ int main(int ac, char **av)
- if (device && strcmp(device, "missing") == 0 &&
- cmd == BTRFS_IOC_RM_DEV) {
- fprintf(stderr, "removing missing devices from %s\n", mnt);
-- } else if (device) {
-+ } else if (cmd != BTRFS_IOC_BALANCE) {
-+ if (cmd == BTRFS_IOC_ADD_DEV) {
-+ ret = check_mounted(device);
-+ if (ret < 0) {
-+ fprintf(stderr,
-+ "error checking %s mount status\n",
-+ device);
-+ exit(1);
-+ }
-+ if (ret == 1) {
-+ fprintf(stderr, "%s is mounted\n", device);
-+ exit(1);
-+ }
-+ }
- devfd = open(device, O_RDWR);
-- if (!devfd) {
-+ if (devfd < 0) {
- fprintf(stderr, "Unable to open device %s\n", device);
-+ exit(1);
- }
- ret = fstat(devfd, &st);
- if (ret) {
-diff --git a/btrfs-zero-log.c b/btrfs-zero-log.c
-new file mode 100644
-index 0000000..f10438b
---- /dev/null
-+++ b/btrfs-zero-log.c
-@@ -0,0 +1,69 @@
-+/*
-+ * Copyright (C) 2007 Oracle. All rights reserved.
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public
-+ * License v2 as published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-+ * General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public
-+ * License along with this program; if not, write to the
-+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-+ * Boston, MA 021110-1307, USA.
-+ */
++ type = btrfs_stack_file_extent_type(item);
++ compressed = btrfs_stack_file_extent_compression(item);
+
-+#define _XOPEN_SOURCE 500
-+#define _GNU_SOURCE 1
-+#include <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"
++ if (type == BTRFS_FILE_EXTENT_REG ||
++ type == BTRFS_FILE_EXTENT_PREALLOC) {
++ disk_start = btrfs_stack_file_extent_disk_bytenr(item);
++ disk_offset = btrfs_stack_file_extent_offset(item);
++ len = btrfs_stack_file_extent_num_bytes(item);
++ } else if (type == BTRFS_FILE_EXTENT_INLINE) {
++ disk_start = 0;
++ disk_offset = 0;
++ len = btrfs_stack_file_extent_ram_bytes(item);
++ } else {
++ printf("unhandled extent type %d for inode %llu "
++ "file offset %llu gen %llu\n",
++ type,
++ (unsigned long long)sh->objectid,
++ (unsigned long long)sh->offset,
++ (unsigned long long)found_gen);
+
-+static void print_usage(void)
-+{
-+ fprintf(stderr, "usage: btrfs-zero-log dev\n");
-+ fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION);
-+ exit(1);
++ return -EIO;
++ }
++ printf("inode %llu file offset %llu len %llu disk start %llu "
++ "offset %llu gen %llu flags ",
++ (unsigned long long)sh->objectid,
++ (unsigned long long)sh->offset,
++ (unsigned long long)len,
++ (unsigned long long)disk_start,
++ (unsigned long long)disk_offset,
++ (unsigned long long)found_gen);
++
++ if (compressed) {
++ printf("COMPRESS");
++ flags++;
++ }
++ if (type == BTRFS_FILE_EXTENT_PREALLOC) {
++ printf("%sPREALLOC", flags ? "|" : "");
++ flags++;
++ }
++ if (type == BTRFS_FILE_EXTENT_INLINE) {
++ printf("%sINLINE", flags ? "|" : "");
++ flags++;
++ }
++ if (!flags)
++ printf("NONE");
++
++ printf(" %s\n", name);
++ return 0;
+}
+
-+int main(int ac, char **av)
++int find_updated_files(int fd, u64 root_id, u64 oldest_gen)
+{
-+ struct btrfs_root *root;
+ int ret;
++ struct btrfs_ioctl_search_args args;
++ struct btrfs_ioctl_search_key *sk = &args.key;
++ struct btrfs_ioctl_search_header *sh;
++ struct btrfs_file_extent_item *item;
++ unsigned long off = 0;
++ u64 found_gen;
++ u64 max_found = 0;
++ int i;
++ int e;
++ u64 cache_dirid = 0;
++ u64 cache_ino = 0;
++ char *cache_dir_name = NULL;
++ char *cache_full_name = NULL;
++ struct btrfs_file_extent_item backup;
+
-+ if (ac != 2)
-+ print_usage();
-+
-+ radix_tree_init();
-+
-+ if((ret = check_mounted(av[1])) < 0) {
-+ fprintf(stderr, "Could not check mount status: %s\n", strerror(ret));
-+ return ret;
-+ } else if(ret) {
-+ fprintf(stderr, "%s is currently mounted. Aborting.\n", av[1]);
-+ return -EBUSY;
-+ }
-+
-+ root = open_ctree(av[1], 0, 1);
++ memset(&backup, 0, sizeof(backup));
++ memset(&args, 0, sizeof(args));
+
-+ if (root == NULL)
-+ return 1;
-+
-+ btrfs_set_super_log_root(&root->fs_info->super_copy, 0);
-+ btrfs_set_super_log_root_level(&root->fs_info->super_copy, 0);
-+ close_ctree(root);
-+ return ret;
-+}
-diff --git a/btrfs.c b/btrfs.c
-new file mode 100644
-index 0000000..46314cf
---- /dev/null
-+++ b/btrfs.c
-@@ -0,0 +1,387 @@
-+/*
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public
-+ * License v2 as published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-+ * General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public
-+ * License along with this program; if not, write to the
-+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-+ * Boston, MA 021110-1307, USA.
-+ */
-+
-+#define _GNU_SOURCE
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+
-+#include "kerncompat.h"
-+#include "btrfs_cmds.h"
-+#include "version.h"
-+
-+typedef int (*CommandFunction)(int argc, char **argv);
-+
-+struct Command {
-+ CommandFunction func; /* function which implements the command */
-+ int nargs; /* if == 999, any number of arguments
-+ if >= 0, number of arguments,
-+ if < 0, _minimum_ number of arguments */
-+ char *verb; /* verb */
-+ char *help; /* help lines; form the 2nd onward they are
-+ indented */
-+
-+ /* the following fields are run-time filled by the program */
-+ char **cmds; /* array of subcommands */
-+ int ncmds; /* number of subcommand */
-+};
-+
-+static struct Command commands[] = {
++ sk->tree_id = root_id;
+
+ /*
-+ avoid short commands different for the case only
-+ */
-+ { do_clone, 2,
-+ "subvolume snapshot", "<source> [<dest>/]<name>\n"
-+ "Create a writable snapshot of the subvolume <source> with\n"
-+ "the name <name> in the <dest> directory."
-+ },
-+ { do_delete_subvolume, 1,
-+ "subvolume delete", "<subvolume>\n"
-+ "Delete the subvolume <subvolume>."
-+ },
-+ { do_create_subvol, 1,
-+ "subvolume create", "[<dest>/]<name>\n"
-+ "Create a subvolume in <dest> (or the current directory if\n"
-+ "not passed)."
-+ },
-+ { do_subvol_list, 1, "subvolume list", "<path>\n"
-+ "List the snapshot/subvolume of a filesystem."
-+ },
-+ { do_find_newer, 2, "subvolume find-new", "<path> <last_gen>\n"
-+ "List the recently modified files in a filesystem."
-+ },
-+ { do_defrag, -1,
-+ "filesystem defragment", "[-vcf] [-s start] [-l len] [-t size] <file>|<dir> [<file>|<dir>...]\n"
-+ "Defragment a file or a directory."
-+ },
-+ { do_set_default_subvol, 2,
-+ "subvolume set-default", "<id> <path>\n"
-+ "Set the subvolume of the filesystem <path> which will be mounted\n"
-+ "as default."
-+ },
-+ { do_fssync, 1,
-+ "filesystem sync", "<path>\n"
-+ "Force a sync on the filesystem <path>."
-+ },
-+ { do_resize, 2,
-+ "filesystem resize", "[+/-]<newsize>[gkm]|max <filesystem>\n"
-+ "Resize the file system. If 'max' is passed, the filesystem\n"
-+ "will occupe all available space on the device."
-+ },
-+ { do_show_filesystem, 999,
-+ "filesystem show", "[<uuid>|<label>]\n"
-+ "Show the info of a btrfs filesystem. If no <uuid> or <label>\n"
-+ "is passed, info of all the btrfs filesystem are shown."
-+ },
-+ { do_df_filesystem, 1,
-+ "filesystem df", "<path>\n"
-+ "Show space usage information for a mount point\n."
-+ },
-+ { do_balance, 1,
-+ "filesystem balance", "<path>\n"
-+ "Balance the chunks across the device."
-+ },
-+ { do_scan,
-+ 999, "device scan", "[<device> [<device>..]\n"
-+ "Scan all device for or the passed device for a btrfs\n"
-+ "filesystem."
-+ },
-+ { do_add_volume, -2,
-+ "device add", "<dev> [<dev>..] <path>\n"
-+ "Add a device to a filesystem."
-+ },
-+ { do_remove_volume, -2,
-+ "device delete", "<dev> [<dev>..] <path>\n"
-+ "Remove a device from a filesystem."
-+ },
-+ /* coming soon
-+ { 2, "filesystem label", "<label> <path>\n"
-+ "Set the label of a filesystem"
-+ }
-+ */
-+ { 0, 0 , 0 }
-+};
-+
-+static char *get_prgname(char *programname)
-+{
-+ char *np;
-+ np = strrchr(programname,'/');
-+ if(!np)
-+ np = programname;
-+ else
-+ np++;
-+
-+ return np;
-+}
-+
-+static void print_help(char *programname, struct Command *cmd)
-+{
-+ char *pc;
-+
-+ printf("\t%s %s ", programname, cmd->verb );
-+
-+ for(pc = cmd->help; *pc; pc++){
-+ putchar(*pc);
-+ if(*pc == '\n')
-+ printf("\t\t");
-+ }
-+ putchar('\n');
-+}
-+
-+static void help(char *np)
-+{
-+ struct Command *cp;
-+
-+ printf("Usage:\n");
-+ for( cp = commands; cp->verb; cp++ )
-+ print_help(np, cp);
-+
-+ printf("\n\t%s help|--help|-h\n\t\tShow the help.\n",np);
-+ printf("\n%s\n", BTRFS_BUILD_VERSION);
-+}
-+
-+static int split_command(char *cmd, char ***commands)
-+{
-+ int c, l;
-+ char *p, *s;
-+
-+ for( *commands = 0, l = c = 0, p = s = cmd ; ; p++, l++ ){
-+ if ( *p && *p != ' ' )
-+ continue;
-+
-+ /* c + 2 so that we have room for the null */
-+ (*commands) = realloc( (*commands), sizeof(char *)*(c + 2));
-+ (*commands)[c] = strndup(s, l);
-+ c++;
-+ l = 0;
-+ s = p+1;
-+ if( !*p ) break;
-+ }
-+
-+ (*commands)[c] = 0;
-+ return c;
-+}
-+
-+/*
-+ This function checks if the passed command is ambiguous
-+*/
-+static int check_ambiguity(struct Command *cmd, char **argv){
-+ int i;
-+ struct Command *cp;
-+ /* check for ambiguity */
-+ for( i = 0 ; i < cmd->ncmds ; i++ ){
-+ int match;
-+ for( match = 0, cp = commands; cp->verb; cp++ ){
-+ int j, skip;
-+ char *s1, *s2;
-+
-+ if( cp->ncmds < i )
-+ continue;
-+
-+ for( skip = 0, j = 0 ; j < i ; j++ )
-+ if( strcmp(cmd->cmds[j], cp->cmds[j])){
-+ skip=1;
-+ break;
-+ }
-+ if(skip)
-+ continue;
++ * 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;
+
-+ if( !strcmp(cmd->cmds[i], cp->cmds[i]))
-+ continue;
-+ for(s2 = cp->cmds[i], s1 = argv[i+1];
-+ *s1 == *s2 && *s1; s1++, s2++ ) ;
-+ if( !*s1 )
-+ match++;
-+ }
-+ if(match){
-+ int j;
-+ fprintf(stderr, "ERROR: in command '");
-+ for( j = 0 ; j <= i ; j++ )
-+ fprintf(stderr, "%s%s",j?" ":"", argv[j+1]);
-+ fprintf(stderr, "', '%s' is ambiguous\n",argv[j]);
-+ return -2;
++ 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;
+ }
-+ }
-+ return 0;
-+}
-+
-+/*
-+ * This function, compacts the program name and the command in the first
-+ * element of the '*av' array
-+ */
-+static int prepare_args(int *ac, char ***av, char *prgname, struct Command *cmd ){
-+
-+ char **ret;
-+ int i;
-+ char *newname;
-+
-+ ret = (char **)malloc(sizeof(char*)*(*ac+1));
-+ newname = (char*)malloc(strlen(prgname)+strlen(cmd->verb)+2);
-+ if( !ret || !newname ){
-+ free(ret);
-+ free(newname);
-+ return -1;
-+ }
-+
-+ ret[0] = newname;
-+ for(i=0; i < *ac ; i++ )
-+ ret[i+1] = (*av)[i];
-+
-+ strcpy(newname, prgname);
-+ strcat(newname, " ");
-+ strcat(newname, cmd->verb);
-+
-+ (*ac)++;
-+ *av = ret;
-+
-+ return 0;
-+
-+}
-+
-+
-+
-+/*
-+
-+ This function perform the following jobs:
-+ - show the help if '--help' or 'help' or '-h' are passed
-+ - verify that a command is not ambiguous, otherwise show which
-+ part of the command is ambiguous
-+ - if after a (even partial) command there is '--help' show the help
-+ for all the matching commands
-+ - if the command doesn't' match show an error
-+ - finally, if a command match, they return which command is matched and
-+ the arguments
-+
-+ The function return 0 in case of help is requested; <0 in case
-+ of uncorrect command; >0 in case of matching commands
-+ argc, argv are the arg-counter and arg-vector (input)
-+ *nargs_ is the number of the arguments after the command (output)
-+ **cmd_ is the invoked command (output)
-+ ***args_ are the arguments after the command
-+
-+*/
-+static int parse_args(int argc, char **argv,
-+ CommandFunction *func_,
-+ int *nargs_, char **cmd_, char ***args_ )
-+{
-+ struct Command *cp;
-+ struct Command *matchcmd=0;
-+ char *prgname = get_prgname(argv[0]);
-+ int i=0, helprequested=0;
-+
-+ if( argc < 2 || !strcmp(argv[1], "help") ||
-+ !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){
-+ help(prgname);
-+ return 0;
-+ }
++ /* the ioctl returns the number of item it found in nr_items */
++ if (sk->nr_items == 0)
++ break;
+
-+ for( cp = commands; cp->verb; cp++ )
-+ if( !cp->ncmds)
-+ cp->ncmds = split_command(cp->verb, &(cp->cmds));
++ off = 0;
+
-+ for( cp = commands; cp->verb; cp++ ){
-+ int match;
++ /*
++ * 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( argc-1 < cp->ncmds )
-+ continue;
-+ for( match = 1, i = 0 ; i < cp->ncmds ; i++ ){
-+ char *s1, *s2;
-+ s1 = cp->cmds[i];
-+ s2 = argv[i+1];
-+
-+ for(s2 = cp->cmds[i], s1 = argv[i+1];
-+ *s1 == *s2 && *s1;
-+ s1++, s2++ ) ;
-+ if( *s1 ){
-+ match=0;
-+ break;
++ /*
++ * 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;
+
-+ /* If you understand why this code works ...
-+ you are a genious !! */
-+ if(argc>i+1 && !strcmp(argv[i+1],"--help")){
-+ if(!helprequested)
-+ printf("Usage:\n");
-+ print_help(prgname, cp);
-+ helprequested=1;
-+ continue;
++ /*
++ * 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;
+ }
-+
-+ if(!match)
-+ continue;
-+
-+ matchcmd = cp;
-+ *nargs_ = argc-matchcmd->ncmds-1;
-+ *cmd_ = matchcmd->verb;
-+ *args_ = argv+matchcmd->ncmds+1;
-+ *func_ = cp->func;
-+
-+ break;
-+ }
-+
-+ if(helprequested){
-+ printf("\n%s\n", BTRFS_BUILD_VERSION);
-+ return 0;
-+ }
-+
-+ if(!matchcmd){
-+ fprintf( stderr, "ERROR: unknown command '%s'\n",argv[1]);
-+ help(prgname);
-+ return -1;
-+ }
-+
-+ if(check_ambiguity(matchcmd, argv))
-+ return -2;
-+
-+ /* check the number of argument */
-+ if (matchcmd->nargs < 0 && matchcmd->nargs < -*nargs_ ){
-+ fprintf(stderr, "ERROR: '%s' requires minimum %d arg(s)\n",
-+ matchcmd->verb, -matchcmd->nargs);
-+ return -2;
-+ }
-+ if(matchcmd->nargs >= 0 && matchcmd->nargs != *nargs_ && matchcmd->nargs != 999){
-+ fprintf(stderr, "ERROR: '%s' requires %d arg(s)\n",
-+ matchcmd->verb, matchcmd->nargs);
-+ return -2;
++ 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;
+ }
-+
-+ if (prepare_args( nargs_, args_, prgname, matchcmd )){
-+ fprintf(stderr, "ERROR: not enough memory\\n");
-+ return -20;
-+ }
-+
-+
-+ return 1;
++ free(cache_dir_name);
++ free(cache_full_name);
++ printf("transid marker was %llu\n", (unsigned long long)max_found);
++ return ret;
+}
-+int main(int ac, char **av )
++
++char *path_for_root(int fd, u64 root)
+{
++ struct root_lookup root_lookup;
++ struct rb_node *n;
++ char *ret_path = NULL;
++ int ret;
+
-+ char *cmd=0, **args=0;
-+ int nargs=0, r;
-+ CommandFunction func=0;
++ ret = __list_subvol_search(fd, &root_lookup);
++ if (ret < 0)
++ return ERR_PTR(ret);
+
-+ r = parse_args(ac, av, &func, &nargs, &cmd, &args);
-+ if( r <= 0 ){
-+ /* error or no command to parse*/
-+ exit(-r);
-+ }
++ ret = __list_subvol_fill_paths(fd, &root_lookup);
++ if (ret < 0)
++ return ERR_PTR(ret);
+
-+ exit(func(nargs, args));
++ 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_cmds.c b/btrfs_cmds.c
+diff --git a/btrfs-map-logical.c b/btrfs-map-logical.c
new file mode 100644
-index 0000000..8031c58
+index 0000000..d79a73a
--- /dev/null
-+++ b/btrfs_cmds.c
-@@ -0,0 +1,924 @@
++++ 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.
@@ -1695,1052 +1651,858 @@ index 0000000..8031c58
+ * Boston, MA 021110-1307, USA.
+ */
+
-+
++#define _XOPEN_SOURCE 500
++#define _GNU_SOURCE 1
+#include <stdio.h>
+#include <stdlib.h>
-+#include <string.h>
-+#include <sys/ioctl.h>
-+#include <sys/types.h>
-+#include <dirent.h>
-+#include <sys/stat.h>
-+#include <unistd.h>
+#include <fcntl.h>
-+#include <libgen.h>
-+#include <limits.h>
-+#include <uuid/uuid.h>
-+#include <ctype.h>
-+
-+#undef ULONG_MAX
-+
++#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 "utils.h"
++#include "list.h"
+#include "version.h"
-+#include "ioctl.h"
-+#include "volumes.h"
+
-+#include "btrfs_cmds.h"
++/* we write the mirror info to stdout unless they are dumping the data
++ * to stdout
++ * */
++static FILE *info_file;
+
-+#ifdef __CHECKER__
-+#define BLKGETSIZE64 0
-+#define BTRFS_IOC_SNAP_CREATE 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
-+
-+/*
-+ * 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 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;
+
-+ struct stat st;
-+ int res;
-+
-+ res = stat(path, &st);
-+ if(res < 0 )
-+ return -1;
-+
-+ return (st.st_ino == 256) && S_ISDIR(st.st_mode);
-+
-+}
-+
-+/*
-+ * 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;
++ eb = btrfs_find_create_tree_block(root, bytenr, blocksize);
++ if (!eb)
++ return NULL;
+
-+ res = stat(path, &st);
-+ if(res < 0 )
-+ return -1;
++ 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;
+
-+ return S_ISDIR(st.st_mode);
++ 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);
+
-+static int open_file_or_dir(const char *fname)
-+{
-+ int ret;
-+ struct stat st;
-+ DIR *dirstream;
-+ int fd;
++ num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
++ eb->start, eb->len);
++ if (num_copies == 1)
++ break;
+
-+ 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;
++ mirror_num++;
++ if (mirror_num > num_copies)
++ break;
+ }
-+ return fd;
++ return eb;
+}
+
-+static u64 parse_size(char *s)
++static void print_usage(void)
+{
-+ 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;
++ 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);
+}
+
-+int do_defrag(int ac, char **av)
++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)
+{
-+ int fd;
-+ int compress = 0;
-+ int flush = 0;
-+ u64 start = 0;
-+ u64 len = (u64)-1;
-+ u32 thresh = 0;
-+ int i;
-+ int errors = 0;
++ 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 verbose = 0;
-+ int fancy_ioctl = 0;
-+ struct btrfs_ioctl_defrag_range_args range;
++ int option_index = 0;
++ int copy = 0;
++ u64 bytes = 0;
++ int out_fd = 0;
++ int err;
+
-+ optind = 1;
+ while(1) {
-+ int c = getopt(ac, av, "vcfs:l:t:");
++ int c;
++ c = getopt_long(ac, av, "l:c:o:b:", long_options,
++ &option_index);
+ if (c < 0)
+ break;
+ switch(c) {
-+ case 'c':
-+ compress = 1;
-+ 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:
-+ fprintf(stderr, "Invalid arguments for defragment\n");
-+ free(av);
-+ return 1;
-+ }
-+ }
-+ if (ac - optind == 0) {
-+ fprintf(stderr, "Invalid arguments for defragment\n");
-+ free(av);
-+ return 1;
-+ }
-+
-+ memset(&range, 0, sizeof(range));
-+ range.start = start;
-+ range.len = len;
-+ range.extent_thresh = thresh;
-+ if (compress)
-+ range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS;
-+ if (flush)
-+ range.flags |= BTRFS_DEFRAG_RANGE_START_IO;
-+
-+ for (i = optind; i < ac; i++) {
-+ if (verbose)
-+ printf("%s\n", av[i]);
-+ fd = open_file_or_dir(av[i]);
-+ if (fd < 0) {
-+ fprintf(stderr, "failed to open %s\n", av[i]);
-+ perror("open:");
-+ errors++;
-+ continue;
-+ }
-+ if (!fancy_ioctl) {
-+ ret = ioctl(fd, BTRFS_IOC_DEFRAG, NULL);
-+ } else {
-+ ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &range);
-+ if (ret && errno == ENOTTY) {
-+ fprintf(stderr, "defrag range ioctl not "
-+ "supported in this kernel, please try "
-+ "without any options.\n");
-+ errors++;
++ case 'l':
++ logical = atoll(optarg);
++ if (logical == 0) {
++ fprintf(stderr,
++ "invalid extent number\n");
++ print_usage();
++ }
+ break;
-+ }
-+ }
-+ if (ret) {
-+ fprintf(stderr, "ioctl failed on %s ret %d errno %d\n",
-+ av[i], ret, errno);
-+ errors++;
++ 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();
+ }
-+ close(fd);
-+ }
-+ if (verbose)
-+ printf("%s\n", BTRFS_BUILD_VERSION);
-+ if (errors) {
-+ fprintf(stderr, "total %d failures\n", errors);
-+ exit(1);
+ }
++ ac = ac - optind;
++ if (ac == 0)
++ print_usage();
++ if (logical == 0)
++ print_usage();
++ if (copy < 0)
++ print_usage();
+
-+ free(av);
-+ return errors + 20;
-+}
++ dev = av[optind];
+
-+int do_find_newer(int argc, char **argv)
-+{
-+ int fd;
-+ int ret;
-+ char *subvol;
-+ u64 last_gen;
-+
-+ subvol = argv[1];
-+ last_gen = atoll(argv[2]);
++ radix_tree_init();
++ cache_tree_init(&root_cache);
+
-+ 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;
++ root = open_ctree(dev, 0, 0);
++ if (!root) {
++ fprintf(stderr, "Open ctree failed\n");
++ exit(1);
+ }
+
-+ fd = open_file_or_dir(subvol);
-+ if (fd < 0) {
-+ fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
-+ return 12;
++ 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;
++ }
+ }
-+ ret = find_updated_files(fd, 0, last_gen);
-+ if (ret)
-+ return 19;
-+ return 0;
-+}
+
-+int do_subvol_list(int argc, char **argv)
-+{
-+ int fd;
-+ int ret;
-+ char *subvol;
++ if (bytes == 0)
++ bytes = root->sectorsize;
+
-+ subvol = argv[1];
++ bytes = (bytes + root->sectorsize - 1) / root->sectorsize;
++ bytes *= root->sectorsize;
+
-+ 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;
++ 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;
+ }
+
-+ fd = open_file_or_dir(subvol);
-+ if (fd < 0) {
-+ fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
-+ return 12;
-+ }
-+ ret = list_subvols(fd);
-+ if (ret)
-+ return 19;
-+ return 0;
++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.
++ */
+
-+int do_clone(int argc, char **argv)
-+{
-+ char *subvol, *dst;
-+ int res, fd, fddst, len;
-+ char *newname;
-+ char *dstdir;
-+
-+ subvol = argv[1];
-+ dst = argv[2];
-+ struct btrfs_ioctl_vol_args args;
++#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"
+
-+ 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;
-+ }
++static void print_usage(void)
++{
++ fprintf(stderr, "usage: btrfs-select-super -s number dev\n");
++ fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION);
++ exit(1);
++}
+
-+ res = test_isdir(dst);
-+ if(res == 0 ){
-+ fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
-+ return 12;
-+ }
++int main(int ac, char **av)
++{
++ struct btrfs_root *root;
++ int ret;
++ int num;
++ u64 bytenr = 0;
+
-+ if(res>0){
-+ newname = strdup(subvol);
-+ newname = basename(newname);
-+ dstdir = dst;
-+ }else{
-+ newname = strdup(dst);
-+ newname = basename(newname);
-+ dstdir = strdup(dst);
-+ dstdir = dirname(dstdir);
++ 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( !strcmp(newname,".") || !strcmp(newname,"..") ||
-+ strchr(newname, '/') ){
-+ fprintf(stderr, "ERROR: incorrect snapshot name ('%s')\n",
-+ newname);
-+ return 14;
-+ }
++ if (ac != 1)
++ print_usage();
+
-+ len = strlen(newname);
-+ if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
-+ fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
-+ newname);
-+ return 14;
++ if (bytenr == 0) {
++ fprintf(stderr, "Please select the super copy with -s\n");
++ print_usage();
+ }
+
-+ fddst = open_file_or_dir(dstdir);
-+ if (fddst < 0) {
-+ fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
-+ return 12;
-+ }
++ radix_tree_init();
+
-+ fd = open_file_or_dir(subvol);
-+ if (fd < 0) {
-+ close(fddst);
-+ fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
-+ return 12;
++ 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;
+ }
+
-+ printf("Create a snapshot of '%s' in '%s/%s'\n",
-+ subvol, dstdir, newname);
-+ args.fd = fd;
-+ strcpy(args.name, newname);
-+ res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE, &args);
-+
-+ close(fd);
-+ close(fddst);
++ root = open_ctree(av[optind], bytenr, 1);
+
-+ if(res < 0 ){
-+ fprintf( stderr, "ERROR: cannot snapshot '%s'\n",subvol);
-+ return 11;
-+ }
++ if (root == NULL)
++ return 1;
+
-+ return 0;
++ /* 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");
+
-+int do_delete_subvolume(int argc, char **argv)
-+{
-+ int res, fd, len;
-+ struct btrfs_ioctl_vol_args args;
-+ char *dname, *vname, *cpath;
-+ char *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;
-+ }
+ 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");
+
-+ len = strlen(vname);
-+ if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
-+ fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
-+ vname);
-+ return 14;
-+ }
+ 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;
+
-+ fd = open_file_or_dir(dname);
-+ if (fd < 0) {
-+ close(fd);
-+ fprintf(stderr, "ERROR: can't access to '%s'\n", dname);
-+ return 12;
-+ }
++ 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.
++ */
+
-+ printf("Delete subvolume '%s/%s'\n", dname, vname);
-+ strcpy(args.name, vname);
-+ res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
++#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"
+
-+ close(fd);
++static void print_usage(void)
++{
++ fprintf(stderr, "usage: btrfs-zero-log dev\n");
++ fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION);
++ exit(1);
++}
+
-+ if(res < 0 ){
-+ fprintf( stderr, "ERROR: cannot delete '%s/%s'\n",dname, vname);
-+ return 11;
++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;
+ }
+
-+ return 0;
++ 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.
++ */
+
-+int do_create_subvol(int argc, char **argv)
-+{
-+ int res, fddst, len;
-+ char *newname;
-+ char *dstdir;
-+ struct btrfs_ioctl_vol_args args;
-+ char *dst = argv[1];
++#define _GNU_SOURCE
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
+
-+ res = test_isdir(dst);
-+ if(res >= 0 ){
-+ fprintf(stderr, "ERROR: '%s' exists\n", dst);
-+ return 12;
-+ }
++#include "commands.h"
++#include "version.h"
+
-+ newname = strdup(dst);
-+ newname = basename(newname);
-+ dstdir = strdup(dst);
-+ dstdir = dirname(dstdir);
++static const char * const btrfs_cmd_group_usage[] = {
++ "btrfs [--help] [--version] <group> [<group>...] <command> [<args>]",
++ NULL
++};
+
-+ if( !strcmp(newname,".") || !strcmp(newname,"..") ||
-+ strchr(newname, '/') ){
-+ fprintf(stderr, "ERROR: uncorrect subvolume name ('%s')\n",
-+ newname);
-+ return 14;
-+ }
++static const char btrfs_cmd_group_info[] =
++ "Use --help as an argument for information on a specific group or command.";
+
-+ len = strlen(newname);
-+ if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
-+ fprintf(stderr, "ERROR: subvolume name too long ('%s)\n",
-+ newname);
-+ return 14;
-+ }
++char argv0_buf[ARGV0_BUF_SIZE] = "btrfs";
+
-+ fddst = open_file_or_dir(dstdir);
-+ if (fddst < 0) {
-+ fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
-+ return 12;
-+ }
++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;
++}
+
-+ printf("Create subvolume '%s/%s'\n", dstdir, newname);
-+ strcpy(args.name, newname);
-+ res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
++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;
++}
+
-+ close(fddst);
++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;
+
-+ if(res < 0 ){
-+ fprintf( stderr, "ERROR: cannot create subvolume\n");
-+ return 11;
++ *cmd_ret = cmd;
++ return 0;
+ }
+
-+ return 0;
++ if (ambiguous_cmd)
++ return -2;
++
++ if (abbrev_cmd) {
++ *cmd_ret = abbrev_cmd;
++ return 0;
++ }
+
++ return -1;
+}
+
-+int do_fssync(int argc, char **argv)
++static const struct cmd_struct *
++parse_command_token(const char *arg, const struct cmd_group *grp)
+{
-+ int fd, res;
-+ char *path = argv[1];
++ const struct cmd_struct *cmd;
+
-+ 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);
-+ close(fd);
-+ if( res < 0 ){
-+ fprintf(stderr, "ERROR: unable to fs-syncing '%s'\n", path);
-+ return 16;
++ switch(parse_one_token(arg, grp, &cmd)) {
++ case -1:
++ help_unknown_token(arg, grp);
++ case -2:
++ help_ambiguous_token(arg, grp);
+ }
+
-+ return 0;
++ return cmd;
+}
+
-+int do_scan(int argc, char **argv)
++void handle_help_options_next_level(const struct cmd_struct *cmd,
++ int argc, char **argv)
+{
-+ int i, fd;
-+ if(argc<=1){
-+ int ret;
++ if (argc < 2)
++ return;
+
-+ printf("Scanning for Btrfs filesystems\n");
-+ ret = btrfs_scan_one_dir("/dev", 1);
-+ if (ret){
-+ fprintf(stderr, "ERROR: error %d while scanning\n", ret);
-+ return 18;
++ if (!strcmp(argv[1], "--help")) {
++ if (cmd->next) {
++ argc--;
++ argv++;
++ help_command_group(cmd->next, argc, argv);
++ } else {
++ usage_command(cmd, 1, 0);
+ }
-+ return 0;
-+ }
+
-+ fd = open("/dev/btrfs-control", O_RDWR);
-+ if (fd < 0) {
-+ perror("failed to open /dev/btrfs-control");
-+ return 10;
++ exit(0);
+ }
++}
+
-+ for( i = 1 ; i < argc ; i++ ){
-+ struct btrfs_ioctl_vol_args args;
-+ int ret;
++static void fixup_argv0(char **argv, const char *token)
++{
++ int len = strlen(argv0_buf);
+
-+ printf("Scanning for Btrfs filesystems in '%s'\n", argv[i]);
++ snprintf(argv0_buf + len, sizeof(argv0_buf) - len, " %s", token);
++ argv[0] = argv0_buf;
++}
+
-+ strcpy(args.name, argv[i]);
-+ /*
-+ * 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);
++int handle_command_group(const struct cmd_group *grp, int argc,
++ char **argv)
+
-+ if( ret < 0 ){
-+ close(fd);
-+ fprintf(stderr, "ERROR: unable to scan the device '%s'\n", argv[i]);
-+ return 11;
-+ }
++{
++ const struct cmd_struct *cmd;
++
++ argc--;
++ argv++;
++ if (argc < 1) {
++ usage_command_group(grp, 0, 0);
++ exit(1);
+ }
+
-+ close(fd);
-+ return 0;
++ 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 do_resize(int argc, char **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);
+
-+ struct btrfs_ioctl_vol_args args;
-+ int fd, res, len;
-+ char *amount=argv[1], *path=argv[2];
++ return nargs != expected;
++}
+
-+ 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;
++int check_argc_min(int nargs, int expected)
++{
++ if (nargs < expected) {
++ fprintf(stderr, "%s: too few arguments\n", argv0_buf);
++ return 1;
+ }
+
-+ printf("Resize '%s' of '%s'\n", path, amount);
-+ strcpy(args.name, amount);
-+ res = ioctl(fd, BTRFS_IOC_RESIZE, &args);
-+ close(fd);
-+ if( res < 0 ){
-+ fprintf(stderr, "ERROR: unable to resize '%s'\n", path);
-+ return 30;
-+ }
+ return 0;
+}
+
-+static int uuid_search(struct btrfs_fs_devices *fs_devices, char *search)
++int check_argc_max(int nargs, int expected)
+{
-+ 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;
++ if (nargs > expected) {
++ fprintf(stderr, "%s: too many arguments\n", argv0_buf);
++ 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);
++const struct cmd_group btrfs_cmd_group;
+
-+ 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_help_usage[] = {
++ "btrfs help [--full]",
++ "Dislay help information",
++ "",
++ "--full display detailed help on every command",
++ NULL
++};
+
-+int do_show_filesystem(int argc, char **argv)
++static int cmd_help(int argc, char **argv)
+{
-+ struct list_head *all_uuids;
-+ struct btrfs_fs_devices *fs_devices;
-+ struct list_head *cur_uuid;
-+ char *search = argv[1];
-+ int ret;
++ help_command_group(&btrfs_cmd_group, argc, argv);
++ return 0;
++}
+
-+ ret = btrfs_scan_one_dir("/dev", 0);
-+ if (ret){
-+ fprintf(stderr, "ERROR: error %d while scanning\n", ret);
-+ return 18;
-+ }
++static const char * const cmd_version_usage[] = {
++ "btrfs version",
++ "Display btrfs-progs version",
++ NULL
++};
+
-+ 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);
-+ }
++static int cmd_version(int argc, char **argv)
++{
+ printf("%s\n", BTRFS_BUILD_VERSION);
+ return 0;
+}
+
-+int do_add_volume(int nargs, char **args)
++static int handle_options(int *argc, char ***argv)
+{
++ char **orig_argv = *argv;
+
-+ char *mntpnt = args[nargs-1];
-+ int i, fdmnt, ret=0;
-+
-+
-+ 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 < (nargs-1) ; i++ ){
-+ struct btrfs_ioctl_vol_args ioctl_args;
-+ int devfd, res;
-+ u64 dev_block_count = 0;
-+ struct stat st;
-+
-+ devfd = open(args[i], O_RDWR);
-+ if (!devfd) {
-+ fprintf(stderr, "ERROR: Unable to open device '%s'\n", args[i]);
-+ close(devfd);
-+ ret++;
-+ continue;
-+ }
-+ ret = fstat(devfd, &st);
-+ if (ret) {
-+ fprintf(stderr, "ERROR: Unable to stat '%s'\n", args[i]);
-+ close(devfd);
-+ ret++;
-+ continue;
-+ }
-+ if (!S_ISBLK(st.st_mode)) {
-+ fprintf(stderr, "ERROR: '%s' is not a block device\n", args[i]);
-+ close(devfd);
-+ ret++;
-+ continue;
-+ }
-+
-+ res = btrfs_prepare_device(devfd, args[i], 1, &dev_block_count);
-+ if (res) {
-+ fprintf(stderr, "ERROR: Unable to init '%s'\n", args[i]);
-+ close(devfd);
-+ ret++;
-+ continue;
-+ }
-+ close(devfd);
++ while (*argc > 0) {
++ const char *arg = (*argv)[0];
++ if (arg[0] != '-')
++ break;
+
-+ strcpy(ioctl_args.name, args[i]);
-+ res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args);
-+ if(res<0){
-+ fprintf(stderr, "ERROR: error adding the device '%s'\n", args[i]);
-+ ret++;
++ 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)--;
+ }
+
-+ close(fdmnt);
-+ if( ret)
-+ return ret+20;
-+ else
-+ return 0;
-+
++ return (*argv) - orig_argv;
+}
+
-+int do_balance(int argc, char **argv)
-+{
-+
-+ int fdmnt, ret=0;
-+ struct btrfs_ioctl_vol_args args;
-+ char *path = argv[1];
-+
-+ fdmnt = open_file_or_dir(path);
-+ if (fdmnt < 0) {
-+ fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-+ return 12;
-+ }
-+
-+ memset(&args, 0, sizeof(args));
-+ ret = ioctl(fdmnt, BTRFS_IOC_BALANCE, &args);
-+ close(fdmnt);
-+ if(ret<0){
-+ fprintf(stderr, "ERROR: balancing '%s'\n", path);
++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 }
++ },
++};
+
-+ return 19;
-+ }
-+ return 0;
-+}
-+int do_remove_volume(int nargs, char **args)
++int main(int argc, char **argv)
+{
-+
-+ char *mntpnt = args[nargs-1];
-+ int i, fdmnt, ret=0;
-+
-+ fdmnt = open_file_or_dir(mntpnt);
-+ if (fdmnt < 0) {
-+ fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt);
-+ return 12;
++ 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);
+ }
+
-+ for(i=1 ; i < (nargs-1) ; i++ ){
-+ struct btrfs_ioctl_vol_args arg;
-+ int res;
++ cmd = parse_command_token(argv[0], &btrfs_cmd_group);
+
-+ strcpy(arg.name, args[i]);
-+ res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg);
-+ if(res<0){
-+ fprintf(stderr, "ERROR: error removing the device '%s'\n", args[i]);
-+ ret++;
-+ }
-+ }
++ handle_help_options_next_level(cmd, argc, argv);
+
-+ close(fdmnt);
-+ if( ret)
-+ return ret+20;
-+ else
-+ return 0;
++ 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;
++};
+
-+int do_set_default_subvol(int nargs, char **argv)
+ 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)
+{
-+ int ret=0, fd;
-+ u64 objectid;
-+ char *path = argv[2];
-+ char *subvolid = argv[1];
-+
-+ 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);
-+ close(fd);
-+ if( ret < 0 ){
-+ fprintf(stderr, "ERROR: unable to set a new default subvolume\n");
-+ return 30;
-+ }
-+ return 0;
-+}
-+
-+int do_df_filesystem(int nargs, char **argv)
-+{
-+ struct btrfs_ioctl_space_args *sargs;
-+ u64 count = 0, i;
-+ int ret;
-+ int fd;
-+ char *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);
-+ if (ret) {
-+ 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);
-+ if (ret) {
-+ 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) {
-+ 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;
-+}
-diff --git a/btrfs_cmds.h b/btrfs_cmds.h
-new file mode 100644
-index 0000000..7bde191
---- /dev/null
-+++ b/btrfs_cmds.h
-@@ -0,0 +1,34 @@
-+/*
-+ * 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.
-+ */
-+
-+/* btrfs_cmds.c*/
-+int do_clone(int nargs, char **argv);
-+int do_delete_subvolume(int nargs, char **argv);
-+int do_create_subvol(int nargs, char **argv);
-+int do_fssync(int nargs, char **argv);
-+int do_defrag(int argc, char **argv);
-+int do_show_filesystem(int nargs, char **argv);
-+int do_add_volume(int nargs, char **args);
-+int do_balance(int nargs, char **argv);
-+int do_remove_volume(int nargs, char **args);
-+int do_scan(int nargs, char **argv);
-+int do_resize(int nargs, char **argv);
-+int do_subvol_list(int nargs, char **argv);
-+int do_set_default_subvol(int nargs, char **argv);
-+int list_subvols(int fd);
-+int do_df_filesystem(int nargs, char **argv);
-+int find_updated_files(int fd, u64 root_id, u64 oldest_gen);
-+int do_find_newer(int argc, char **argv);
-diff --git a/btrfsck.c b/btrfsck.c
-index 40c90f8..63e44d1 100644
---- a/btrfsck.c
-+++ b/btrfsck.c
-@@ -20,7 +20,9 @@
- #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"
-@@ -28,6 +30,7 @@
- #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 +39,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;
-@@ -100,7 +103,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 +151,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 +181,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 +289,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;
-+}
++ 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 +348,7 @@ static void maybe_free_inode_rec(struct cache_tree *inode_cache,
+@@ -309,8 +355,7 @@ static void maybe_free_inode_rec(struct cache_tree *inode_cache,
}
BUG_ON(rec->refs != 1);
@@ -2750,7 +2512,7 @@ index 40c90f8..63e44d1 100644
cache = find_cache_extent(inode_cache, rec->ino, 1);
node = container_of(cache, struct ptr_node, cache);
BUG_ON(node->data != rec);
-@@ -338,14 +376,12 @@ static int check_orphan_item(struct btrfs_root *root, u64 ino)
+@@ -338,14 +383,12 @@ static int check_orphan_item(struct btrfs_root *root, u64 ino)
return ret;
}
@@ -2766,7 +2528,7 @@ index 40c90f8..63e44d1 100644
rec = active_node->current;
BUG_ON(rec->ino != key->objectid || rec->refs > 1);
-@@ -361,11 +397,8 @@ static int process_inode_item(struct btrfs_root *root,
+@@ -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;
@@ -2780,7 +2542,7 @@ index 40c90f8..63e44d1 100644
maybe_free_inode_rec(&active_node->inode_cache, rec);
return 0;
}
-@@ -391,7 +424,6 @@ static struct inode_backref *get_inode_backref(struct inode_record *rec,
+@@ -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);
@@ -2788,7 +2550,7 @@ index 40c90f8..63e44d1 100644
return backref;
}
-@@ -419,6 +451,7 @@ static int add_inode_backref(struct cache_tree *inode_cache,
+@@ -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) {
@@ -2796,7 +2558,7 @@ index 40c90f8..63e44d1 100644
if (backref->found_dir_item)
backref->errors |= REF_ERR_DUP_DIR_ITEM;
if (backref->found_dir_index && backref->filetype != filetype)
-@@ -443,10 +476,10 @@ static int add_inode_backref(struct cache_tree *inode_cache,
+@@ -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,
@@ -2809,7 +2571,7 @@ index 40c90f8..63e44d1 100644
dst->merging = 1;
list_for_each_entry(backref, &src->backrefs, list) {
-@@ -457,6 +490,7 @@ static int merge_inode_recs(struct inode_record *src, struct inode_record *dst,
+@@ -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) {
@@ -2817,7 +2579,7 @@ index 40c90f8..63e44d1 100644
add_inode_backref(dst_cache, dst->ino,
backref->dir, 0, backref->name,
backref->namelen, backref->filetype,
-@@ -481,6 +515,8 @@ static int merge_inode_recs(struct inode_record *src, struct inode_record *dst,
+@@ -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;
@@ -2826,7 +2588,7 @@ index 40c90f8..63e44d1 100644
dst->found_size += src->found_size;
if (src->extent_start != (u64)-1) {
if (dst->extent_start == (u64)-1) {
-@@ -510,14 +546,8 @@ static int merge_inode_recs(struct inode_record *src, struct inode_record *dst,
+@@ -510,14 +553,8 @@ static int merge_inode_recs(struct inode_record *src, struct inode_record *dst,
dst->errors |= I_ERR_DUP_INODE_ITEM;
}
}
@@ -2842,7 +2604,7 @@ index 40c90f8..63e44d1 100644
return 0;
}
-@@ -537,8 +567,9 @@ static int splice_shared_node(struct shared_node *src_node,
+@@ -537,8 +574,9 @@ static int splice_shared_node(struct shared_node *src_node,
if (src_node->current)
current_ino = src_node->current->ino;
@@ -2854,7 +2616,7 @@ index 40c90f8..63e44d1 100644
cache = find_first_cache_extent(src, 0);
while (cache) {
node = container_of(cache, struct ptr_node, cache);
-@@ -558,13 +589,26 @@ static int splice_shared_node(struct shared_node *src_node,
+@@ -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);
@@ -2882,7 +2644,7 @@ index 40c90f8..63e44d1 100644
if (current_ino > 0 && (!dst_node->current ||
current_ino > dst_node->current->ino)) {
if (dst_node->current) {
-@@ -616,6 +660,7 @@ static int add_shared_node(struct cache_tree *shared, u64 bytenr, u32 refs)
+@@ -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;
@@ -2890,7 +2652,7 @@ index 40c90f8..63e44d1 100644
cache_tree_init(&node->inode_cache);
node->refs = refs;
-@@ -646,6 +691,7 @@ static int enter_shared_node(struct btrfs_root *root, u64 bytenr, u32 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) {
@@ -2898,7 +2660,7 @@ index 40c90f8..63e44d1 100644
free_inode_recs(&node->inode_cache);
remove_cache_extent(&wc->shared, &node->cache);
free(node);
-@@ -708,10 +754,12 @@ static int process_dir_item(struct extent_buffer *eb,
+@@ -708,10 +761,12 @@ static int process_dir_item(struct extent_buffer *eb,
int filetype;
struct btrfs_dir_item *di;
struct inode_record *rec;
@@ -2911,7 +2673,7 @@ index 40c90f8..63e44d1 100644
inode_cache = &active_node->inode_cache;
rec = active_node->current;
rec->found_dir_item = 1;
-@@ -740,7 +788,9 @@ static int process_dir_item(struct extent_buffer *eb,
+@@ -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) {
@@ -2922,7 +2684,16 @@ index 40c90f8..63e44d1 100644
} else {
fprintf(stderr, "warning line %d\n", __LINE__);
}
-@@ -977,8 +1027,7 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb,
+@@ -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:
@@ -2932,7 +2703,37 @@ index 40c90f8..63e44d1 100644
break;
case BTRFS_EXTENT_DATA_KEY:
ret = process_file_extent(root, eb, i, &key,
-@@ -1120,7 +1169,7 @@ static int check_root_dir(struct inode_record *rec)
+@@ -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;
@@ -2941,7 +2742,7 @@ index 40c90f8..63e44d1 100644
goto out;
if (list_empty(&rec->backrefs))
goto out;
-@@ -1176,13 +1225,23 @@ static int check_inode_recs(struct btrfs_root *root,
+@@ -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);
@@ -2966,7 +2767,7 @@ index 40c90f8..63e44d1 100644
error++;
if (!rec->found_inode_item)
rec->errors |= I_ERR_NO_INODE_ITEM;
-@@ -1205,13 +1264,314 @@ static int check_inode_recs(struct btrfs_root *root,
+@@ -1205,13 +1274,314 @@ static int check_inode_recs(struct btrfs_root *root,
backref->namelen, backref->name,
backref->filetype, backref->errors);
}
@@ -3282,7 +3083,7 @@ index 40c90f8..63e44d1 100644
struct walk_control *wc)
{
int ret = 0;
-@@ -1219,10 +1579,18 @@ static int check_fs_root(struct btrfs_root *root,
+@@ -1219,10 +1589,18 @@ static int check_fs_root(struct btrfs_root *root,
int level;
struct btrfs_path path;
struct shared_node root_node;
@@ -3301,7 +3102,7 @@ index 40c90f8..63e44d1 100644
cache_tree_init(&root_node.inode_cache);
level = btrfs_header_level(root->node);
-@@ -1266,6 +1634,8 @@ static int check_fs_root(struct btrfs_root *root,
+@@ -1266,6 +1644,8 @@ static int check_fs_root(struct btrfs_root *root,
}
btrfs_release_path(root, &path);
@@ -3310,7 +3111,7 @@ index 40c90f8..63e44d1 100644
if (root_node.current) {
root_node.current->checked = 1;
maybe_free_inode_rec(&root_node.inode_cache,
-@@ -1280,13 +1650,15 @@ static int fs_root_objectid(u64 objectid)
+@@ -1280,13 +1660,15 @@ static int fs_root_objectid(u64 objectid)
{
if (objectid == BTRFS_FS_TREE_OBJECTID ||
objectid == BTRFS_TREE_RELOC_OBJECTID ||
@@ -3328,7 +3129,7 @@ index 40c90f8..63e44d1 100644
{
struct btrfs_path path;
struct btrfs_key key;
-@@ -1319,10 +1691,14 @@ static int check_fs_roots(struct btrfs_root *root)
+@@ -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);
@@ -3344,976 +3145,12487 @@ index 40c90f8..63e44d1 100644
}
path.slots[0]++;
}
-@@ -1895,7 +2271,6 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
- return 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);
-
- static int add_pending(struct cache_tree *pending,
- struct cache_tree *seen, u64 bytenr, u32 size)
+- 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)
{
-@@ -2443,14 +2818,45 @@ static void print_usage(void)
+ 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;
- int main(int ac, char **av)
- {
-+ struct cache_tree root_cache;
- struct btrfs_root *root;
-+ u64 bytenr = 0;
- int ret;
-+ int num;
+@@ -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 (ac < 2)
-+ 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();
-+ }
+ 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;
+ }
-+ ac = ac - optind;
++ return is_extent;
++}
+
-+ if (ac != 1)
- print_usage();
-
- radix_tree_init();
-- root = open_ctree(av[1], 0, 0);
-+ cache_tree_init(&root_cache);
+
-+ 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;
-+ }
++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);
++}
+
-+ root = open_ctree(av[optind], bytenr, 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;
- if (root == NULL)
+ cache = find_cache_extent(extent_cache, buf->start, buf->len);
+ if (!cache)
return 1;
-@@ -2458,10 +2864,15 @@ int main(int ac, char **av)
- ret = check_extents(root);
- if (ret)
- goto out;
-- ret = check_fs_roots(root);
-+ ret = check_fs_roots(root, &root_cache);
-+ if (ret)
-+ goto out;
-
-+ ret = check_root_refs(root, &root_cache);
- out:
-+ free_root_recs(&root_cache);
- close_ctree(root);
+ 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);
+
- if (found_old_backref) {
- /*
- * there was a disk format change when mixed
-diff --git a/btrfsctl.c b/btrfsctl.c
-index b323818..92bdf39 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)
++ 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)
{
- 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);
+ 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;
}
-@@ -99,7 +103,9 @@ int main(int ac, char **av)
- int i;
- unsigned long command = 0;
- int len;
-+ char *pos;
- char *fullpath;
-+ u64 objectid = 0;
- if (ac == 2 && strcmp(av[1], "-a") == 0) {
- fprintf(stderr, "Scanning for Btrfs filesystems\n");
-@@ -158,6 +164,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 +206,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);
-+ }
-+ }
- }
+ 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;
}
- if (command == 0) {
-@@ -206,6 +244,9 @@ int main(int ac, char **av)
- 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", objectid);
-+ ret = ioctl(fd, command, &objectid);
- } else
- ret = ioctl(fd, command, &args);
- if (ret < 0) {
-@@ -219,8 +260,8 @@ int main(int ac, char **av)
+ 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();
}
- printf("%s\n", BTRFS_BUILD_VERSION);
- if (ret)
-- exit(0);
-- else
- exit(1);
-+
-+ return 0;
++ 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;
}
-diff --git a/convert.c b/convert.c
-index d2c9efa..d037c98 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;
+-
+ 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);
+ }
- 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);
+ ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item);
+ refs = btrfs_extent_refs(eb, ei);
-- bytes_used = btrfs_root_used(&root->root_item);
-- btrfs_set_root_used(&root->root_item, bytes_used + num_bytes);
--
- btrfs_release_path(root, &path);
+ add_extent_rec(extent_cache, NULL, key.objectid, key.offset,
+- refs, 0, 0, 0);
++ refs, 0, 0, 0, key.offset);
- ins_key.objectid = disk_bytenr;
-@@ -454,9 +450,6 @@ static int record_file_extent(struct btrfs_trans_handle *trans,
+ 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;
+ }
- btrfs_mark_buffer_dirty(leaf);
+@@ -2135,9 +2514,18 @@ static int run_next_block(struct btrfs_root *root,
-- 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)
-diff --git a/ctree.h b/ctree.h
-index a9062ea..b79e238 100644
---- a/ctree.h
-+++ b/ctree.h
-@@ -350,11 +350,13 @@ 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 (2ULL << 0)
+ /* 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);
- #define BTRFS_FEATURE_COMPAT_SUPP 0ULL
- #define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL
- #define BTRFS_FEATURE_INCOMPAT_SUPP \
-- BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF
-+ (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \
-+ BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL)
+ ret = btrfs_lookup_extent_info(NULL, root, bytenr, size, NULL, &flags);
++ if (ret < 0)
++ flags = BTRFS_BLOCK_FLAG_FULL_BACKREF;
- /*
- * A leaf is full of items. offset and size tell us where to find
-@@ -1047,6 +1049,7 @@ BTRFS_SETGET_STACK_FUNCS(block_group_flags,
+ if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) {
+ parent = bytenr;
+@@ -2148,6 +2536,8 @@ static int run_next_block(struct btrfs_root *root,
+ }
- /* 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);
+ ret = check_block(root, extent_cache, buf, flags);
++ if (ret)
++ goto out;
- /* struct btrfs_inode_item */
-@@ -1325,6 +1328,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);
+ 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);
-+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);
-@@ -1572,6 +1579,7 @@ static inline unsigned long btrfs_leaf_data(struct extent_buffer *l)
+- add_tree_backref(extent_cache, ptr, parent,
+- owner, 1);
++ add_tree_backref(extent_cache, ptr, parent, owner, 1);
- /* 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);
+ 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);
- static inline unsigned long btrfs_file_extent_inline_start(struct
- btrfs_file_extent_item *e)
-@@ -1588,18 +1596,30 @@ static inline u32 btrfs_file_extent_calc_inline_size(u32 datasize)
+ 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;
+ }
- 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,
-diff --git a/debug-tree.c b/debug-tree.c
-index 1d47519..0525354 100644
---- a/debug-tree.c
-+++ b/debug-tree.c
-@@ -116,19 +116,27 @@ int main(int ac, char **av)
- int ret;
- int slot;
- int extent_only = 0;
-+ int device_only = 0;
-+ u64 block_only = 0;
- struct btrfs_root *tree_root_scan;
-
- radix_tree_init();
+-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(©_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, ©_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) {
- int c;
-- c = getopt(ac, av, "e");
-+ c = getopt(ac, av, "deb:");
- if (c < 0)
++ fixed = 0;
+ cache = find_first_cache_extent(extent_cache, 0);
+ if (!cache)
break;
- switch(c) {
- case 'e':
- extent_only = 1;
- break;
-+ case 'd':
-+ device_only = 1;
-+ break;
-+ case 'b':
-+ block_only = atoll(optarg);
-+ break;
- default:
- print_usage();
+@@ -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;
++
}
-@@ -142,14 +150,37 @@ int main(int ac, char **av)
- fprintf(stderr, "unable to open %s\n", av[optind]);
- exit(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);
}
-+ if (block_only) {
-+ leaf = read_tree_block(root,
-+ block_only,
-+ root->leafsize, 0);
++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 at 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;
+
-+ if (leaf && btrfs_header_level(leaf) != 0) {
-+ free_extent_buffer(leaf);
-+ leaf = NULL;
-+ }
++ slot = path->slots[level] + 1;
+
-+ if (!leaf) {
-+ leaf = read_tree_block(root,
-+ block_only,
-+ root->nodesize, 0);
++ 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 (!leaf) {
-+ fprintf(stderr, "failed to read %llu\n", block_only);
-+ return 0;
++
++ 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;
+ }
-+ 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);
-+ root->fs_info->tree_root->node, 1);
-
- printf("chunk tree\n");
- btrfs_print_tree(root->fs_info->chunk_root,
-- root->fs_info->chunk_root->node);
-+ root->fs_info->chunk_root->node, 1);
- }
- tree_root_scan = root->fs_info->tree_root;
-
-@@ -175,7 +206,7 @@ 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));
-@@ -188,8 +219,9 @@ again:
- 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 +230,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 +239,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,13 +286,13 @@ 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);
-+ btrfs_print_tree(tree_root_scan, buf, 1);
- }
- }
- path.slots[0]++;
-@@ -275,7 +305,7 @@ again:
- goto again;
- }
-
-- if (extent_only)
-+ if (extent_only || device_only)
- return 0;
-
- printf("total bytes %llu\n",
-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..a6e1000 100644
---- a/disk-io.c
-+++ b/disk-io.c
-@@ -86,7 +86,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;
- }
-@@ -970,13 +970,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);
-diff --git a/ioctl-test.c b/ioctl-test.c
-new file mode 100644
-index 0000000..7cf3bc2
---- /dev/null
-+++ b/ioctl-test.c
-@@ -0,0 +1,36 @@
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include "kerncompat.h"
-+#include "ioctl.h"
++ ram_size = btrfs_file_extent_ram_bytes(leaf, fi);
++ outbuf = malloc(ram_size);
++ if (!outbuf) {
++ fprintf(stderr, "No memory\n");
++ return -1;
++ }
+
-+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,
-+ 0 };
++ ret = decompress(buf, outbuf, len, ram_size);
++ if (ret) {
++ free(outbuf);
++ return ret;
++ }
+
-+int main(int ac, char **av)
++ 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)
+{
-+ int i = 0;
-+ while(ioctls[i]) {
-+ printf("%lu\n" ,ioctls[i]);
-+ i++;
++ 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;
+}
+
-diff --git a/ioctl.h b/ioctl.h
-index a084f33..776d7a9 100644
---- a/ioctl.h
-+++ b/ioctl.h
-@@ -30,6 +30,108 @@ struct btrfs_ioctl_vol_args {
- char name[BTRFS_PATH_NAME_MAX + 1];
- };
-
-+struct btrfs_ioctl_search_key {
-+ /* which root are we searching. 0 is the tree of tree roots */
-+ __u64 tree_id;
++static int ask_to_continue(const char *file)
++{
++ char buf[2];
++ char *ret;
+
-+ /* keys returned will be >= min and <= max */
-+ __u64 min_objectid;
-+ __u64 max_objectid;
++ 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;
++ }
+
-+ /* keys returned will be >= min and <= max */
-+ __u64 min_offset;
-+ __u64 max_offset;
++ return 0;
++}
+
-+ /* 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;
++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;
+
-+ /*
-+ * how many items did userland ask for, and how many are we
-+ * returning
-+ */
-+ __u32 nr_items;
++ 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;
+
-+ /* align to 64 bits */
-+ __u32 unused;
++ 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;
++ }
+
-+ /* some extra for later */
-+ __u64 unused1;
-+ __u64 unused2;
-+ __u64 unused3;
-+ __u64 unused4;
-+};
++ 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];
++ }
+
-+struct btrfs_ioctl_search_header {
-+ __u64 transid;
-+ __u64 objectid;
-+ __u64 offset;
-+ __u32 type;
-+ __u32 len;
-+} __attribute__((may_alias));
++ 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;
++ }
+
-+#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];
-+};
++ 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]++;
++ }
+
-+#define BTRFS_INO_LOOKUP_PATH_MAX 4080
-+struct btrfs_ioctl_ino_lookup_args {
-+ __u64 treeid;
-+ __u64 objectid;
-+ char name[BTRFS_INO_LOOKUP_PATH_MAX];
-+};
++ btrfs_free_path(path);
++set_size:
++ if (found_size)
++ ftruncate(fd, (loff_t)found_size);
++ return 0;
++}
+
-+/* flags for the defrag range ioctl */
-+#define BTRFS_DEFRAG_RANGE_COMPRESS 1
-+#define BTRFS_DEFRAG_RANGE_START_IO 2
++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;
+
-+struct btrfs_ioctl_defrag_range_args {
-+ /* start of the defrag operation */
-+ __u64 start;
++ path = btrfs_alloc_path();
++ if (!path) {
++ fprintf(stderr, "Ran out of memory\n");
++ return -1;
++ }
++ path->skip_locking = 1;
+
-+ /* number of bytes to defrag, use (u64)-1 to say all */
-+ __u64 len;
++ key->offset = 0;
++ key->type = BTRFS_DIR_INDEX_KEY;
+
-+ /*
-+ * flags for the operation, which can include turning
-+ * on compression for this one defrag
-+ */
-+ __u64 flags;
++ 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;
++ }
+
-+ /*
-+ * 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;
++ 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];
++ }
+
-+ /* spare for later */
-+ __u32 unused[5];
-+};
++ 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;
++ }
+
-+struct btrfs_ioctl_space_info {
-+ __u64 flags;
-+ __u64 total_bytes;
-+ __u64 used_bytes;
-+};
++ 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);
+
-+struct btrfs_ioctl_space_args {
-+ __u64 space_slots;
-+ __u64 total_spaces;
-+ struct btrfs_ioctl_space_info spaces[0];
-+};
++ snprintf(path_name, 4096, "%s/%s", dir, filename);
+
- #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 +158,15 @@ 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)
- #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
++ /*
++ * 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;
++ }
+
- #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
++ 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;
++ }
+
- btrfsctl.8.gz: btrfsctl.8.in
- $(GZIP) -n -c btrfsctl.8.in > btrfsctl.8.gz
-
-diff --git a/man/btrfs.8.in b/man/btrfs.8.in
-new file mode 100644
-index 0000000..26ef982
---- /dev/null
-+++ b/man/btrfs.8.in
-@@ -0,0 +1,170 @@
-+.TH BTRFS 8 "" "btrfs" "btrfs"
-+.\"
-+.\" Man page written by Goffredo Baroncelli <kreijack at inwind.it> (Feb 2010)
-+.\"
-+.SH NAME
-+btrfs \- control a btrfs filesystem
-+.SH SYNOPSIS
-+\fBbtrfs\fP \fBsubvolume snapshot\fP\fI <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 <path>\fP
-+.PP
-+\fBbtrfs\fP \fBsubvolume set-default\fP\fI <id> <path>\fP
-+.PP
-+\fBbtrfs\fP \fBfilesystem defrag\fP\fI <file>|<dir> [<file>|<dir>...]\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 \fBdevice scan\fP\fI [<device> [<device>..]]\fP
-+.PP
-+\fBbtrfs\fP \fBdevice show\fP\fI <dev>|<label> [<dev>|<label>...]\fP
-+.PP
-+\fBbtrfs\fP \fBdevice balance\fP\fI <path> \fP
-+.PP
-+\fBbtrfs\fP \fBdevice add\fP\fI <dev> [<dev>..] <path> \fP
-+.PP
-+\fBbtrfs\fP \fBdevice delete\fP\fI <dev> [<dev>..] <path> \fP]
++ 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);
++ }
+
-+.PP
-+\fBbtrfs\fP \fBhelp|\-\-help|\-h \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.
++ /*
++ * 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;
++ }
+
-+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 dev s
-+is not allowed, because
-+.I dev s
-+may be interpreted both as
-+.I device show
-+and as
-+.I device scan.
-+In this case
-+.I btrfs
-+returns an error.
++ 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 a command is terminated by
-+.I --help
-+, the relevant help is showed. If the passed command matches more commands,
-+the help of all the matched commands are showed. For example
-+.I btrfs dev --help
-+shows the help of all
-+.I device*
-+command.
++ if (verbose)
++ printf("Done searching %s\n", dir);
++ btrfs_free_path(path);
++ return 0;
++}
+
-+.SH COMMANDS
-+.TP
++static void usage()
++{
++ fprintf(stderr, "Usage: restore [-svio] [-t disk offset] <device> "
++ "<directory>\n");
++}
+
-+\fBsubvolume snapshot\fR\fI <source> [<dest>/]<name>\fR
-+Create a writable 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.
-+.TP
++static struct btrfs_root *open_fs(const char *dev, u64 root_location, int super_mirror)
++{
++ struct btrfs_root *root;
++ u64 bytenr;
++ int i;
+
-+\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
++ 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");
++ }
+
-+\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
++ 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;
+
-+\fBsubvolume list\fR\fI <path>\fR
-+List the subvolumes present in the filesystem \fI<path>\fR. For every
-+subvolume is showed the subvolume ID (second column),
-+the ID of the \fItop level\fR
-+subvolume (fifth column), and the path (seventh column) relative to the
-+\fItop level\fR subvolume.
-+These <ID> may be used by the \fBsubvolume set-default\fR command, or at
-+mount time via the \fIsubvol=\fR option.
-+.TP
++ key.objectid = 0;
++ key.type = BTRFS_DIR_INDEX_KEY;
++ key.offset = 0;
+
-+\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 \fB<id>\fR, which
-+is returned by the \fBsubvolume list\fR command.
-+.TP
++ path = btrfs_alloc_path();
++ if (!path) {
++ fprintf(stderr, "Ran out of memory\n");
++ goto out;
++ }
+
-+\fBfilesystem defragment\fP\fI <file>|<dir> [<file>|<dir>...]\fR
-+Defragment files and/or directories.
-+.TP
++ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
++ if (ret < 0) {
++ fprintf(stderr, "Error searching %d\n", ret);
++ goto out;
++ }
+
-+\fBdevice scan\fR \fI[<device> [<device>..]]\fR
-+Scan devices for a btrfs filesystem. If no devices are passed, \fBbtrfs\fR scans
-+all the block devices.
-+.TP
++ 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;
+
-+\fBfilesystem sync\fR\fI <path> \fR
-+Force a sync for the filesystem identified by \fI<path>\fR.
-+.TP
++ 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;
++}
+
-+.\"
-+.\" Some wording are extracted by the resize2fs man page
-+.\"
++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;
+
-+\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.
++ 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 'max' is passed, the filesystem will occupy all available space on the
-+volume(s).
++ if (optind + 1 >= argc) {
++ usage();
++ exit(1);
++ }
+
-+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
++ 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;
++ }
+
-+\fBfilesystem show\fR [<uuid>|<label>]\fR
-+Show the btrfs filesystem with some additional info. If no UUID or label is
-+passed, \fBbtrfs\fR show info of all the btrfs filesystem.
-+.TP
++ root = open_fs(argv[optind], tree_location, super_mirror);
++ if (root == NULL)
++ return 1;
+
-+\fBdevice balance\fR \fI<path>\fR
-+Balance the chunks of the filesystem identified by \fI<path>\fR
-+across the devices.
-+.TP
++ 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;
++ }
++ }
+
-+\fBdevice add\fR\fI <dev> [<dev>..] <path>\fR
-+Add device(s) to the filesystem identified by \fI<path>\fR.
-+.TP
++ printf("Root objectid is %Lu\n", root->objectid);
+
-+\fBdevice delete\fR\fI <dev> [<dev>..] <path>\fR
-+Remove device(s) from a filesystem identified by \fI<path>\fR.
-+.PP
++ memset(path_name, 0, 4096);
+
-+.SH EXIT STATUS
-+\fBbtrfs\fR returns a zero exist status if it succeeds. Non zero is returned in
-+case of failure.
++ strncpy(dir_name, argv[optind + 1], 128);
+
-+.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/print-tree.c b/print-tree.c
-index 59f4358..ac575d5 100644
---- a/print-tree.c
-+++ b/print-tree.c
-@@ -413,8 +413,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");
++ /* Strip the trailing / on the dir name */
++ while (1) {
++ len = strlen(dir_name);
++ if (dir_name[len - 1] != '/')
+ break;
-+ }
-+ /* fall-thru */
- default:
- printf("%llu", objectid);
- }
-@@ -607,7 +610,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 +646,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;
++ dir_name[len - 1] = '\0';
++ }
+
- for (i = 0; i < nr; i++) {
- struct extent_buffer *next = read_tree_block(root,
- btrfs_node_blockptr(eb, i),
-@@ -660,8 +666,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;
++ 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..fd894f3 100644
+index 2f4c6e1..ee7fa1b 100644
--- a/utils.c
+++ b/utils.c
@@ -31,6 +31,10 @@
@@ -4327,7 +15639,139 @@ index 2f4c6e1..fd894f3 100644
#include "kerncompat.h"
#include "radix-tree.h"
#include "ctree.h"
-@@ -586,55 +590,224 @@ error:
+@@ -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;
}
@@ -4387,6 +15831,8 @@ index 2f4c6e1..fd894f3 100644
+ if(stat(a, &st_buf_a) < 0 ||
+ stat(b, &st_buf_b) < 0)
+ {
++ if (errno == ENOENT)
++ return 0;
+ return -errno;
+ }
+
@@ -4423,9 +15869,11 @@ index 2f4c6e1..fd894f3 100644
+
+ /* Resolve a if it is a loop device */
+ if((ret = is_loop_device(a)) < 0) {
-+ return ret;
-+ } else if(ret) {
-+ if((ret = resolve_loop_device(a, res_a, sizeof(res_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;
@@ -4434,9 +15882,11 @@ index 2f4c6e1..fd894f3 100644
+ }
+
+ /* Resolve b if it is a loop device */
-+ if((ret = is_loop_device(b)) < 0) {
-+ return ret;
-+ } else if(ret) {
++ 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;
+
@@ -4497,64 +15947,61 @@ index 2f4c6e1..fd894f3 100644
- 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;
-+ int fd;
+ u64 total_devs = 1;
+ int is_btrfs;
-+ struct btrfs_fs_devices* fs_devices_mnt = NULL;
++ struct btrfs_fs_devices *fs_devices_mnt = NULL;
FILE *f;
- int ret = 0;
+ struct mntent *mnt;
-
-- if ((f = setmntent ("/proc/mounts", "r")) == NULL)
-+ fd = open(file, O_RDONLY);
-+ if (fd < 0) {
-+ fprintf (stderr, "check_mounted(): Could not open %s\n", file);
- return -errno;
-+ }
-
-- if (stat(file, &st_buf) < 0) {
-- return -errno;
-- } else {
-- if (S_ISBLK(st_buf.st_mode)) {
-- file_rdev = st_buf.st_rdev;
-- } else {
-- file_dev = st_buf.st_dev;
-- file_ino = st_buf.st_ino;
-- }
++
+ /* scan the initial device */
+ ret = btrfs_scan_one_device(fd, file, &fs_devices_mnt,
+ &total_devs, BTRFS_SUPER_INFO_OFFSET);
+ is_btrfs = (ret >= 0);
-+ close(fd);
-+
+
+ /* 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;
++ }
+
- while ((mnt = getmntent (f)) != NULL) {
-- if (strcmp(file, mnt->mnt_fsname) == 0)
-- break;
++ /* 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;
-
-- 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;
-- }
++
+ ret = blk_file_in_dev_list(fs_devices_mnt, mnt->mnt_fsname);
-+ } else {
+ } 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)
@@ -4566,34 +16013,701 @@ index 2f4c6e1..fd894f3 100644
}
- }
-- if (mnt) {
-- /* found an entry in mnt table */
-- ret = 1;
+- while ((mnt = getmntent (f)) != NULL) {
+- if (strcmp(file, mnt->mnt_fsname) == 0)
+ if(ret < 0)
+ goto out_mntloop_err;
+ else if(ret)
-+ break;
+ 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? */
-+ ret = (mnt != NULL);
++ 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..9dce5b0 100644
+index 7ff542b..c5f55e1 100644
--- a/utils.h
+++ b/utils.h
-@@ -36,7 +36,7 @@ int btrfs_scan_for_fsid(struct btrfs_fs_devices *fs_devices, u64 total_devs,
+@@ -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-valgrind.patch b/btrfs-progs-valgrind.patch
index decc197..49f0e4b 100644
--- a/btrfs-progs-valgrind.patch
+++ b/btrfs-progs-valgrind.patch
@@ -1,21 +1,8 @@
-diff --git a/btrfsck.c b/btrfsck.c
-index 63e44d1..1ae7487 100644
---- a/btrfsck.c
-+++ b/btrfsck.c
-@@ -2806,6 +2806,8 @@ static int check_extents(struct btrfs_root *root)
- break;
- }
- ret = check_extent_refs(root, &extent_cache);
-+ free_cache_tree(&seen);
-+ free(bits);
- return ret;
- }
-
-diff --git a/disk-io.c b/disk-io.c
-index a6e1000..b903163 100644
---- a/disk-io.c
-+++ b/disk-io.c
-@@ -425,8 +425,10 @@ static int find_and_setup_log_root(struct btrfs_root *tree_root,
+diff -up btrfs-progs-0.19/btrfsck.c.orig btrfs-progs-0.19/btrfsck.c
+diff -up btrfs-progs-0.19/disk-io.c.orig btrfs-progs-0.19/disk-io.c
+--- btrfs-progs-0.19/disk-io.c.orig 2012-04-11 10:42:36.213042556 -0400
++++ btrfs-progs-0.19/disk-io.c 2012-04-11 10:42:58.790867701 -0400
+@@ -458,8 +458,10 @@ static int find_and_setup_log_root(struc
u64 blocknr = btrfs_super_log_root(disk_super);
struct btrfs_root *log_root = malloc(sizeof(struct btrfs_root));
@@ -27,7 +14,7 @@ index a6e1000..b903163 100644
blocksize = btrfs_level_size(tree_root,
btrfs_super_log_root_level(disk_super));
-@@ -605,7 +607,7 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
+@@ -622,7 +624,7 @@ static struct btrfs_fs_info *__open_ctre
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));
@@ -36,8 +23,8 @@ index a6e1000..b903163 100644
int ret;
struct btrfs_super_block *disk_super;
struct btrfs_fs_devices *fs_devices = NULL;
-@@ -628,7 +630,7 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
- BUG_ON(ret);
+@@ -646,7 +648,7 @@ static struct btrfs_fs_info *__open_ctre
+ goto out;
}
- memset(fs_info, 0, sizeof(*fs_info));
@@ -45,7 +32,7 @@ index a6e1000..b903163 100644
fs_info->tree_root = tree_root;
fs_info->extent_root = extent_root;
fs_info->chunk_root = chunk_root;
-@@ -928,15 +930,19 @@ static int close_all_devices(struct btrfs_fs_info *fs_info)
+@@ -1063,15 +1065,19 @@ static int close_all_devices(struct btrf
{
struct list_head *list;
struct list_head *next;
@@ -68,7 +55,7 @@ index a6e1000..b903163 100644
return 0;
}
-@@ -983,12 +989,14 @@ int close_ctree(struct btrfs_root *root)
+@@ -1121,12 +1127,14 @@ int close_ctree(struct btrfs_root *root)
extent_io_tree_cleanup(&fs_info->pinned_extents);
extent_io_tree_cleanup(&fs_info->pending_del);
extent_io_tree_cleanup(&fs_info->extent_ins);
@@ -83,11 +70,10 @@ index a6e1000..b903163 100644
free(fs_info);
return 0;
-diff --git a/extent-cache.c b/extent-cache.c
-index b871e18..b424975 100644
---- a/extent-cache.c
-+++ b/extent-cache.c
-@@ -170,3 +170,14 @@ void remove_cache_extent(struct cache_tree *tree,
+diff -up btrfs-progs-0.19/extent-cache.c.orig btrfs-progs-0.19/extent-cache.c
+--- btrfs-progs-0.19/extent-cache.c.orig 2012-04-11 10:42:36.214042548 -0400
++++ btrfs-progs-0.19/extent-cache.c 2012-04-11 10:42:58.833867368 -0400
+@@ -168,3 +168,14 @@ void remove_cache_extent(struct cache_tr
rb_erase(&pe->rb_node, &tree->root);
}
@@ -102,11 +88,10 @@ index b871e18..b424975 100644
+ free(cache);
+ }
+}
-diff --git a/extent-cache.h b/extent-cache.h
-index 7f2f2a6..1696bc2 100644
---- a/extent-cache.h
-+++ b/extent-cache.h
-@@ -43,6 +43,7 @@ struct cache_extent *find_cache_extent(struct cache_tree *tree,
+diff -up btrfs-progs-0.19/extent-cache.h.orig btrfs-progs-0.19/extent-cache.h
+--- btrfs-progs-0.19/extent-cache.h.orig 2009-06-11 12:56:15.000000000 -0400
++++ btrfs-progs-0.19/extent-cache.h 2012-04-11 10:42:58.850867237 -0400
+@@ -43,6 +43,7 @@ struct cache_extent *find_cache_extent(s
int insert_cache_extent(struct cache_tree *tree, u64 start, u64 size);
int insert_existing_cache_extent(struct cache_tree *tree,
struct cache_extent *pe);
@@ -114,11 +99,10 @@ index 7f2f2a6..1696bc2 100644
static inline int cache_tree_empty(struct cache_tree *tree)
{
-diff --git a/extent-tree.c b/extent-tree.c
-index b2f9bb2..e1d7ffd 100644
---- a/extent-tree.c
-+++ b/extent-tree.c
-@@ -2985,6 +2985,7 @@ out:
+diff -up btrfs-progs-0.19/extent-tree.c.orig btrfs-progs-0.19/extent-tree.c
+--- btrfs-progs-0.19/extent-tree.c.orig 2012-04-11 10:42:36.216042532 -0400
++++ btrfs-progs-0.19/extent-tree.c 2012-04-11 10:42:58.851867229 -0400
+@@ -2999,6 +2999,7 @@ out:
int btrfs_free_block_groups(struct btrfs_fs_info *info)
{
@@ -126,7 +110,7 @@ index b2f9bb2..e1d7ffd 100644
u64 start;
u64 end;
u64 ptr;
-@@ -3008,6 +3009,15 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
+@@ -3022,6 +3023,15 @@ int btrfs_free_block_groups(struct btrfs
clear_extent_dirty(&info->free_space_cache, start,
end, GFP_NOFS);
}
@@ -142,23 +126,10 @@ index b2f9bb2..e1d7ffd 100644
return 0;
}
-diff --git a/extent_io.c b/extent_io.c
-index 069c199..71e6826 100644
---- a/extent_io.c
-+++ b/extent_io.c
-@@ -572,6 +572,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;
-diff --git a/volumes.c b/volumes.c
-index 7671855..eee66a7 100644
---- a/volumes.c
-+++ b/volumes.c
-@@ -862,6 +862,20 @@ void btrfs_mapping_init(struct btrfs_mapping_tree *tree)
+diff -up btrfs-progs-0.19/volumes.c.orig btrfs-progs-0.19/volumes.c
+--- btrfs-progs-0.19/volumes.c.orig 2012-04-11 10:42:36.228042439 -0400
++++ btrfs-progs-0.19/volumes.c 2012-04-11 10:43:03.313832673 -0400
+@@ -957,6 +957,20 @@ void btrfs_mapping_init(struct btrfs_map
cache_tree_init(&tree->cache_tree);
}
@@ -179,7 +150,7 @@ index 7671855..eee66a7 100644
int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len)
{
struct cache_extent *ce;
-@@ -1340,7 +1354,7 @@ int btrfs_read_sys_array(struct btrfs_root *root)
+@@ -1484,7 +1498,7 @@ int btrfs_read_sys_array(struct btrfs_ro
if (!sb)
return -ENOMEM;
btrfs_set_buffer_uptodate(sb);
@@ -188,11 +159,10 @@ index 7671855..eee66a7 100644
array_size = btrfs_super_sys_array_size(super_copy);
/*
-diff --git a/volumes.h b/volumes.h
-index bb78751..e466b31 100644
---- a/volumes.h
-+++ b/volumes.h
-@@ -130,4 +130,5 @@ int btrfs_add_system_chunk(struct btrfs_trans_handle *trans,
+diff -up btrfs-progs-0.19/volumes.h.orig btrfs-progs-0.19/volumes.h
+--- btrfs-progs-0.19/volumes.h.orig 2012-04-11 10:42:36.228042439 -0400
++++ btrfs-progs-0.19/volumes.h 2012-04-11 10:43:03.314832666 -0400
+@@ -182,4 +182,5 @@ int btrfs_add_system_chunk(struct btrfs_
struct btrfs_root *root, struct btrfs_key *key,
struct btrfs_chunk *chunk, int item_size);
int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset);
diff --git a/btrfs-progs.spec b/btrfs-progs.spec
index 1a914e1..16620e2 100644
--- a/btrfs-progs.spec
+++ b/btrfs-progs.spec
@@ -1,6 +1,6 @@
Name: btrfs-progs
Version: 0.19
-Release: 17%{?dist}
+Release: 18%{?dist}
Summary: Userspace programs for btrfs
Group: System Environment/Base
@@ -9,9 +9,8 @@ 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-build-everything.patch
-Patch3: btrfs-progs-valgrind.patch
-Patch4: btrfs-progs-build-fixes.patch
+Patch2: btrfs-progs-valgrind.patch
+Patch3: btrfs-progs-build-fixes.patch
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildRequires: e2fsprogs-devel, libuuid-devel, zlib-devel, libacl-devel
@@ -28,15 +27,13 @@ check, modify and correct any inconsistencies in the btrfs filesystem.
%patch1 -p1
%patch2 -p1
%patch3 -p1
-%patch4 -p1
%build
make CFLAGS="$RPM_OPT_FLAGS" %{?_smp_mflags}
-make CFLAGS="$RPM_OPT_FLAGS" %{?_smp_mflags} LDFLAGS="-lcom_err" convert
%install
rm -rf $RPM_BUILD_ROOT
-make mandir=%{_mandir} bindir=%{_root_sbindir} install DESTDIR=$RPM_BUILD_ROOT
+make mandir=%{_mandir} bindir=%{_sbindir} install DESTDIR=$RPM_BUILD_ROOT
%clean
rm -rf $RPM_BUILD_ROOT
@@ -44,18 +41,20 @@ rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root,-)
%doc COPYING INSTALL
-%{_root_sbindir}/btrfsctl
-%{_root_sbindir}/btrfsck
-%{_root_sbindir}/mkfs.btrfs
-%{_root_sbindir}/btrfs-debug-tree
-%{_root_sbindir}/btrfs-image
-%{_root_sbindir}/btrfs-show
-%{_root_sbindir}/btrfs-vol
-%{_root_sbindir}/btrfs-convert
-%{_root_sbindir}/btrfstune
-%{_root_sbindir}/btrfs
-%{_root_sbindir}/btrfs-map-logical
-%{_root_sbindir}/btrfs-zero-log
+%{_sbindir}/btrfsctl
+%{_sbindir}/btrfsck
+%{_sbindir}/mkfs.btrfs
+%{_sbindir}/btrfs-debug-tree
+%{_sbindir}/btrfs-image
+%{_sbindir}/btrfs-show
+%{_sbindir}/btrfs-vol
+%{_sbindir}/btrfs-convert
+%{_sbindir}/btrfstune
+%{_sbindir}/btrfs
+%{_sbindir}/btrfs-map-logical
+%{_sbindir}/btrfs-zero-log
+%{_sbindir}/btrfs-restore
+%{_sbindir}/btrfs-find-root
%{_mandir}/man8/btrfs-image.8.gz
%{_mandir}/man8/btrfs-show.8.gz
%{_mandir}/man8/btrfsck.8.gz
@@ -64,6 +63,9 @@ rm -rf $RPM_BUILD_ROOT
%{_mandir}/man8/btrfs.8.gz
%changelog
+* Wed Apr 11 2012 Josef Bacik <josef at toxicpanda.com> 0.19-18
+- updated to latest btrfs-progs
+
* Thu Jan 12 2012 Fedora Release Engineering <rel-eng at lists.fedoraproject.org> - 0.19-17
- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
More information about the scm-commits
mailing list