All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots
@ 2012-09-18 10:35 Miao Xie
  2012-09-18 10:50 ` [PATCH V4 1/7] Btrfs-progs: move the function declarations to a new head file Miao Xie
                   ` (10 more replies)
  0 siblings, 11 replies; 31+ messages in thread
From: Miao Xie @ 2012-09-18 10:35 UTC (permalink / raw)
  To: Linux Btrfs

We want 'btrfs subvolume list' only to list readonly subvolumes, this patch set
introduces a new option 'r' to implement it.

You can use the command like that:

	btrfs subvolume list -r <mnt>

Changelog v3 -> v4:
- modify the check method which is used to check if btrfs_root_item contains otime and uuid
  or not.
- add filter set and comparer set which are used to manage the filters and comparers specified
  by the users.
- re-base the read-only subvolume list function.

Changelog v2 -> v3:
- re-implement list_subvols()
- re-implement this read-only subvolume list function based on the new list_subvols()

Changelog v1 -> v2:
- address the comments from Goffredo Baroncelli
  i,   change the changelog of the patches and make them more elaborate.
  ii,  move the function declarations to a new head file.
  iii, add the introduction of the new option 'r' into the man page

We can pull the patches from the URL

	git://github.com/miaoxie/btrfs-progs.git master

This patchset is against the patches of Liu Bo, Anand Jain and mine which were sent by several
days ago. And we also can pull those patches from the above URL.

Thanks
Miao
---
Miao Xie (5):
      Btrfs-progs: fix compile warning of implicit declaration of "list_snapshots"
      Btrfs-progs: fix wrong usage of btrfs subvolume list command
      Btrfs-progs: fix wrong way to check if the root item contains otime and uuid
      Btrfs-progs: restructure list_subvolumes
      Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots

Zhou Bo (2):
      Btrfs-progs: move the function declarations to a new head file
      Btrfs-progs: update the manpage entries for the btrfs subvolume list

 btrfs-list.c     | 1023 ++++++++++++++++++++++++++++++++----------------------
 btrfs-list.h     |   88 +++++
 cmds-inspect.c   |    6 +-
 cmds-subvolume.c |   78 ++++-
 ctree.h          |   17 +
 man/btrfs.8.in   |    5 +-
 send-utils.c     |    7 +-
 7 files changed, 786 insertions(+), 438 deletions(-)

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH V4 1/7] Btrfs-progs: move the function declarations to a new head file
  2012-09-18 10:35 [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
@ 2012-09-18 10:50 ` Miao Xie
  2012-09-18 10:52 ` [PATCH V4 2/7] Btrfs-progs: fix compile warning of implicit declaration of "list_snapshots" Miao Xie
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 31+ messages in thread
From: Miao Xie @ 2012-09-18 10:50 UTC (permalink / raw)
  To: Linux Btrfs

From: Zhou Bo <zhoub-fnst@cn.fujitsu.com>

Move the function declarations to a new head file.

Signed-off-by: Zhou Bo <zhoub-fnst@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
---
Changelog v3 -> v4:
- no change.

Changelog v2 -> v3:
- remove path_for_root() in send-utils.c

Changelog v1 -> v2:
- new patch.
---
 btrfs-list.c     |    1 +
 btrfs-list.h     |   21 +++++++++++++++++++++
 cmds-inspect.c   |    4 +---
 cmds-subvolume.c |    5 +----
 send-utils.c     |    4 +---
 5 files changed, 25 insertions(+), 10 deletions(-)
 create mode 100644 btrfs-list.h

diff --git a/btrfs-list.c b/btrfs-list.c
index 514cac7..ef621f0 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -35,6 +35,7 @@
 #include "transaction.h"
 #include "utils.h"
 #include <uuid/uuid.h>
+#include "btrfs-list.h"
 
 /* we store all the roots we find in an rbtree so that we can
  * search for them later.
diff --git a/btrfs-list.h b/btrfs-list.h
new file mode 100644
index 0000000..96f3641
--- /dev/null
+++ b/btrfs-list.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012 Fujitsu.  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 list_subvols(int fd, int print_parent, int get_default, int print_uuid);
+int find_updated_files(int fd, u64 root_id, u64 oldest_gen);
+char *path_for_root(int fd, u64 root);
diff --git a/cmds-inspect.c b/cmds-inspect.c
index 2f0228f..f943ed9 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -24,9 +24,7 @@
 #include "ioctl.h"
 
 #include "commands.h"
-
-/* btrfs-list.c */
-char *path_for_root(int fd, u64 root);
+#include "btrfs-list.h"
 
 static const char * const inspect_cmd_group_usage[] = {
 	"btrfs inspect-internal <command> <args>",
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index aaf5dde..ffc5d95 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -29,10 +29,7 @@
 #include "qgroup.h"
 
 #include "commands.h"
-
-/* btrfs-list.c */
-int list_subvols(int fd, int print_parent, int print_uuid, int get_default);
-int find_updated_files(int fd, u64 root_id, u64 oldest_gen);
+#include "btrfs-list.h"
 
 static const char * const subvolume_cmd_group_usage[] = {
 	"btrfs subvolume <command> <args>",
diff --git a/send-utils.c b/send-utils.c
index af495a0..096fa02 100644
--- a/send-utils.c
+++ b/send-utils.c
@@ -21,9 +21,7 @@
 #include "ctree.h"
 #include "send-utils.h"
 #include "ioctl.h"
-
-/* btrfs-list.c */
-char *path_for_root(int fd, u64 root);
+#include "btrfs-list.h"
 
 static struct rb_node *tree_insert(struct rb_root *root,
 				   struct subvol_info *si,
-- 
1.7.6.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH V4 2/7] Btrfs-progs: fix compile warning of implicit declaration of "list_snapshots"
  2012-09-18 10:35 [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
  2012-09-18 10:50 ` [PATCH V4 1/7] Btrfs-progs: move the function declarations to a new head file Miao Xie
@ 2012-09-18 10:52 ` Miao Xie
  2012-09-18 10:55 ` [PATCH V4 3/7] Btrfs-progs: fix wrong usage of btrfs subvolume list command Miao Xie
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 31+ messages in thread
From: Miao Xie @ 2012-09-18 10:52 UTC (permalink / raw)
  To: Linux Btrfs

From: Miao Xie <miaox@cn.fujitsu.com>

This patch fixes the following warning:
cmds-subvolume.c:283:3: warning: implicit declaration of function "list_snapshots"

Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
---
Changelog v3 -> v4:
- no change.

Changelog v1 -> v3:
- new patch.
---
 btrfs-list.h |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/btrfs-list.h b/btrfs-list.h
index 96f3641..b4a7f30 100644
--- a/btrfs-list.h
+++ b/btrfs-list.h
@@ -17,5 +17,6 @@
  */
 
 int list_subvols(int fd, int print_parent, int get_default, int print_uuid);
+int list_snapshots(int fd, int print_parent, int order, int print_uuid);
 int find_updated_files(int fd, u64 root_id, u64 oldest_gen);
 char *path_for_root(int fd, u64 root);
-- 
1.7.6.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH V4 3/7] Btrfs-progs: fix wrong usage of btrfs subvolume list command
  2012-09-18 10:35 [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
  2012-09-18 10:50 ` [PATCH V4 1/7] Btrfs-progs: move the function declarations to a new head file Miao Xie
  2012-09-18 10:52 ` [PATCH V4 2/7] Btrfs-progs: fix compile warning of implicit declaration of "list_snapshots" Miao Xie
@ 2012-09-18 10:55 ` Miao Xie
  2012-09-18 10:59 ` [PATCH V4 4/7] Btrfs-progs: fix wrong way to check if the root item contains otime and uuid Miao Xie
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 31+ messages in thread
From: Miao Xie @ 2012-09-18 10:55 UTC (permalink / raw)
  To: Linux Btrfs

From: Miao Xie <miaox@cn.fujitsu.com>

Since the uuid output function has been implemented, we should update
the usage to tell the users.

Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
---
Changelog v3 -> v4:
- no change.

Changelog v1 -> v3:
- new patch
---
 cmds-subvolume.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index ffc5d95..cd4b5a7 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -258,10 +258,11 @@ static int cmd_subvol_delete(int argc, char **argv)
 }
 
 static const char * const cmd_subvol_list_usage[] = {
-	"btrfs subvolume list [-ps] <path>",
+	"btrfs subvolume list [-pu] [-s 0|1] <path>",
 	"List subvolumes (and snapshots)",
 	"",
 	"-p           print parent ID",
+	"-u           print the uuid of subvolumes (and snapshots)",
 	"-s value     list snapshots with generation in ascending/descending order",
 	"             (1: ascending, 0: descending)",
 	NULL
-- 
1.7.6.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH V4 4/7] Btrfs-progs: fix wrong way to check if the root item contains otime and uuid
  2012-09-18 10:35 [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
                   ` (2 preceding siblings ...)
  2012-09-18 10:55 ` [PATCH V4 3/7] Btrfs-progs: fix wrong usage of btrfs subvolume list command Miao Xie
@ 2012-09-18 10:59 ` Miao Xie
  2012-09-19  1:55   ` Anand Jain
  2012-09-18 11:06 ` [PATCH V4 5/7] Btrfs-progs: restructure list_subvolumes Miao Xie
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 31+ messages in thread
From: Miao Xie @ 2012-09-18 10:59 UTC (permalink / raw)
  To: Linux Btrfs

From: Miao Xie <miaox@cn.fujitsu.com>

Now we check if the root item contains otime and uuid or not by comparing
->generation_v2 and ->generation of the btrfs_root_item structure, it is
wrong because it is possbile that ->generation may equal to the first
variant of the next item. We fix this problem by check the size of btrfs_root_item,
if it is larger than the original one, the new btrfs_root_item contains otime
and uuid. we needn't worry the case that the new filesystem is mounted on the
old kernel. because the otime and uuid are not changed on the old kernel, we can
get the correct result even on the kernel.

Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
---
Changelog v3 -> v4:
- change the check method of btrfs_root_item, if the size of btrfs_root_item
  is larger than the original one, the new btrfs_root_item contains otime and
  uuid.

Changelog v1 -> v3:
- new patch
---
 btrfs-list.c |    9 ++++++---
 ctree.h      |   15 +++++++++++++++
 2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/btrfs-list.c b/btrfs-list.c
index ef621f0..ed28021 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -737,7 +737,8 @@ again:
 			} else if (get_gen && sh->type == BTRFS_ROOT_ITEM_KEY) {
 				ri = (struct btrfs_root_item *)(args.buf + off);
 				gen = btrfs_root_generation(ri);
-				if(ri->generation == ri->generation_v2) {
+				if(sh->len >
+				   sizeof(struct btrfs_root_item_v0)) {
 					t = ri->otime.sec;
 					memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
 				} else {
@@ -844,9 +845,11 @@ static int __list_snapshot_search(int fd, struct root_lookup *root_lookup)
 			off += sizeof(*sh);
 			if (sh->type == BTRFS_ROOT_ITEM_KEY && sh->offset) {
 				item = (struct btrfs_root_item *)(args.buf + off);
-				if(item->generation == item->generation_v2) {
+				if(sh->len >
+				   sizeof(struct btrfs_root_item_v0)) {
 					t = item->otime.sec;
-					memcpy(uuid, item->uuid, BTRFS_UUID_SIZE);
+					memcpy(uuid, item->uuid,
+					       BTRFS_UUID_SIZE);
 				} else {
 					t = 0;
 					memset(uuid, 0, BTRFS_UUID_SIZE);
diff --git a/ctree.h b/ctree.h
index 7f55229..c55d033 100644
--- a/ctree.h
+++ b/ctree.h
@@ -637,6 +637,21 @@ struct btrfs_dir_item {
 	u8 type;
 } __attribute__ ((__packed__));
 
+struct btrfs_root_item_v0 {
+	struct btrfs_inode_item inode;
+	__le64 generation;
+	__le64 root_dirid;
+	__le64 bytenr;
+	__le64 byte_limit;
+	__le64 bytes_used;
+	__le64 last_snapshot;
+	__le64 flags;
+	__le32 refs;
+	struct btrfs_disk_key drop_progress;
+	u8 drop_level;
+	u8 level;
+} __attribute__ ((__packed__));
+
 struct btrfs_root_item {
 	struct btrfs_inode_item inode;
 	__le64 generation;
-- 
1.7.6.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH V4 5/7] Btrfs-progs: restructure list_subvolumes
  2012-09-18 10:35 [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
                   ` (3 preceding siblings ...)
  2012-09-18 10:59 ` [PATCH V4 4/7] Btrfs-progs: fix wrong way to check if the root item contains otime and uuid Miao Xie
@ 2012-09-18 11:06 ` Miao Xie
  2012-09-20 12:09   ` David Sterba
  2012-10-09 16:05   ` Alex Lyakas
  2012-09-18 11:09 ` [PATCH V4 6/7] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
                   ` (5 subsequent siblings)
  10 siblings, 2 replies; 31+ messages in thread
From: Miao Xie @ 2012-09-18 11:06 UTC (permalink / raw)
  To: Linux Btrfs

The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.

Beside that, the most code of list_snapshots() is similar to list_subvols(),

So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
	btrfs_list_filter_func filter_func;
	void *data;
};

struct btrfs_list_comparer {
	btrfs_list_comp_func comp_func;
	int is_descending;
};

struct {
	char	*name;
	char	*column_name;
	int	need_print;
} btrfs_list_columns[];

If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.

The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.

The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.

After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().

[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).

Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
---
Changelog v3 -> v4:
- add the filter set and comparer set which are used to manage the filters and
  comparers. And the memory space of these two set are allocated dynamically,
  in this way, the users can specify lots of filters and comparers, not be limited
  by the size of the array.

Changelog v1 -> v3:
- new patch.
---
 btrfs-list.c     | 1004 ++++++++++++++++++++++++++++++++----------------------
 btrfs-list.h     |   73 ++++-
 cmds-inspect.c   |    2 +-
 cmds-subvolume.c |   59 +++-
 send-utils.c     |    3 +-
 5 files changed, 712 insertions(+), 429 deletions(-)

diff --git a/btrfs-list.c b/btrfs-list.c
index ed28021..bace903 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -37,6 +37,9 @@
 #include <uuid/uuid.h>
 #include "btrfs-list.h"
 
+#define BTRFS_LIST_NFILTERS_INCREASE	(2 * BTRFS_LIST_FILTER_MAX)
+#define BTRFS_LIST_NCOMPS_INCREASE	(2 * BTRFS_LIST_COMP_MAX)
+
 /* we store all the roots we find in an rbtree so that we can
  * search for them later.
  */
@@ -49,19 +52,28 @@ struct root_lookup {
  */
 struct root_info {
 	struct rb_node rb_node;
+	struct rb_node sort_node;
 
 	/* this root's id */
 	u64 root_id;
 
+	/* equal the offset of the root's key */
+	u64 root_offset;
+
 	/* the id of the root that references this one */
 	u64 ref_tree;
 
 	/* the dir id we're in from ref_tree */
 	u64 dir_id;
 
+	u64 top_id;
+
 	/* generation when the root is created or last updated */
 	u64 gen;
 
+	/* creation generation of this root in sec*/
+	u64 ogen;
+
 	/* creation time of this root in sec*/
 	time_t otime;
 
@@ -73,35 +85,254 @@ struct root_info {
 	char *path;
 
 	/* the name of this root in the directory it lives in */
-	char name[];
+	char *name;
+
+	char *full_path;
 };
 
+struct {
+	char	*name;
+	char	*column_name;
+	int	need_print;
+} btrfs_list_columns[] = {
+	{
+		.name		= "ID",
+		.column_name	= "ID",
+		.need_print	= 1,
+	},
+	{
+		.name		= "gen",
+		.column_name	= "Gen",
+		.need_print	= 1,
+	},
+	{
+		.name		= "cgen",
+		.column_name	= "CGen",
+		.need_print	= 0,
+	},
+	{
+		.name		= "parent",
+		.column_name	= "Parent",
+		.need_print	= 0,
+	},
+	{
+		.name		= "top level",
+		.column_name	= "Top Level",
+		.need_print	= 1,
+	},
+	{
+		.name		= "otime",
+		.column_name	= "OTime",
+		.need_print	= 0,
+	},
+	{
+		.name		= "uuid",
+		.column_name	= "UUID",
+		.need_print	= 0,
+	},
+	{
+		.name		= "path",
+		.column_name	= "Path",
+		.need_print	= 1,
+	},
+	{
+		.name		= NULL,
+		.column_name	= NULL,
+		.need_print	= 0,
+	},
+};
+
+static btrfs_list_filter_func all_filter_funcs[];
+static btrfs_list_comp_func all_comp_funcs[];
+
+void btrfs_list_setup_print_column(enum btrfs_list_column_enum column)
+{
+	int i;
+
+	BUG_ON(column < 0 || column > BTRFS_LIST_ALL);
+
+	if (column < BTRFS_LIST_ALL) {
+		btrfs_list_columns[column].need_print = 1;
+		return;
+	}
+
+	for (i = 0; i < BTRFS_LIST_ALL; i++)
+		btrfs_list_columns[i].need_print = 1;
+}
+
 static void root_lookup_init(struct root_lookup *tree)
 {
 	tree->root.rb_node = NULL;
 }
 
-static int comp_entry(struct root_info *entry, u64 root_id, u64 ref_tree)
+static int comp_entry_with_rootid(struct root_info *entry1,
+				  struct root_info *entry2,
+				  int is_descending)
 {
-	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;
+	int ret;
+
+	if (entry1->root_id > entry2->root_id)
+		ret = 1;
+	else if (entry1->root_id < entry2->root_id)
+		ret = -1;
+	else
+		ret = 0;
+
+	return is_descending ? -ret : ret;
+}
+
+static int comp_entry_with_gen(struct root_info *entry1,
+			       struct root_info *entry2,
+			       int is_descending)
+{
+	int ret;
+
+	if (entry1->gen > entry2->gen)
+		ret = 1;
+	else if (entry1->gen < entry2->gen)
+		ret = -1;
+	else
+		ret = 0;
+
+	return is_descending ? -ret : ret;
+}
+
+static int comp_entry_with_ogen(struct root_info *entry1,
+				struct root_info *entry2,
+				int is_descending)
+{
+	int ret;
+
+	if (entry1->ogen > entry2->ogen)
+		ret = 1;
+	else if (entry1->ogen < entry2->ogen)
+		ret = -1;
+	else
+		ret = 0;
+
+	return is_descending ? -ret : ret;
+}
+
+static btrfs_list_comp_func all_comp_funcs[] = {
+	[BTRFS_LIST_COMP_ROOTID]	= comp_entry_with_rootid,
+	[BTRFS_LIST_COMP_OGEN]		= comp_entry_with_ogen,
+	[BTRFS_LIST_COMP_GEN]		= comp_entry_with_gen,
+};
+
+struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void)
+{
+	struct btrfs_list_comparer_set *set;
+	int size;
+
+	size = sizeof(struct btrfs_list_comparer_set) +
+	       BTRFS_LIST_NCOMPS_INCREASE * sizeof(struct btrfs_list_comparer);
+	set = malloc(size);
+	if (!set) {
+		fprintf(stderr, "memory allocation failed\n");
+		exit(1);
+	}
+
+	memset(set, 0, size);
+	set->total = BTRFS_LIST_NCOMPS_INCREASE;
+
+	return set;
+}
+
+void btrfs_list_free_comparer_set(struct btrfs_list_comparer_set *comp_set)
+{
+	free(comp_set);
+}
+
+int btrfs_list_setup_comparer(struct btrfs_list_comparer_set  **comp_set,
+			      enum btrfs_list_comp_enum comparer,
+			      int is_descending)
+{
+	struct btrfs_list_comparer_set *set = *comp_set;
+	int size;
+
+	BUG_ON(!set);
+	BUG_ON(comparer >= BTRFS_LIST_COMP_MAX);
+	BUG_ON(set->ncomps > set->total);
+
+	if (set->ncomps == set->total) {
+		size = set->total + BTRFS_LIST_NCOMPS_INCREASE;
+		size = sizeof(*set) + size * sizeof(struct btrfs_list_comparer);
+		set = realloc(set, size);
+		if (!set) {
+			fprintf(stderr, "memory allocation failed\n");
+			exit(1);
+		}
+
+		memset(&set->comps[set->total], 0,
+		       BTRFS_LIST_NCOMPS_INCREASE *
+		       sizeof(struct btrfs_list_comparer));
+		set->total += BTRFS_LIST_NCOMPS_INCREASE;
+		*comp_set = set;
+	}
+
+	BUG_ON(set->comps[set->ncomps].comp_func);
+
+	set->comps[set->ncomps].comp_func = all_comp_funcs[comparer];
+	set->comps[set->ncomps].is_descending = is_descending;
+	set->ncomps++;
 	return 0;
 }
 
-static int comp_entry_with_gen(struct root_info *entry, u64 root_id,
-			       u64 ref_tree, u64 gen)
+static int sort_comp(struct root_info *entry1, struct root_info *entry2,
+		     struct btrfs_list_comparer_set *set)
 {
-	if (entry->gen < gen)
-		return 1;
-	if (entry->gen > gen)
-		return -1;
-	return comp_entry(entry, root_id, ref_tree);
+	int rootid_compared = 0;
+	int i, ret = 0;
+
+	if (!set || !set->ncomps)
+		goto comp_rootid;
+
+	for (i = 0; i < set->ncomps; i++) {
+		if (!set->comps[i].comp_func)
+			break;
+
+		ret = set->comps[i].comp_func(entry1, entry2,
+					      set->comps[i].is_descending);
+		if (ret)
+			return ret;
+
+		if (set->comps[i].comp_func == comp_entry_with_rootid)
+			rootid_compared = 1;
+	}
+
+	if (!rootid_compared) {
+comp_rootid:
+		ret = comp_entry_with_rootid(entry1, entry2, 0);
+	}
+
+	return ret;
+}
+
+static int sort_tree_insert(struct root_lookup *sort_tree,
+			    struct root_info *ins,
+			    struct btrfs_list_comparer_set *comp_set)
+{
+	struct rb_node **p = &sort_tree->root.rb_node;
+	struct rb_node *parent = NULL;
+	struct root_info *curr;
+	int ret;
+
+	while (*p) {
+		parent = *p;
+		curr = rb_entry(parent, struct root_info, sort_node);
+
+		ret = sort_comp(ins, curr, comp_set);
+		if (ret < 0)
+			p = &(*p)->rb_left;
+		else if (ret > 0)
+			p = &(*p)->rb_right;
+		else
+			return -EEXIST;
+	}
+
+	rb_link_node(&ins->sort_node, parent, p);
+	rb_insert_color(&ins->sort_node, &sort_tree->root);
+	return 0;
 }
 
 /*
@@ -109,118 +340,165 @@ static int comp_entry_with_gen(struct root_info *entry, u64 root_id,
  * 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, u64 *gen, struct rb_node *node)
+static int root_tree_insert(struct root_lookup *root_tree,
+			    struct root_info *ins)
 {
-	struct rb_node ** p = &root->rb_node;
+	struct rb_node **p = &root_tree->root.rb_node;
 	struct rb_node * parent = NULL;
-	struct root_info *entry;
-	int comp;
+	struct root_info *curr;
+	int ret;
 
 	while(*p) {
 		parent = *p;
-		entry = rb_entry(parent, struct root_info, rb_node);
+		curr = rb_entry(parent, struct root_info, rb_node);
 
-		if (!gen)
-			comp = comp_entry(entry, root_id, ref_tree);
-		else
-			comp = comp_entry_with_gen(entry, root_id, ref_tree,
-						   *gen);
-
-		if (comp < 0)
+		ret = comp_entry_with_rootid(ins, curr, 0);
+		if (ret < 0)
 			p = &(*p)->rb_left;
-		else if (comp > 0)
+		else if (ret > 0)
 			p = &(*p)->rb_right;
 		else
-			return parent;
+			return -EEXIST;
 	}
 
-	entry = rb_entry(parent, struct root_info, rb_node);
-	rb_link_node(node, parent, p);
-	rb_insert_color(node, root);
-	return NULL;
+	rb_link_node(&ins->rb_node, parent, p);
+	rb_insert_color(&ins->rb_node, &root_tree->root);
+	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 struct root_info *root_tree_search(struct root_lookup *root_tree,
+					  u64 root_id)
 {
-	struct rb_node * n = root->rb_node;
+	struct rb_node *n = root_tree->root.rb_node;
 	struct root_info *entry;
+	struct root_info tmp;
+	int ret;
+
+	tmp.root_id = root_id;
 
 	while(n) {
 		entry = rb_entry(n, struct root_info, rb_node);
 
-		if (entry->root_id < root_id)
+		ret = comp_entry_with_rootid(&tmp, entry, 0);
+		if (ret < 0)
 			n = n->rb_left;
-		else if (entry->root_id > root_id)
+		else if (ret > 0)
 			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;
-			}
+		else
 			return entry;
-		}
 	}
 	return NULL;
 }
 
+static int update_root(struct root_lookup *root_lookup,
+		       u64 root_id, u64 ref_tree, u64 root_offset, u64 dir_id,
+		       char *name, int name_len, u64 ogen, u64 gen, time_t ot,
+		       void *uuid)
+{
+	struct root_info *ri;
+
+	ri = root_tree_search(root_lookup, root_id);
+	if (!ri || ri->root_id != root_id)
+		return -ENOENT;
+	if (name && name_len > 0) {
+		if (ri->name)
+			free(ri->name);
+
+		ri->name = malloc(name_len + 1);
+		if (!ri->name) {
+			fprintf(stderr, "memory allocation failed\n");
+			exit(1);
+		}
+		strncpy(ri->name, name, name_len);
+		ri->name[name_len] = 0;
+	}
+	if (ref_tree)
+		ri->ref_tree = ref_tree;
+	if (root_offset)
+		ri->root_offset = root_offset;
+	if (dir_id)
+		ri->dir_id = dir_id;
+	if (gen)
+		ri->gen = gen;
+	if (ogen)
+		ri->ogen = ogen;
+	if (!ri->ogen && root_offset)
+		ri->ogen = root_offset;
+	if (ot)
+		ri->otime = ot;
+	if (uuid)
+		memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
+
+	return 0;
+}
+
 /*
- * 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
+ * add_root - update the existed root, or allocate a new root and insert it
+ *	      into the lookup tree.
+ * root_id: object id of the root
+ * ref_tree: object id of the referring root.
+ * root_offset: offset value of the root'key
+ * dir_id: inode id of the directory in ref_tree where this root can be found.
+ * name: the name of root_id in that directory
+ * name_len: the length of name
+ * ogen: the original generation of the root
+ * gen: the current generation of the root
+ * ot: the original time(create time) of the root
+ * uuid: uuid of the root
  */
 static int add_root(struct root_lookup *root_lookup,
-		    u64 root_id, u64 ref_tree, u64 dir_id, char *name,
-		    int name_len, u64 *gen, time_t ot, void *uuid)
+		    u64 root_id, u64 ref_tree, u64 root_offset, u64 dir_id,
+		    char *name, int name_len, u64 ogen, u64 gen, time_t ot,
+		    void *uuid)
 {
 	struct root_info *ri;
-	struct rb_node *ret;
-	ri = malloc(sizeof(*ri) + name_len + 1);
+	int ret;
+
+	ret = update_root(root_lookup, root_id, ref_tree, root_offset, dir_id,
+			  name, name_len, ogen, gen, ot, uuid);
+	if (!ret)
+		return 0;
+
+	ri = malloc(sizeof(*ri));
 	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;
+	memset(ri, 0, sizeof(*ri));
 	ri->root_id = root_id;
-	ri->ref_tree = ref_tree;
-	if (name)
+
+	if (name && name_len > 0) {
+		ri->name = malloc(name_len + 1);
+		if (!ri->name) {
+			fprintf(stderr, "memory allocation failed\n");
+			exit(1);
+		}
 		strncpy(ri->name, name, name_len);
-	if (name_len > 0)
 		ri->name[name_len] = 0;
+	}
+	if (ref_tree)
+		ri->ref_tree = ref_tree;
+	if (dir_id)
+		ri->dir_id = dir_id;
+	if (root_offset)
+		ri->root_offset = root_offset;
 	if (gen)
-		ri->gen = *gen;
-	ri->otime = ot;
+		ri->gen = gen;
+	if (ogen)
+		ri->ogen = ogen;
+	if (!ri->ogen && root_offset)
+		ri->ogen = root_offset;
+	if (ot)
+		ri->otime = ot;
 
 	if (uuid) 
 		memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
-	else
-		memset(&ri->uuid, 0, BTRFS_UUID_SIZE);
 
-	ret = tree_insert(&root_lookup->root, root_id, ref_tree, gen,
-			  &ri->rb_node);
+	ret = root_tree_insert(root_lookup, ri);
 	if (ret) {
 		printf("failed to insert tree %llu\n", (unsigned long long)root_id);
 		exit(1);
@@ -228,24 +506,33 @@ static int add_root(struct root_lookup *root_lookup,
 	return 0;
 }
 
-static int update_root(struct root_lookup *root_lookup, u64 root_id, u64 gen,
-			time_t ot, void *uuid)
+void __free_root_info(struct root_info *ri)
 {
-	struct root_info *ri;
+	if (ri->name)
+		free(ri->name);
 
-	ri = tree_search(&root_lookup->root, root_id);
-	if (!ri || ri->root_id != root_id) {
-		fprintf(stderr, "could not find subvol %llu\n", root_id);
-		return -ENOENT;
-	}
-	ri->gen = gen;
-	ri->otime = ot;
-	if (uuid)
-		memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
-	else
-		memset(&ri->uuid, 0, BTRFS_UUID_SIZE);
+	if (ri->path)
+		free(ri->path);
 
-	return 0;
+	if (ri->full_path)
+		free(ri->full_path);
+
+	free(ri);
+}
+
+void __free_all_subvolumn(struct root_lookup *root_tree)
+{
+	struct root_info *entry;
+	struct rb_node *n;
+
+	n = rb_first(&root_tree->root);
+	while (n) {
+		entry = rb_entry(n, struct root_info, rb_node);
+		rb_erase(n, &root_tree->root);
+		__free_root_info(entry);
+
+		n = rb_first(&root_tree->root);
+	}
 }
 
 /*
@@ -255,8 +542,7 @@ static int update_root(struct root_lookup *root_lookup, u64 root_id, u64 gen,
  * 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 *parent_id, u64 *top_id, char **path)
+static int resolve_root(struct root_lookup *rl, struct root_info *ri)
 {
 	char *full_path = NULL;
 	int len = 0;
@@ -266,7 +552,6 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
 	 * we go backwards from the root_info object and add pathnames
 	 * from parent directories as we go.
 	 */
-	*parent_id = 0;
 	found = ri;
 	while (1) {
 		char *tmp;
@@ -275,6 +560,10 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
 
 		/* room for / and for null */
 		tmp = malloc(add_len + 2 + len);
+		if (!tmp) {
+			perror("malloc failed");
+			exit(1);
+		}
 		if (full_path) {
 			memcpy(tmp + add_len + 1, full_path, len);
 			tmp[add_len] = '/';
@@ -289,13 +578,10 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
 		}
 
 		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;
+			ri->top_id = next;
 			break;
 		}
 
@@ -303,14 +589,14 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
 		 * if the ref_tree wasn't in our tree of roots, we're
 		 * at the top
 		 */
-		found = tree_search(&rl->root, next);
+		found = root_tree_search(rl, next);
 		if (!found) {
-			*top_id = next;
+			ri->top_id = next;
 			break;
 		}
 	}
 
-	*path = full_path;
+	ri->full_path = full_path;
 
 	return 0;
 }
@@ -608,7 +894,7 @@ build:
 	return full;
 }
 
-static int get_default_subvolid(int fd, u64 *default_id)
+int btrfs_list_get_default_subvolume(int fd, u64 *default_id)
 {
 	struct btrfs_ioctl_search_args args;
 	struct btrfs_ioctl_search_key *sk = &args.key;
@@ -674,10 +960,9 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 	int name_len;
 	char *name;
 	u64 dir_id;
-	u8 type;
 	u64 gen = 0;
+	u64 ogen;
 	int i;
-	int get_gen = 0;
 	time_t t;
 	u8 uuid[BTRFS_UUID_SIZE];
 
@@ -692,7 +977,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 	 * only send back this type of key now.
 	 */
 	sk->max_type = BTRFS_ROOT_BACKREF_KEY;
-	sk->min_type = BTRFS_ROOT_BACKREF_KEY;
+	sk->min_type = BTRFS_ROOT_ITEM_KEY;
 
 	sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
 
@@ -704,7 +989,6 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 	sk->max_offset = (u64)-1;
 	sk->max_transid = (u64)-1;
 
-again:
 	/* just a big number, doesn't matter much */
 	sk->nr_items = 4096;
 
@@ -726,28 +1010,32 @@ again:
 			sh = (struct btrfs_ioctl_search_header *)(args.buf +
 								  off);
 			off += sizeof(*sh);
-			if (!get_gen && sh->type == BTRFS_ROOT_BACKREF_KEY) {
+			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, NULL, 0, NULL);
-			} else if (get_gen && sh->type == BTRFS_ROOT_ITEM_KEY) {
+					 0, dir_id, name, name_len, 0, 0, 0,
+					 NULL);
+			} else if (sh->type == BTRFS_ROOT_ITEM_KEY) {
 				ri = (struct btrfs_root_item *)(args.buf + off);
 				gen = btrfs_root_generation(ri);
 				if(sh->len >
 				   sizeof(struct btrfs_root_item_v0)) {
 					t = ri->otime.sec;
+					ogen = btrfs_root_otransid(ri);
 					memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
 				} else {
 					t = 0;
+					ogen = 0;
 					memset(uuid, 0, BTRFS_UUID_SIZE);
 				}
 
-				update_root(root_lookup, sh->objectid, gen, t,
-					uuid);
+				add_root(root_lookup, sh->objectid, 0,
+					 sh->offset, 0, NULL, 0, ogen, gen, t,
+					 uuid);
 			}
 
 			off += sh->len;
@@ -761,129 +1049,139 @@ again:
 			sk->min_offset = sh->offset;
 		}
 		sk->nr_items = 4096;
-		/* this iteration is done, step forward one root for the next
-		 * ioctl
-		 */
-		if (get_gen)
-			type = BTRFS_ROOT_ITEM_KEY;
+		sk->min_offset++;
+		if (!sk->min_offset)	/* overflow */
+			sk->min_type++;
 		else
-			type = BTRFS_ROOT_BACKREF_KEY;
+			continue;
 
-		if (sk->min_type < type) {
-			sk->min_type = type;
-			sk->min_offset = 0;
-		} else  if (sk->min_objectid < BTRFS_LAST_FREE_OBJECTID) {
+		if (sk->min_type > BTRFS_ROOT_BACKREF_KEY) {
+			sk->min_type = BTRFS_ROOT_ITEM_KEY;
 			sk->min_objectid++;
-			sk->min_type = type;
-			sk->min_offset = 0;
 		} else
+			continue;
+
+		if (sk->min_objectid > sk->max_objectid)
 			break;
 	}
 
-	if (!get_gen) {
-		memset(&args, 0, sizeof(args));
+	return 0;
+}
 
-		sk->tree_id = 1;
-		sk->max_type = BTRFS_ROOT_ITEM_KEY;
-		sk->min_type = BTRFS_ROOT_ITEM_KEY;
+static int filter_by_rootid(struct root_info *ri, void *arg)
+{
+	u64 default_root_id = *((u64 *)arg);
 
-		sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
+	return ri->root_id == default_root_id;
+}
 
-		sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
-		sk->max_offset = (u64)-1;
-		sk->max_transid = (u64)-1;
+static int filter_snapshot(struct root_info *ri, void *arg)
+{
+	return !!ri->root_offset;
+}
+
+static btrfs_list_filter_func all_filter_funcs[] = {
+	[BTRFS_LIST_FILTER_ROOTID]		= filter_by_rootid,
+	[BTRFS_LIST_FILTER_SNAPSHOT_ONLY]	= filter_snapshot,
+};
 
-		get_gen = 1;
-		goto again;
+struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void)
+{
+	struct btrfs_list_filter_set *set;
+	int size;
+
+	size = sizeof(struct btrfs_list_filter_set) +
+	       BTRFS_LIST_NFILTERS_INCREASE * sizeof(struct btrfs_list_filter);
+	set = malloc(size);
+	if (!set) {
+		fprintf(stderr, "memory allocation failed\n");
+		exit(1);
 	}
-	return 0;
+
+	memset(set, 0, size);
+	set->total = BTRFS_LIST_NFILTERS_INCREASE;
+
+	return set;
 }
 
-static int __list_snapshot_search(int fd, struct root_lookup *root_lookup)
+void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set)
 {
-	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 gen = 0;
-	int i;
-
-	root_lookup_init(root_lookup);
-	memset(&args, 0, sizeof(args));
+	free(filter_set);
+}
 
-	sk->tree_id = 1;
-	sk->max_type = BTRFS_ROOT_ITEM_KEY;
-	sk->min_type = BTRFS_ROOT_ITEM_KEY;
-	sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
-	sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
-	sk->max_offset = (u64)-1;
-	sk->max_transid = (u64)-1;
-	sk->nr_items = 4096;
+int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
+			    enum btrfs_list_filter_enum filter, void *data)
+{
+	struct btrfs_list_filter_set *set = *filter_set;
+	int size;
+
+	BUG_ON(!set);
+	BUG_ON(filter >= BTRFS_LIST_FILTER_MAX);
+	BUG_ON(set->nfilters > set->total);
+
+	if (set->nfilters == set->total) {
+		size = set->total + BTRFS_LIST_NFILTERS_INCREASE;
+		size = sizeof(*set) + size * sizeof(struct btrfs_list_filter);
+		set = realloc(set, size);
+		if (!set) {
+			fprintf(stderr, "memory allocation failed\n");
+			exit(1);
+		}
 
-	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;
+		memset(&set->filters[set->total], 0,
+		       BTRFS_LIST_NFILTERS_INCREASE *
+		       sizeof(struct btrfs_list_filter));
+		set->total += BTRFS_LIST_NFILTERS_INCREASE;
+		*filter_set = set;
+	}
 
-		off = 0;
+	BUG_ON(set->filters[set->nfilters].filter_func);
 
-		/*
-		 * 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;
-			time_t  t;
-			u8 uuid[BTRFS_UUID_SIZE];
+	set->filters[set->nfilters].filter_func = all_filter_funcs[filter];
+	set->filters[set->nfilters].data = data;
+	set->nfilters++;
+	return 0;
+}
 
-			sh = (struct btrfs_ioctl_search_header *)(args.buf +
-								  off);
-			off += sizeof(*sh);
-			if (sh->type == BTRFS_ROOT_ITEM_KEY && sh->offset) {
-				item = (struct btrfs_root_item *)(args.buf + off);
-				if(sh->len >
-				   sizeof(struct btrfs_root_item_v0)) {
-					t = item->otime.sec;
-					memcpy(uuid, item->uuid,
-					       BTRFS_UUID_SIZE);
-				} else {
-					t = 0;
-					memset(uuid, 0, BTRFS_UUID_SIZE);
-				}
-				gen = sh->offset;
+static int filter_root(struct root_info *ri,
+		       struct btrfs_list_filter_set *set)
+{
+	int i, ret;
 
-				add_root(root_lookup, sh->objectid, 0,
-					 0, NULL, 0, &gen, t, uuid);
-			}
-			off += sh->len;
+	if (!set || !set->nfilters)
+		return 1;
 
-			/*
-			 * 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_ITEM_KEY) {
-			sk->min_type = BTRFS_ROOT_ITEM_KEY;
-			sk->min_offset = 0;
-		} else  if (sk->min_objectid < BTRFS_LAST_FREE_OBJECTID) {
-			sk->min_objectid++;
-			sk->min_type = BTRFS_ROOT_ITEM_KEY;
-			sk->min_offset = 0;
-		} else
+	for (i = 0; i < set->nfilters; i++) {
+		if (!set->filters[i].filter_func)
 			break;
+		ret = set->filters[i].filter_func(ri, set->filters[i].data);
+		if (!ret)
+			return 0;
+	}
+	return 1;
+}
+
+static void __filter_and_sort_subvol(struct root_lookup *all_subvols,
+				    struct root_lookup *sort_tree,
+				    struct btrfs_list_filter_set *filter_set,
+				    struct btrfs_list_comparer_set *comp_set)
+{
+	struct rb_node *n;
+	struct root_info *entry;
+	int ret;
+
+	root_lookup_init(sort_tree);
+
+	n = rb_last(&all_subvols->root);
+	while (n) {
+		entry = rb_entry(n, struct root_info, rb_node);
+
+		resolve_root(all_subvols, entry);
+		ret = filter_root(entry, filter_set);
+		if (ret)
+			sort_tree_insert(sort_tree, entry, comp_set);
+		n = rb_prev(n);
 	}
-	return 0;
 }
 
 static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
@@ -904,128 +1202,91 @@ static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
 	return 0;
 }
 
-int list_subvols(int fd, int print_parent, int get_default, int print_uuid)
+static void print_subvolume_column(struct root_info *subv,
+				   enum btrfs_list_column_enum column)
 {
-	struct root_lookup root_lookup;
-	struct rb_node *n;
-	u64 default_id;
-	int ret;
+	char tstr[256];
 	char uuidparse[37];
 
-	if (get_default) {
-		ret = get_default_subvolid(fd, &default_id);
-		if (ret) {
-			fprintf(stderr, "ERROR: can't perform the search - %s\n",
-				strerror(errno));
-			return ret;
-		}
-		if (default_id == 0) {
-			fprintf(stderr, "ERROR: 'default' dir item not found\n");
-			return ret;
-		}
-
-		/* no need to resolve roots if FS_TREE is default */
-		if (default_id == BTRFS_FS_TREE_OBJECTID) {
-			printf("ID 5 (FS_TREE)\n");
-			return ret;
-		}
-	}
-
-	ret = __list_subvol_search(fd, &root_lookup);
-	if (ret) {
-		fprintf(stderr, "ERROR: can't perform the search - %s\n",
-				strerror(errno));
-		return ret;
+	BUG_ON(column >= BTRFS_LIST_ALL || column < 0);
+
+	switch (column) {
+	case BTRFS_LIST_OBJECTID:
+		printf("%llu", subv->root_id);
+		break;
+	case BTRFS_LIST_GENERATION:
+		printf("%llu", subv->gen);
+		break;
+	case BTRFS_LIST_OGENERATION:
+		printf("%llu", subv->ogen);
+		break;
+	case BTRFS_LIST_PARENT:
+		printf("%llu", subv->ref_tree);
+		break;
+	case BTRFS_LIST_TOP_LEVEL:
+		printf("%llu", subv->top_id);
+		break;
+	case BTRFS_LIST_OTIME:
+		if (subv->otime)
+			strftime(tstr, 256, "%Y-%m-%d %X",
+				 localtime(&subv->otime));
+		else
+			strcpy(tstr, "-");
+		printf("%s", tstr);
+		break;
+	case BTRFS_LIST_UUID:
+		if (uuid_is_null(subv->uuid))
+			strcpy(uuidparse, "-");
+		else
+			uuid_unparse(subv->uuid, uuidparse);
+		printf("%s", uuidparse);
+		break;
+	case BTRFS_LIST_PATH:
+		BUG_ON(!subv->full_path);
+		printf("%s", subv->full_path);
+		break;
+	default:
+		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.
-	 */
-	ret = __list_subvol_fill_paths(fd, &root_lookup);
-	if (ret < 0)
-		return ret;
-
-	/* now that we have all the subvol-relative paths filled in,
-	 * we have to string the subvols together so that we can get
-	 * a path all the way back to the FS root
-	 */
-	n = rb_last(&root_lookup.root);
-	while (n) {
-		struct root_info *entry;
-		u64 level;
-		u64 parent_id;
-		char *path;
+static void print_single_volume_info_default(struct root_info *subv)
+{
+	int i;
 
-		entry = rb_entry(n, struct root_info, rb_node);
-		if (get_default && entry->root_id != default_id) {
-			n = rb_prev(n);
+	for (i = 0; i < BTRFS_LIST_ALL; i++) {
+		if (!btrfs_list_columns[i].need_print)
 			continue;
-		}
 
-		resolve_root(&root_lookup, entry, &parent_id, &level, &path);
-		if (print_parent) {
-			if (print_uuid) {
-				if (uuid_is_null(entry->uuid))
-					strcpy(uuidparse, "-");
-				else
-					uuid_unparse(entry->uuid, uuidparse);
-				printf("ID %llu gen %llu parent %llu top level %llu"
-					" uuid %s path %s\n",
-					(unsigned long long)entry->root_id,
-					(unsigned long long)entry->gen,
-					(unsigned long long)parent_id,
-					(unsigned long long)level,
-					uuidparse, path);
-			} else {
-				printf("ID %llu gen %llu parent %llu top level"
-					" %llu path %s\n",
-					(unsigned long long)entry->root_id,
-					(unsigned long long)entry->gen,
-					(unsigned long long)parent_id,
-					(unsigned long long)level, path);
-			}
-		} else {
-			if (print_uuid) {
-				if (uuid_is_null(entry->uuid))
-					strcpy(uuidparse, "-");
-				else
-					uuid_unparse(entry->uuid, uuidparse);
-				printf("ID %llu gen %llu top level %llu"
-					" uuid %s path %s\n",
-					(unsigned long long)entry->root_id,
-					(unsigned long long)entry->gen,
-					(unsigned long long)level,
-					uuidparse, path);
-			} else {
-				printf("ID %llu gen %llu top level %llu path %s\n",
-					(unsigned long long)entry->root_id,
-					(unsigned long long)entry->gen,
-					(unsigned long long)level, path);
-			}
-		}
+		printf("%s ", btrfs_list_columns[i].name);
+		print_subvolume_column(subv, i);
 
-		free(path);
-		n = rb_prev(n);
+		if (i != BTRFS_LIST_PATH)
+			printf(" ");
 	}
+	printf("\n");
+}
 
-	return ret;
+static void print_all_volume_info_default(struct root_lookup *sorted_tree)
+{
+	struct rb_node *n;
+	struct root_info *entry;
+
+	n = rb_first(&sorted_tree->root);
+	while (n) {
+		entry = rb_entry(n, struct root_info, sort_node);
+		print_single_volume_info_default(entry);
+		n = rb_next(n);
+	}
 }
 
-int list_snapshots(int fd, int print_parent, int order, int print_uuid)
+int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
+		       struct btrfs_list_comparer_set *comp_set)
 {
 	struct root_lookup root_lookup;
-	struct root_lookup root_lookup_snap;
-	struct rb_node *n;
+	struct root_lookup root_sort;
 	int ret;
 
-	ret = __list_snapshot_search(fd, &root_lookup_snap);
-	if (ret) {
-		fprintf(stderr, "ERROR: can't perform the search - %s\n",
-				strerror(errno));
-		return ret;
-	}
-	
 	ret = __list_subvol_search(fd, &root_lookup);
 	if (ret) {
 		fprintf(stderr, "ERROR: can't perform the search - %s\n",
@@ -1041,86 +1302,11 @@ int list_snapshots(int fd, int print_parent, int order, int print_uuid)
 	if (ret < 0)
 		return ret;
 
-	/* now that we have all the subvol-relative paths filled in,
-	 * we have to string the subvols together so that we can get
-	 * a path all the way back to the FS root
-	 */
-	if (!order)
-		n = rb_last(&root_lookup_snap.root);
-	else
-		n = rb_first(&root_lookup_snap.root);
-	while (n) {
-		struct root_info *entry_snap;
-		struct root_info *entry;
-		u64 level;
-		u64 parent_id;
-		char *path;
-		time_t t;
-		char tstr[256];
-		char uuidparse[37];
-
-		entry_snap = rb_entry(n, struct root_info, rb_node);
-		entry = tree_search(&root_lookup.root, entry_snap->root_id);
-
-		resolve_root(&root_lookup, entry, &parent_id, &level, &path);
-		t = entry->otime;
-		if(t)
-			strftime(tstr,256,"%Y-%m-%d %X",localtime(&t));
-		else
-			strcpy(tstr,"-");
-		if (print_parent) {
-			if (print_uuid) {
-				if (uuid_is_null(entry->uuid))
-					strcpy(uuidparse, "-");
-				else
-					uuid_unparse(entry->uuid, uuidparse);
-				printf("ID %llu gen %llu cgen %llu parent %llu"
-					" top level %llu otime %s uuid %s path %s\n",
-					(unsigned long long)entry->root_id,
-					(unsigned long long)entry->gen,
-					(unsigned long long)entry_snap->gen,
-					(unsigned long long)parent_id,
-					(unsigned long long)level,
-					tstr, uuidparse, path);
-			} else {
-				printf("ID %llu gen %llu cgen %llu parent %llu"
-					" top level %llu otime %s path %s\n",
-					(unsigned long long)entry->root_id,
-					(unsigned long long)entry->gen,
-					(unsigned long long)entry_snap->gen,
-					(unsigned long long)parent_id,
-					(unsigned long long)level, tstr, path);
-			}
-		} else {
-			if (print_uuid) {
-				if (uuid_is_null(entry->uuid))
-					strcpy(uuidparse, "-");
-				else
-					uuid_unparse(entry->uuid, uuidparse);
-				printf("ID %llu gen %llu cgen %llu top level %llu "
-					"otime %s uuid %s path %s\n",
-					(unsigned long long)entry->root_id,
-					(unsigned long long)entry->gen,
-					(unsigned long long)entry_snap->gen,
-					(unsigned long long)level,
-					tstr, uuidparse, path);
-			} else {
-				printf("ID %llu gen %llu cgen %llu top level %llu "
-					"otime %s path %s\n",
-					(unsigned long long)entry->root_id,
-					(unsigned long long)entry->gen,
-					(unsigned long long)entry_snap->gen,
-					(unsigned long long)level, tstr, path);
-			}
-		}
-
-		free(path);
-		if (!order)
-			n = rb_prev(n);
-		else
-			n = rb_next(n);
-	}
+	__filter_and_sort_subvol(&root_lookup, &root_sort, filter_set,
+				 comp_set);
 
+	print_all_volume_info_default(&root_sort);
+	__free_all_subvolumn(&root_lookup);
 	return ret;
 }
 
@@ -1203,7 +1389,7 @@ static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh,
 	return 0;
 }
 
-int find_updated_files(int fd, u64 root_id, u64 oldest_gen)
+int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen)
 {
 	int ret;
 	struct btrfs_ioctl_search_args args;
@@ -1304,7 +1490,7 @@ int find_updated_files(int fd, u64 root_id, u64 oldest_gen)
 	return ret;
 }
 
-char *path_for_root(int fd, u64 root)
+char *btrfs_list_path_for_root(int fd, u64 root)
 {
 	struct root_lookup root_lookup;
 	struct rb_node *n;
@@ -1322,19 +1508,17 @@ char *path_for_root(int fd, u64 root)
 	n = rb_last(&root_lookup.root);
 	while (n) {
 		struct root_info *entry;
-		u64 parent_id;
-		u64 level;
-		char *path;
 
 		entry = rb_entry(n, struct root_info, rb_node);
-		resolve_root(&root_lookup, entry, &parent_id, &level, &path);
-		if (entry->root_id == root)
-			ret_path = path;
-		else
-			free(path);
+		resolve_root(&root_lookup, entry);
+		if (entry->root_id == root) {
+			ret_path = entry->full_path;
+			entry->full_path = NULL;
+		}
 
 		n = rb_prev(n);
 	}
+	__free_all_subvolumn(&root_lookup);
 
 	return ret_path;
 }
diff --git a/btrfs-list.h b/btrfs-list.h
index b4a7f30..ca3eb7b 100644
--- a/btrfs-list.h
+++ b/btrfs-list.h
@@ -16,7 +16,72 @@
  * Boston, MA 021110-1307, USA.
  */
 
-int list_subvols(int fd, int print_parent, int get_default, int print_uuid);
-int list_snapshots(int fd, int print_parent, int order, int print_uuid);
-int find_updated_files(int fd, u64 root_id, u64 oldest_gen);
-char *path_for_root(int fd, u64 root);
+struct root_info;
+
+typedef int (*btrfs_list_filter_func)(struct root_info *, void *);
+typedef int (*btrfs_list_comp_func)(struct root_info *, struct root_info *,
+				    int);
+
+struct btrfs_list_filter {
+	btrfs_list_filter_func filter_func;
+	void *data;
+};
+
+struct btrfs_list_comparer {
+	btrfs_list_comp_func comp_func;
+	int is_descending;
+};
+
+struct btrfs_list_filter_set {
+	int total;
+	int nfilters;
+	struct btrfs_list_filter filters[0];
+};
+
+struct btrfs_list_comparer_set {
+	int total;
+	int ncomps;
+	struct btrfs_list_comparer comps[0];
+};
+
+enum btrfs_list_column_enum {
+	BTRFS_LIST_OBJECTID,
+	BTRFS_LIST_GENERATION,
+	BTRFS_LIST_OGENERATION,
+	BTRFS_LIST_PARENT,
+	BTRFS_LIST_TOP_LEVEL,
+	BTRFS_LIST_OTIME,
+	BTRFS_LIST_UUID,
+	BTRFS_LIST_PATH,
+	BTRFS_LIST_ALL,
+};
+
+enum btrfs_list_filter_enum {
+	BTRFS_LIST_FILTER_ROOTID,
+	BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
+	BTRFS_LIST_FILTER_MAX,
+};
+
+enum btrfs_list_comp_enum {
+	BTRFS_LIST_COMP_ROOTID,
+	BTRFS_LIST_COMP_OGEN,
+	BTRFS_LIST_COMP_GEN,
+	BTRFS_LIST_COMP_MAX,
+};
+
+void btrfs_list_setup_print_column(enum btrfs_list_column_enum column);
+struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void);
+void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set);
+int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
+			    enum btrfs_list_filter_enum filter, void *data);
+struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void);
+void btrfs_list_free_comparer_set(struct btrfs_list_comparer_set *comp_set);
+int btrfs_list_setup_comparer(struct btrfs_list_comparer_set **comp_set,
+			      enum btrfs_list_comp_enum comparer,
+			      int is_descending);
+
+int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
+		       struct btrfs_list_comparer_set *comp_set);
+int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen);
+int btrfs_list_get_default_subvolume(int fd, u64 *default_id);
+char *btrfs_list_path_for_root(int fd, u64 root);
diff --git a/cmds-inspect.c b/cmds-inspect.c
index f943ed9..376fab2 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -194,7 +194,7 @@ static int cmd_logical_resolve(int argc, char **argv)
 		char *name;
 
 		if (getpath) {
-			name = path_for_root(fd, root);
+			name = btrfs_list_path_for_root(fd, root);
 			if (IS_ERR(name))
 				return PTR_ERR(name);
 			if (!name) {
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index cd4b5a7..b1cf2bd 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -28,6 +28,7 @@
 #include "ioctl.h"
 #include "qgroup.h"
 
+#include "ctree.h"
 #include "commands.h"
 #include "btrfs-list.h"
 
@@ -270,13 +271,15 @@ static const char * const cmd_subvol_list_usage[] = {
 
 static int cmd_subvol_list(int argc, char **argv)
 {
+	struct btrfs_list_filter_set *filter_set;
+	struct btrfs_list_comparer_set *comparer_set;
 	int fd;
 	int ret;
-	int print_parent = 0;
-	int print_snap_only = 0;
-	int order = 0;
+	int order;
 	char *subvol;
-	int print_uuid = 0;
+
+	filter_set = btrfs_list_alloc_filter_set();
+	comparer_set = btrfs_list_alloc_comparer_set();
 
 	optind = 1;
 	while(1) {
@@ -286,14 +289,21 @@ static int cmd_subvol_list(int argc, char **argv)
 
 		switch(c) {
 		case 'p':
-			print_parent = 1;
+			btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
 			break;
 		case 's':
-			print_snap_only = 1;
 			order = atoi(optarg);
+			btrfs_list_setup_filter(&filter_set,
+						BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
+						NULL);
+			btrfs_list_setup_comparer(&comparer_set,
+						  BTRFS_LIST_COMP_OGEN,
+						  !order);
+			btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
+			btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
 			break;
 		case 'u':
-			print_uuid =1;
+			btrfs_list_setup_print_column(BTRFS_LIST_UUID);
 			break;
 		default:
 			usage(cmd_subvol_list_usage);
@@ -320,10 +330,8 @@ static int cmd_subvol_list(int argc, char **argv)
 		fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
 		return 12;
 	}
-	if (!print_snap_only)
-		ret = list_subvols(fd, print_parent, 0, print_uuid);
-	else
-		ret = list_snapshots(fd, print_parent, order, print_uuid);
+
+	ret = btrfs_list_subvols(fd, filter_set, comparer_set);
 	if (ret)
 		return 19;
 	return 0;
@@ -483,6 +491,8 @@ static int cmd_subvol_get_default(int argc, char **argv)
 	int fd;
 	int ret;
 	char *subvol;
+	struct btrfs_list_filter_set *filter_set;
+	u64 default_id;
 
 	if (check_argc_exact(argc, 2))
 		usage(cmd_subvol_get_default_usage);
@@ -504,7 +514,30 @@ static int cmd_subvol_get_default(int argc, char **argv)
 		fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
 		return 12;
 	}
-	ret = list_subvols(fd, 0, 1, 0);
+
+	ret = btrfs_list_get_default_subvolume(fd, &default_id);
+	if (ret) {
+		fprintf(stderr, "ERROR: can't perform the search - %s\n",
+			strerror(errno));
+		return ret;
+	}
+
+	if (default_id == 0) {
+		fprintf(stderr, "ERROR: 'default' dir item not found\n");
+		return ret;
+	}
+
+	/* no need to resolve roots if FS_TREE is default */
+	if (default_id == BTRFS_FS_TREE_OBJECTID) {
+		printf("ID 5 (FS_TREE)\n");
+		return ret;
+	}
+
+	filter_set = btrfs_list_alloc_filter_set();
+	btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
+				(void *)&default_id);
+
+	ret = btrfs_list_subvols(fd, filter_set, NULL);
 	if (ret)
 		return 19;
 	return 0;
@@ -585,7 +618,7 @@ static int cmd_find_new(int argc, char **argv)
 		fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
 		return 12;
 	}
-	ret = find_updated_files(fd, 0, last_gen);
+	ret = btrfs_list_find_updated_files(fd, 0, last_gen);
 	if (ret)
 		return 19;
 	return 0;
diff --git a/send-utils.c b/send-utils.c
index 096fa02..fcde5c2 100644
--- a/send-utils.c
+++ b/send-utils.c
@@ -244,7 +244,8 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
 				if (!root_item_valid)
 					goto skip;
 
-				path = path_for_root(mnt_fd, sh->objectid);
+				path = btrfs_list_path_for_root(mnt_fd,
+								sh->objectid);
 				if (!path)
 					path = strdup("");
 				if (IS_ERR(path)) {
-- 
1.7.6.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH V4 6/7] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots
  2012-09-18 10:35 [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
                   ` (4 preceding siblings ...)
  2012-09-18 11:06 ` [PATCH V4 5/7] Btrfs-progs: restructure list_subvolumes Miao Xie
@ 2012-09-18 11:09 ` Miao Xie
  2012-09-18 15:11   ` Martin Steigerwald
  2012-09-18 11:12 ` [PATCH V4 7/7] Btrfs-progs: update the manpage entries for the btrfs subvolume list Miao Xie
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 31+ messages in thread
From: Miao Xie @ 2012-09-18 11:09 UTC (permalink / raw)
  To: Linux Btrfs

From: Miao Xie <miaox@cn.fujitsu.com>

We want 'btrfs subvolume list' only to list readonly subvolumes, this patch set
introduces a new option 'r' to implement it.

You can use the command like that:

        btrfs subvolume list -r <path>

Original-Signed-off-by: Zhou Bo <zhoub-fnst@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
---
Changelog v3 -> v4:
- re-base to the new list_subvols()

Changelog v2 -> v3:
- re-implement this function based on the new list_subvols()

Changelog v1 -> v2:
- change the changelog of the patches and make them more elaborate
---
 btrfs-list.c     |   39 ++++++++++++++++++++++++++++-----------
 btrfs-list.h     |    1 +
 cmds-subvolume.c |   13 +++++++++++--
 ctree.h          |    2 ++
 4 files changed, 42 insertions(+), 13 deletions(-)

diff --git a/btrfs-list.c b/btrfs-list.c
index bace903..7c422bc 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -60,6 +60,9 @@ struct root_info {
 	/* equal the offset of the root's key */
 	u64 root_offset;
 
+	/* flags of the root */
+	u64 flags;
+
 	/* the id of the root that references this one */
 	u64 ref_tree;
 
@@ -395,9 +398,9 @@ static struct root_info *root_tree_search(struct root_lookup *root_tree,
 }
 
 static int update_root(struct root_lookup *root_lookup,
-		       u64 root_id, u64 ref_tree, u64 root_offset, u64 dir_id,
-		       char *name, int name_len, u64 ogen, u64 gen, time_t ot,
-		       void *uuid)
+		       u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
+		       u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
+		       time_t ot, void *uuid)
 {
 	struct root_info *ri;
 
@@ -420,6 +423,8 @@ static int update_root(struct root_lookup *root_lookup,
 		ri->ref_tree = ref_tree;
 	if (root_offset)
 		ri->root_offset = root_offset;
+	if (flags)
+		ri->flags = flags;
 	if (dir_id)
 		ri->dir_id = dir_id;
 	if (gen)
@@ -451,15 +456,15 @@ static int update_root(struct root_lookup *root_lookup,
  * uuid: uuid of the root
  */
 static int add_root(struct root_lookup *root_lookup,
-		    u64 root_id, u64 ref_tree, u64 root_offset, u64 dir_id,
-		    char *name, int name_len, u64 ogen, u64 gen, time_t ot,
-		    void *uuid)
+		    u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
+		    u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
+		    time_t ot, void *uuid)
 {
 	struct root_info *ri;
 	int ret;
 
-	ret = update_root(root_lookup, root_id, ref_tree, root_offset, dir_id,
-			  name, name_len, ogen, gen, ot, uuid);
+	ret = update_root(root_lookup, root_id, ref_tree, root_offset, flags,
+			  dir_id, name, name_len, ogen, gen, ot, uuid);
 	if (!ret)
 		return 0;
 
@@ -486,6 +491,8 @@ static int add_root(struct root_lookup *root_lookup,
 		ri->dir_id = dir_id;
 	if (root_offset)
 		ri->root_offset = root_offset;
+	if (flags)
+		ri->flags = flags;
 	if (gen)
 		ri->gen = gen;
 	if (ogen)
@@ -962,6 +969,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 	u64 dir_id;
 	u64 gen = 0;
 	u64 ogen;
+	u64 flags;
 	int i;
 	time_t t;
 	u8 uuid[BTRFS_UUID_SIZE];
@@ -1017,11 +1025,12 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 				dir_id = btrfs_stack_root_ref_dirid(ref);
 
 				add_root(root_lookup, sh->objectid, sh->offset,
-					 0, dir_id, name, name_len, 0, 0, 0,
+					 0, 0, dir_id, name, name_len, 0, 0, 0,
 					 NULL);
 			} else if (sh->type == BTRFS_ROOT_ITEM_KEY) {
 				ri = (struct btrfs_root_item *)(args.buf + off);
 				gen = btrfs_root_generation(ri);
+				flags = btrfs_root_flags(ri);
 				if(sh->len >
 				   sizeof(struct btrfs_root_item_v0)) {
 					t = ri->otime.sec;
@@ -1034,8 +1043,8 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 				}
 
 				add_root(root_lookup, sh->objectid, 0,
-					 sh->offset, 0, NULL, 0, ogen, gen, t,
-					 uuid);
+					 sh->offset, flags, 0, NULL, 0, ogen,
+					 gen, t, uuid);
 			}
 
 			off += sh->len;
@@ -1080,9 +1089,17 @@ static int filter_snapshot(struct root_info *ri, void *arg)
 	return !!ri->root_offset;
 }
 
+static int filter_flags(struct root_info *ri, void *arg)
+{
+	u64 flags = *((u64 *)arg);
+
+	return ri->flags & flags;
+}
+
 static btrfs_list_filter_func all_filter_funcs[] = {
 	[BTRFS_LIST_FILTER_ROOTID]		= filter_by_rootid,
 	[BTRFS_LIST_FILTER_SNAPSHOT_ONLY]	= filter_snapshot,
+	[BTRFS_LIST_FILTER_FLAGS]		= filter_flags,
 };
 
 struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void)
diff --git a/btrfs-list.h b/btrfs-list.h
index ca3eb7b..3d6bdc1 100644
--- a/btrfs-list.h
+++ b/btrfs-list.h
@@ -59,6 +59,7 @@ enum btrfs_list_column_enum {
 enum btrfs_list_filter_enum {
 	BTRFS_LIST_FILTER_ROOTID,
 	BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
+	BTRFS_LIST_FILTER_FLAGS,
 	BTRFS_LIST_FILTER_MAX,
 };
 
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index b1cf2bd..0ca1e15 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -259,13 +259,14 @@ static int cmd_subvol_delete(int argc, char **argv)
 }
 
 static const char * const cmd_subvol_list_usage[] = {
-	"btrfs subvolume list [-pu] [-s 0|1] <path>",
+	"btrfs subvolume list [-pur] [-s 0|1] <path>",
 	"List subvolumes (and snapshots)",
 	"",
 	"-p           print parent ID",
 	"-u           print the uuid of subvolumes (and snapshots)",
 	"-s value     list snapshots with generation in ascending/descending order",
 	"             (1: ascending, 0: descending)",
+	"-r           list readonly subvolumes(including snapshots)",
 	NULL
 };
 
@@ -273,6 +274,7 @@ static int cmd_subvol_list(int argc, char **argv)
 {
 	struct btrfs_list_filter_set *filter_set;
 	struct btrfs_list_comparer_set *comparer_set;
+	u64 flags = 0;
 	int fd;
 	int ret;
 	int order;
@@ -283,7 +285,7 @@ static int cmd_subvol_list(int argc, char **argv)
 
 	optind = 1;
 	while(1) {
-		int c = getopt(argc, argv, "ps:u");
+		int c = getopt(argc, argv, "ps:ur");
 		if (c < 0)
 			break;
 
@@ -305,11 +307,18 @@ static int cmd_subvol_list(int argc, char **argv)
 		case 'u':
 			btrfs_list_setup_print_column(BTRFS_LIST_UUID);
 			break;
+		case 'r':
+			flags |= BTRFS_ROOT_SUBVOL_RDONLY;
+			break;
 		default:
 			usage(cmd_subvol_list_usage);
 		}
 	}
 
+	if (flags)
+		btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
+					(void *)&flags);
+
 	if (check_argc_exact(argc - optind, 1))
 		usage(cmd_subvol_list_usage);
 
diff --git a/ctree.h b/ctree.h
index c55d033..4bb66ff 100644
--- a/ctree.h
+++ b/ctree.h
@@ -139,6 +139,8 @@ static int btrfs_csum_sizes[] = { 4, 0 };
 #define BTRFS_FT_XATTR		8
 #define BTRFS_FT_MAX		9
 
+#define BTRFS_ROOT_SUBVOL_RDONLY	(1ULL << 0)
+
 /*
  * the key defines the order in the tree, and so it also defines (optimal)
  * block layout.  objectid corresonds to the inode number.  The flags
-- 
1.7.6.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH V4 7/7] Btrfs-progs: update the manpage entries for the btrfs subvolume list
  2012-09-18 10:35 [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
                   ` (5 preceding siblings ...)
  2012-09-18 11:09 ` [PATCH V4 6/7] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
@ 2012-09-18 11:12 ` Miao Xie
  2012-09-28 12:55 ` [PATCH] Btrfs-progs: make btrfs_list_setup_filter to modify a set filter Anand jain
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 31+ messages in thread
From: Miao Xie @ 2012-09-18 11:12 UTC (permalink / raw)
  To: Linux Btrfs

From: Zhou Bo <zhoub-fnst@cn.fujitsu.com>

This patch adds the introduction of the new option '-r' into the man page of
'btrfs subvolume list' command.

Signed-off-by: Zhou Bo <zhoub-fnst@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
---
Changelog v3 -> v4:
- No change.

Changelog v2 -> v3:
- No change.

Changelog v1 -> v2:
- new patch
---
 man/btrfs.8.in |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/man/btrfs.8.in b/man/btrfs.8.in
index 4b0a9f9..0845b4d 100644
--- a/man/btrfs.8.in
+++ b/man/btrfs.8.in
@@ -11,7 +11,7 @@ btrfs \- control a btrfs filesystem
 .PP
 \fBbtrfs\fP \fBsubvolume create\fP\fI [<dest>/]<name>\fP
 .PP
-\fBbtrfs\fP \fBsubvolume list\fP\fI [-p] <path>\fP
+\fBbtrfs\fP \fBsubvolume list\fP\fI [-pr] <path>\fP
 .PP
 \fBbtrfs\fP \fBsubvolume set-default\fP\fI <id> <path>\fP
 .PP
@@ -108,7 +108,7 @@ 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
+\fBsubvolume list\fR\fI [-pr] <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>
@@ -119,6 +119,7 @@ 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.
+If \fI-r\fR is given, only readonly subvolumes in the filesystem will be listed.
 .TP
 
 \fBsubvolume set-default\fR\fI <id> <path>\fR
-- 
1.7.6.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH V4 6/7] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots
  2012-09-18 11:09 ` [PATCH V4 6/7] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
@ 2012-09-18 15:11   ` Martin Steigerwald
  0 siblings, 0 replies; 31+ messages in thread
From: Martin Steigerwald @ 2012-09-18 15:11 UTC (permalink / raw)
  To: linux-btrfs, miaox

Am Dienstag, 18. September 2012 schrieb Miao Xie:
>  static const char * const cmd_subvol_list_usage[] = {
> -       "btrfs subvolume list [-pu] [-s 0|1] <path>",
> +       "btrfs subvolume list [-pur] [-s 0|1] <path>",
>         "List subvolumes (and snapshots)",
>         "",
>         "-p           print parent ID",
>         "-u           print the uuid of subvolumes (and snapshots)",
>         "-s value     list snapshots with generation in ascending/descending order",
>         "             (1: ascending, 0: descending)",
> +       "-r           list readonly subvolumes(including snapshots)",

I would add an space between "subvolumes" and "(including snapshots)" here.

>         NULL
>  };

(No kernel developer, thus not commenting on the code.)

Thanks,
-- 
Martin 'Helios' Steigerwald - http://www.Lichtvoll.de
GPG: 03B0 0D6C 0040 0710 4AFA  B82F 991B EAAC A599 84C7

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH V4 4/7] Btrfs-progs: fix wrong way to check if the root item contains otime and uuid
  2012-09-18 10:59 ` [PATCH V4 4/7] Btrfs-progs: fix wrong way to check if the root item contains otime and uuid Miao Xie
@ 2012-09-19  1:55   ` Anand Jain
  2012-09-19  2:58     ` Miao Xie
  0 siblings, 1 reply; 31+ messages in thread
From: Anand Jain @ 2012-09-19  1:55 UTC (permalink / raw)
  To: miaox; +Cc: Linux Btrfs


Miao,

> wrong because it is possbile that ->generation may equal to the first
> variant of the next item.

  When can this happen ?  Can you provide a scenario to run through. ?

Thanks, Anand

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH V4 4/7] Btrfs-progs: fix wrong way to check if the root item contains otime and uuid
  2012-09-19  1:55   ` Anand Jain
@ 2012-09-19  2:58     ` Miao Xie
  0 siblings, 0 replies; 31+ messages in thread
From: Miao Xie @ 2012-09-19  2:58 UTC (permalink / raw)
  To: Anand Jain; +Cc: Linux Btrfs

On 	wed, 19 Sep 2012 09:55:19 +0800, Anand Jain wrote:
> 
> Miao,
> 
>> wrong because it is possbile that ->generation may equal to the first
>> variant of the next item.
> 
>  When can this happen ?  Can you provide a scenario to run through. ?

On the old kernel:
# mkfs.btrfs <partition>
# mount <partition> <mnt>
# btrfs sub snap <mnt> <mnt>/snap0
# btrfs sub snap <mnt> <mnt>/snap1
# btrfs sub list -u <mnt>

Thanks
Miao

> 
> Thanks, Anand
> -- 
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH V4 5/7] Btrfs-progs: restructure list_subvolumes
  2012-09-18 11:06 ` [PATCH V4 5/7] Btrfs-progs: restructure list_subvolumes Miao Xie
@ 2012-09-20 12:09   ` David Sterba
  2012-10-09 16:05   ` Alex Lyakas
  1 sibling, 0 replies; 31+ messages in thread
From: David Sterba @ 2012-09-20 12:09 UTC (permalink / raw)
  To: Miao Xie; +Cc: Linux Btrfs

On Tue, Sep 18, 2012 at 07:06:49PM +0800, Miao Xie wrote:
> The current code of list_subvols() has very bad scalability, if we want to
> add new filter conditions or new sort methods, we have to modify lots of code.

I've briefly skimmed through the patch, not a short one, IMO the right
way to go.

david

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH] Btrfs-progs: make btrfs_list_setup_filter to modify a set filter
  2012-09-18 10:35 [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
                   ` (6 preceding siblings ...)
  2012-09-18 11:12 ` [PATCH V4 7/7] Btrfs-progs: update the manpage entries for the btrfs subvolume list Miao Xie
@ 2012-09-28 12:55 ` Anand jain
  2012-10-03  0:03 ` [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Chris Mason
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 31+ messages in thread
From: Anand jain @ 2012-09-28 12:55 UTC (permalink / raw)
  To: linux-btrfs

From: Anand Jain <anand.jain@oracle.com>

Signed-off-by: Anand Jain <anand.jain@oracle.com>
---
 btrfs-list.c |   50 ++++++++++++++++++++++++++++++++------------------
 1 files changed, 32 insertions(+), 18 deletions(-)

diff --git a/btrfs-list.c b/btrfs-list.c
index e5f0f96..b1c9714 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -1213,37 +1213,51 @@ void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set)
 	free(filter_set);
 }
 
+static int btrfs_list_is_filter_set(struct btrfs_list_filter_set *fset,
+			enum btrfs_list_filter_enum filter)
+{
+	int i;
+	for (i=0; i < fset->nfilters; i++) {
+		if (fset->filters[i].filter_func == all_filter_funcs[filter])
+			return i;
+	}
+	return -1;
+}
+
 int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
 			    enum btrfs_list_filter_enum filter, u64 data)
 {
 	struct btrfs_list_filter_set *set = *filter_set;
 	int size;
+	int nfilter;
 
 	BUG_ON(!set);
 	BUG_ON(filter >= BTRFS_LIST_FILTER_MAX);
 	BUG_ON(set->nfilters > set->total);
 
-	if (set->nfilters == set->total) {
-		size = set->total + BTRFS_LIST_NFILTERS_INCREASE;
-		size = sizeof(*set) + size * sizeof(struct btrfs_list_filter);
-		set = realloc(set, size);
-		if (!set) {
-			fprintf(stderr, "memory allocation failed\n");
-			exit(1);
-		}
+	nfilter = btrfs_list_is_filter_set(set, filter);
+	if (nfilter < 0) {
+		if (set->nfilters == set->total) {
+			size = set->total + BTRFS_LIST_NFILTERS_INCREASE;
+			size = sizeof(*set) + size * sizeof(struct btrfs_list_filter);
+			set = realloc(set, size);
+			if (!set) {
+				fprintf(stderr, "memory allocation failed\n");
+				exit(1);
+			}
 
-		memset(&set->filters[set->total], 0,
-		       BTRFS_LIST_NFILTERS_INCREASE *
-		       sizeof(struct btrfs_list_filter));
-		set->total += BTRFS_LIST_NFILTERS_INCREASE;
-		*filter_set = set;
+			memset(&set->filters[set->total], 0,
+			       BTRFS_LIST_NFILTERS_INCREASE *
+			       sizeof(struct btrfs_list_filter));
+			set->total += BTRFS_LIST_NFILTERS_INCREASE;
+			*filter_set = set;
+		}
+		nfilter = set->nfilters;
+		set->nfilters++;
 	}
 
-	BUG_ON(set->filters[set->nfilters].filter_func);
-
-	set->filters[set->nfilters].filter_func = all_filter_funcs[filter];
-	set->filters[set->nfilters].data = data;
-	set->nfilters++;
+	set->filters[nfilter].filter_func = all_filter_funcs[filter];
+	set->filters[nfilter].data = data;
 	return 0;
 }
 
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots
  2012-09-18 10:35 [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
                   ` (7 preceding siblings ...)
  2012-09-28 12:55 ` [PATCH] Btrfs-progs: make btrfs_list_setup_filter to modify a set filter Anand jain
@ 2012-10-03  0:03 ` Chris Mason
  2012-10-09  6:01   ` Miao Xie
  2012-10-04 10:12 ` [PATCH] Btrfs-progs: add parent uuid for snapshots Anand jain
  2012-10-05  2:25 ` [PATCH] Btrfs-progs: Corrections and additions to the btrfs man page Anand jain
  10 siblings, 1 reply; 31+ messages in thread
From: Chris Mason @ 2012-10-03  0:03 UTC (permalink / raw)
  To: Miao Xie; +Cc: Linux Btrfs

On Tue, Sep 18, 2012 at 04:35:25AM -0600, Miao Xie wrote:
> We want 'btrfs subvolume list' only to list readonly subvolumes, this patch set
> introduces a new option 'r' to implement it.
> 
> You can use the command like that:
> 
> 	btrfs subvolume list -r <mnt>
> 
> Changelog v3 -> v4:
> - modify the check method which is used to check if btrfs_root_item contains otime and uuid
>   or not.
> - add filter set and comparer set which are used to manage the filters and comparers specified
>   by the users.
> - re-base the read-only subvolume list function.
> 
> Changelog v2 -> v3:
> - re-implement list_subvols()
> - re-implement this read-only subvolume list function based on the new list_subvols()
> 
> Changelog v1 -> v2:
> - address the comments from Goffredo Baroncelli
>   i,   change the changelog of the patches and make them more elaborate.
>   ii,  move the function declarations to a new head file.
>   iii, add the introduction of the new option 'r' into the man page
> 
> We can pull the patches from the URL
> 
> 	git://github.com/miaoxie/btrfs-progs.git master
> 
> This patchset is against the patches of Liu Bo, Anand Jain and mine which were sent by several
> days ago. And we also can pull those patches from the above URL.

These are all really useful additions!  How do you plan on using the
table option? (just curious).

I've got it pulled in here and it is working properly on my send/receive
backup volume (with lots of snapshots).

-chris

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH] Btrfs-progs: add parent uuid for snapshots
  2012-09-18 10:35 [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
                   ` (8 preceding siblings ...)
  2012-10-03  0:03 ` [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Chris Mason
@ 2012-10-04 10:12 ` Anand jain
  2012-10-04 12:00   ` Dong Robin
  2012-10-05  2:25 ` [PATCH] Btrfs-progs: Corrections and additions to the btrfs man page Anand jain
  10 siblings, 1 reply; 31+ messages in thread
From: Anand jain @ 2012-10-04 10:12 UTC (permalink / raw)
  To: linux-btrfs

From: Anand Jain <anand.jain@oracle.com>

Signed-off-by: Anand Jain <anand.jain@oracle.com>
---
 btrfs-list.c     |   32 +++++++++++++++++++++++++++-----
 btrfs-list.h     |    1 +
 cmds-subvolume.c |    6 +++++-
 3 files changed, 33 insertions(+), 6 deletions(-)

diff --git a/btrfs-list.c b/btrfs-list.c
index b1c9714..9d5a11f 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -80,6 +80,7 @@ struct root_info {
 	time_t otime;
 
 	u8 uuid[BTRFS_UUID_SIZE];
+	u8 puuid[BTRFS_UUID_SIZE];
 
 	/* 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.
@@ -128,6 +129,11 @@ struct {
 		.need_print	= 0,
 	},
 	{
+		.name		= "puuid",
+		.column_name	= "PUUID",
+		.need_print	= 0,
+	},
+	{
 		.name		= "uuid",
 		.column_name	= "UUID",
 		.need_print	= 0,
@@ -435,7 +441,7 @@ static struct root_info *root_tree_search(struct root_lookup *root_tree,
 static int update_root(struct root_lookup *root_lookup,
 		       u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
 		       u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
-		       time_t ot, void *uuid)
+		       time_t ot, void *uuid, void *puuid)
 {
 	struct root_info *ri;
 
@@ -472,6 +478,8 @@ static int update_root(struct root_lookup *root_lookup,
 		ri->otime = ot;
 	if (uuid)
 		memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
+	if (puuid)
+		memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
 
 	return 0;
 }
@@ -489,17 +497,18 @@ static int update_root(struct root_lookup *root_lookup,
  * gen: the current generation of the root
  * ot: the original time(create time) of the root
  * uuid: uuid of the root
+ * puuid: uuid of the root parent if any
  */
 static int add_root(struct root_lookup *root_lookup,
 		    u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
 		    u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
-		    time_t ot, void *uuid)
+		    time_t ot, void *uuid, void *puuid)
 {
 	struct root_info *ri;
 	int ret;
 
 	ret = update_root(root_lookup, root_id, ref_tree, root_offset, flags,
-			  dir_id, name, name_len, ogen, gen, ot, uuid);
+			  dir_id, name, name_len, ogen, gen, ot, uuid, puuid);
 	if (!ret)
 		return 0;
 
@@ -540,6 +549,9 @@ static int add_root(struct root_lookup *root_lookup,
 	if (uuid) 
 		memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
 
+	if (puuid)
+		memcpy(&ri->puuid, puuid, BTRFS_UUID_SIZE);
+
 	ret = root_tree_insert(root_lookup, ri);
 	if (ret) {
 		printf("failed to insert tree %llu\n", (unsigned long long)root_id);
@@ -1022,6 +1034,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 	int i;
 	time_t t;
 	u8 uuid[BTRFS_UUID_SIZE];
+	u8 puuid[BTRFS_UUID_SIZE];
 
 	root_lookup_init(root_lookup);
 	memset(&args, 0, sizeof(args));
@@ -1075,7 +1088,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 
 				add_root(root_lookup, sh->objectid, sh->offset,
 					 0, 0, dir_id, name, name_len, 0, 0, 0,
-					 NULL);
+					 NULL, NULL);
 			} else if (sh->type == BTRFS_ROOT_ITEM_KEY) {
 				ri = (struct btrfs_root_item *)(args.buf + off);
 				gen = btrfs_root_generation(ri);
@@ -1085,15 +1098,17 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 					t = ri->otime.sec;
 					ogen = btrfs_root_otransid(ri);
 					memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
+					memcpy(puuid, ri->parent_uuid, BTRFS_UUID_SIZE);
 				} else {
 					t = 0;
 					ogen = 0;
 					memset(uuid, 0, BTRFS_UUID_SIZE);
+					memset(puuid, 0, BTRFS_UUID_SIZE);
 				}
 
 				add_root(root_lookup, sh->objectid, 0,
 					 sh->offset, flags, 0, NULL, 0, ogen,
-					 gen, t, uuid);
+					 gen, t, uuid, puuid);
 			}
 
 			off += sh->len;
@@ -1361,6 +1376,13 @@ static void print_subvolume_column(struct root_info *subv,
 			uuid_unparse(subv->uuid, uuidparse);
 		printf("%s", uuidparse);
 		break;
+	case BTRFS_LIST_PUUID:
+		if (uuid_is_null(subv->puuid))
+			strcpy(uuidparse, "-");
+		else
+			uuid_unparse(subv->puuid, uuidparse);
+		printf("%s", uuidparse);
+		break;
 	case BTRFS_LIST_PATH:
 		BUG_ON(!subv->full_path);
 		printf("%s", subv->full_path);
diff --git a/btrfs-list.h b/btrfs-list.h
index cde4b3c..a32545f 100644
--- a/btrfs-list.h
+++ b/btrfs-list.h
@@ -53,6 +53,7 @@ enum btrfs_list_column_enum {
 	BTRFS_LIST_PARENT,
 	BTRFS_LIST_TOP_LEVEL,
 	BTRFS_LIST_OTIME,
+	BTRFS_LIST_PUUID,
 	BTRFS_LIST_UUID,
 	BTRFS_LIST_PATH,
 	BTRFS_LIST_ALL,
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 58e8983..3690ca5 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -267,6 +267,7 @@ static const char * const cmd_subvol_list_usage[] = {
 	"-p           print parent ID",
 	"-a           print all the subvolumes in the filesystem.",
 	"-u           print the uuid of subvolumes (and snapshots)",
+	"-P           print the parent uuid of snapshots",
 	"-t           print the result as a table",
 	"-s           list snapshots only in the filesystem",
 	"-r           list readonly subvolumes (including snapshots)",
@@ -306,7 +307,7 @@ static int cmd_subvol_list(int argc, char **argv)
 	optind = 1;
 	while(1) {
 		c = getopt_long(argc, argv,
-				    "apsurg:c:t", long_options, NULL);
+				    "apsuPrg:c:t", long_options, NULL);
 		if (c < 0)
 			break;
 
@@ -330,6 +331,9 @@ static int cmd_subvol_list(int argc, char **argv)
 		case 'u':
 			btrfs_list_setup_print_column(BTRFS_LIST_UUID);
 			break;
+		case 'P':
+			btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
+			break;
 		case 'r':
 			flags |= BTRFS_ROOT_SUBVOL_RDONLY;
 			break;
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH] Btrfs-progs: add parent uuid for snapshots
  2012-10-04 10:12 ` [PATCH] Btrfs-progs: add parent uuid for snapshots Anand jain
@ 2012-10-04 12:00   ` Dong Robin
  2012-10-05  2:27     ` Anand Jain
  0 siblings, 1 reply; 31+ messages in thread
From: Dong Robin @ 2012-10-04 12:00 UTC (permalink / raw)
  To: Anand jain; +Cc: linux-btrfs

2012/10/4 Anand jain <Anand.Jain@oracle.com>:
> From: Anand Jain <anand.jain@oracle.com>
>
> Signed-off-by: Anand Jain <anand.jain@oracle.com>
> ---
>  btrfs-list.c     |   32 +++++++++++++++++++++++++++-----
>  btrfs-list.h     |    1 +
>  cmds-subvolume.c |    6 +++++-
>  3 files changed, 33 insertions(+), 6 deletions(-)
>
> diff --git a/btrfs-list.c b/btrfs-list.c
> index b1c9714..9d5a11f 100644
> --- a/btrfs-list.c
> +++ b/btrfs-list.c
> @@ -80,6 +80,7 @@ struct root_info {
>         time_t otime;
>
>         u8 uuid[BTRFS_UUID_SIZE];
> +       u8 puuid[BTRFS_UUID_SIZE];
>
>         /* 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.
> @@ -128,6 +129,11 @@ struct {
>                 .need_print     = 0,
>         },
>         {
> +               .name           = "puuid",
> +               .column_name    = "PUUID",
> +               .need_print     = 0,
> +       },
> +       {
>                 .name           = "uuid",
>                 .column_name    = "UUID",
>                 .need_print     = 0,
> @@ -435,7 +441,7 @@ static struct root_info *root_tree_search(struct root_lookup *root_tree,
>  static int update_root(struct root_lookup *root_lookup,
>                        u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
>                        u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
> -                      time_t ot, void *uuid)
> +                      time_t ot, void *uuid, void *puuid)
>  {
>         struct root_info *ri;
>
> @@ -472,6 +478,8 @@ static int update_root(struct root_lookup *root_lookup,
>                 ri->otime = ot;
>         if (uuid)
>                 memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
> +       if (puuid)
> +               memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);

I think here is "&ri->puuid"

>
>         return 0;
>  }
> @@ -489,17 +497,18 @@ static int update_root(struct root_lookup *root_lookup,
>   * gen: the current generation of the root
>   * ot: the original time(create time) of the root
>   * uuid: uuid of the root
> + * puuid: uuid of the root parent if any
>   */
>  static int add_root(struct root_lookup *root_lookup,
>                     u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
>                     u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
> -                   time_t ot, void *uuid)
> +                   time_t ot, void *uuid, void *puuid)
>  {
>         struct root_info *ri;
>         int ret;
>
>         ret = update_root(root_lookup, root_id, ref_tree, root_offset, flags,
> -                         dir_id, name, name_len, ogen, gen, ot, uuid);
> +                         dir_id, name, name_len, ogen, gen, ot, uuid, puuid);
>         if (!ret)
>                 return 0;
>
> @@ -540,6 +549,9 @@ static int add_root(struct root_lookup *root_lookup,
>         if (uuid)
>                 memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
>
> +       if (puuid)
> +               memcpy(&ri->puuid, puuid, BTRFS_UUID_SIZE);
> +
>         ret = root_tree_insert(root_lookup, ri);
>         if (ret) {
>                 printf("failed to insert tree %llu\n", (unsigned long long)root_id);
> @@ -1022,6 +1034,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>         int i;
>         time_t t;
>         u8 uuid[BTRFS_UUID_SIZE];
> +       u8 puuid[BTRFS_UUID_SIZE];
>
>         root_lookup_init(root_lookup);
>         memset(&args, 0, sizeof(args));
> @@ -1075,7 +1088,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>
>                                 add_root(root_lookup, sh->objectid, sh->offset,
>                                          0, 0, dir_id, name, name_len, 0, 0, 0,
> -                                        NULL);
> +                                        NULL, NULL);
>                         } else if (sh->type == BTRFS_ROOT_ITEM_KEY) {
>                                 ri = (struct btrfs_root_item *)(args.buf + off);
>                                 gen = btrfs_root_generation(ri);
> @@ -1085,15 +1098,17 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>                                         t = ri->otime.sec;
>                                         ogen = btrfs_root_otransid(ri);
>                                         memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
> +                                       memcpy(puuid, ri->parent_uuid, BTRFS_UUID_SIZE);
>                                 } else {
>                                         t = 0;
>                                         ogen = 0;
>                                         memset(uuid, 0, BTRFS_UUID_SIZE);
> +                                       memset(puuid, 0, BTRFS_UUID_SIZE);
>                                 }
>
>                                 add_root(root_lookup, sh->objectid, 0,
>                                          sh->offset, flags, 0, NULL, 0, ogen,
> -                                        gen, t, uuid);
> +                                        gen, t, uuid, puuid);
>                         }
>
>                         off += sh->len;
> @@ -1361,6 +1376,13 @@ static void print_subvolume_column(struct root_info *subv,
>                         uuid_unparse(subv->uuid, uuidparse);
>                 printf("%s", uuidparse);
>                 break;
> +       case BTRFS_LIST_PUUID:
> +               if (uuid_is_null(subv->puuid))
> +                       strcpy(uuidparse, "-");
> +               else
> +                       uuid_unparse(subv->puuid, uuidparse);
> +               printf("%s", uuidparse);
> +               break;
>         case BTRFS_LIST_PATH:
>                 BUG_ON(!subv->full_path);
>                 printf("%s", subv->full_path);
> diff --git a/btrfs-list.h b/btrfs-list.h
> index cde4b3c..a32545f 100644
> --- a/btrfs-list.h
> +++ b/btrfs-list.h
> @@ -53,6 +53,7 @@ enum btrfs_list_column_enum {
>         BTRFS_LIST_PARENT,
>         BTRFS_LIST_TOP_LEVEL,
>         BTRFS_LIST_OTIME,
> +       BTRFS_LIST_PUUID,
>         BTRFS_LIST_UUID,
>         BTRFS_LIST_PATH,
>         BTRFS_LIST_ALL,
> diff --git a/cmds-subvolume.c b/cmds-subvolume.c
> index 58e8983..3690ca5 100644
> --- a/cmds-subvolume.c
> +++ b/cmds-subvolume.c
> @@ -267,6 +267,7 @@ static const char * const cmd_subvol_list_usage[] = {
>         "-p           print parent ID",
>         "-a           print all the subvolumes in the filesystem.",
>         "-u           print the uuid of subvolumes (and snapshots)",
> +       "-P           print the parent uuid of snapshots",
>         "-t           print the result as a table",
>         "-s           list snapshots only in the filesystem",
>         "-r           list readonly subvolumes (including snapshots)",
> @@ -306,7 +307,7 @@ static int cmd_subvol_list(int argc, char **argv)
>         optind = 1;
>         while(1) {
>                 c = getopt_long(argc, argv,
> -                                   "apsurg:c:t", long_options, NULL);
> +                                   "apsuPrg:c:t", long_options, NULL);
>                 if (c < 0)
>                         break;
>
> @@ -330,6 +331,9 @@ static int cmd_subvol_list(int argc, char **argv)
>                 case 'u':
>                         btrfs_list_setup_print_column(BTRFS_LIST_UUID);
>                         break;
> +               case 'P':
> +                       btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
> +                       break;
>                 case 'r':
>                         flags |= BTRFS_ROOT_SUBVOL_RDONLY;
>                         break;
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
--
Best Regard
Robin Dong

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH] Btrfs-progs: Corrections and additions to the btrfs man page
  2012-09-18 10:35 [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
                   ` (9 preceding siblings ...)
  2012-10-04 10:12 ` [PATCH] Btrfs-progs: add parent uuid for snapshots Anand jain
@ 2012-10-05  2:25 ` Anand jain
  2012-10-05  2:25   ` [PATCH] Btrfs-progs: Update btrfs man page for -P option Anand jain
  2012-10-05  2:25   ` [PATCH V2] Btrfs-progs: add parent uuid for snapshots Anand jain
  10 siblings, 2 replies; 31+ messages in thread
From: Anand jain @ 2012-10-05  2:25 UTC (permalink / raw)
  To: linux-btrfs

From: Anand Jain <anand.jain@oracle.com>

Signed-off-by: Anand Jain <anand.jain@oracle.com>
---
 man/btrfs.8.in |   10 ++++++++--
 1 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/man/btrfs.8.in b/man/btrfs.8.in
index 6d6c70a..71af35c 100644
--- a/man/btrfs.8.in
+++ b/man/btrfs.8.in
@@ -126,9 +126,15 @@ and top level. The parent's ID may be used at mount time via the
 
 \fB-a\fP print all the subvolumes in the filesystem.
 
-\fB-r\fP only readonly subvolumes in the filesystem wille be listed.
+\fB-r\fP only readonly subvolumes in the filesystem will be listed.
 
-\fB-s\fP only snapshot subvolumes in the filesystem will  be listed.
+\fB-s\fP only snapshot subvolumes in the filesystem will be listed.
+
+\fB-u\fP print the uuid of subvolumes (and snapshots).
+
+\fB-P\fP print parent uuid of snapshots.
+
+\fB-p\fP print parent ID.
 
 \fB-g [+|-]value\fP
 list subvolumes in the filesystem that its generation is
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH] Btrfs-progs: Update btrfs man page for -P option
  2012-10-05  2:25 ` [PATCH] Btrfs-progs: Corrections and additions to the btrfs man page Anand jain
@ 2012-10-05  2:25   ` Anand jain
  2012-10-05  2:25   ` [PATCH V2] Btrfs-progs: add parent uuid for snapshots Anand jain
  1 sibling, 0 replies; 31+ messages in thread
From: Anand jain @ 2012-10-05  2:25 UTC (permalink / raw)
  To: linux-btrfs

From: Anand Jain <anand.jain@oracle.com>

Signed-off-by: Anand Jain <anand.jain@oracle.com>
---
 man/btrfs.8.in |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/man/btrfs.8.in b/man/btrfs.8.in
index 29f93cf..71af35c 100644
--- a/man/btrfs.8.in
+++ b/man/btrfs.8.in
@@ -132,6 +132,8 @@ and top level. The parent's ID may be used at mount time via the
 
 \fB-u\fP print the uuid of subvolumes (and snapshots).
 
+\fB-P\fP print parent uuid of snapshots.
+
 \fB-p\fP print parent ID.
 
 \fB-g [+|-]value\fP
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH V2] Btrfs-progs: add parent uuid for snapshots
  2012-10-05  2:25 ` [PATCH] Btrfs-progs: Corrections and additions to the btrfs man page Anand jain
  2012-10-05  2:25   ` [PATCH] Btrfs-progs: Update btrfs man page for -P option Anand jain
@ 2012-10-05  2:25   ` Anand jain
  2012-10-09 15:44     ` David Sterba
  1 sibling, 1 reply; 31+ messages in thread
From: Anand jain @ 2012-10-05  2:25 UTC (permalink / raw)
  To: linux-btrfs

From: Anand Jain <anand.jain@oracle.com>

Reviewed-by: Dong Robin <robin.k.dong@gmail.com>
Signed-off-by: Anand Jain <anand.jain@oracle.com>
---
 btrfs-list.c     |   32 +++++++++++++++++++++++++++-----
 btrfs-list.h     |    1 +
 cmds-subvolume.c |    6 +++++-
 3 files changed, 33 insertions(+), 6 deletions(-)

diff --git a/btrfs-list.c b/btrfs-list.c
index b1c9714..2b7d79e 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -80,6 +80,7 @@ struct root_info {
 	time_t otime;
 
 	u8 uuid[BTRFS_UUID_SIZE];
+	u8 puuid[BTRFS_UUID_SIZE];
 
 	/* 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.
@@ -128,6 +129,11 @@ struct {
 		.need_print	= 0,
 	},
 	{
+		.name		= "puuid",
+		.column_name	= "PUUID",
+		.need_print	= 0,
+	},
+	{
 		.name		= "uuid",
 		.column_name	= "UUID",
 		.need_print	= 0,
@@ -435,7 +441,7 @@ static struct root_info *root_tree_search(struct root_lookup *root_tree,
 static int update_root(struct root_lookup *root_lookup,
 		       u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
 		       u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
-		       time_t ot, void *uuid)
+		       time_t ot, void *uuid, void *puuid)
 {
 	struct root_info *ri;
 
@@ -472,6 +478,8 @@ static int update_root(struct root_lookup *root_lookup,
 		ri->otime = ot;
 	if (uuid)
 		memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
+	if (puuid)
+		memcpy(&ri->puuid, puuid, BTRFS_UUID_SIZE);
 
 	return 0;
 }
@@ -489,17 +497,18 @@ static int update_root(struct root_lookup *root_lookup,
  * gen: the current generation of the root
  * ot: the original time(create time) of the root
  * uuid: uuid of the root
+ * puuid: uuid of the root parent if any
  */
 static int add_root(struct root_lookup *root_lookup,
 		    u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
 		    u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
-		    time_t ot, void *uuid)
+		    time_t ot, void *uuid, void *puuid)
 {
 	struct root_info *ri;
 	int ret;
 
 	ret = update_root(root_lookup, root_id, ref_tree, root_offset, flags,
-			  dir_id, name, name_len, ogen, gen, ot, uuid);
+			  dir_id, name, name_len, ogen, gen, ot, uuid, puuid);
 	if (!ret)
 		return 0;
 
@@ -540,6 +549,9 @@ static int add_root(struct root_lookup *root_lookup,
 	if (uuid) 
 		memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
 
+	if (puuid)
+		memcpy(&ri->puuid, puuid, BTRFS_UUID_SIZE);
+
 	ret = root_tree_insert(root_lookup, ri);
 	if (ret) {
 		printf("failed to insert tree %llu\n", (unsigned long long)root_id);
@@ -1022,6 +1034,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 	int i;
 	time_t t;
 	u8 uuid[BTRFS_UUID_SIZE];
+	u8 puuid[BTRFS_UUID_SIZE];
 
 	root_lookup_init(root_lookup);
 	memset(&args, 0, sizeof(args));
@@ -1075,7 +1088,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 
 				add_root(root_lookup, sh->objectid, sh->offset,
 					 0, 0, dir_id, name, name_len, 0, 0, 0,
-					 NULL);
+					 NULL, NULL);
 			} else if (sh->type == BTRFS_ROOT_ITEM_KEY) {
 				ri = (struct btrfs_root_item *)(args.buf + off);
 				gen = btrfs_root_generation(ri);
@@ -1085,15 +1098,17 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 					t = ri->otime.sec;
 					ogen = btrfs_root_otransid(ri);
 					memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
+					memcpy(puuid, ri->parent_uuid, BTRFS_UUID_SIZE);
 				} else {
 					t = 0;
 					ogen = 0;
 					memset(uuid, 0, BTRFS_UUID_SIZE);
+					memset(puuid, 0, BTRFS_UUID_SIZE);
 				}
 
 				add_root(root_lookup, sh->objectid, 0,
 					 sh->offset, flags, 0, NULL, 0, ogen,
-					 gen, t, uuid);
+					 gen, t, uuid, puuid);
 			}
 
 			off += sh->len;
@@ -1361,6 +1376,13 @@ static void print_subvolume_column(struct root_info *subv,
 			uuid_unparse(subv->uuid, uuidparse);
 		printf("%s", uuidparse);
 		break;
+	case BTRFS_LIST_PUUID:
+		if (uuid_is_null(subv->puuid))
+			strcpy(uuidparse, "-");
+		else
+			uuid_unparse(subv->puuid, uuidparse);
+		printf("%s", uuidparse);
+		break;
 	case BTRFS_LIST_PATH:
 		BUG_ON(!subv->full_path);
 		printf("%s", subv->full_path);
diff --git a/btrfs-list.h b/btrfs-list.h
index cde4b3c..a32545f 100644
--- a/btrfs-list.h
+++ b/btrfs-list.h
@@ -53,6 +53,7 @@ enum btrfs_list_column_enum {
 	BTRFS_LIST_PARENT,
 	BTRFS_LIST_TOP_LEVEL,
 	BTRFS_LIST_OTIME,
+	BTRFS_LIST_PUUID,
 	BTRFS_LIST_UUID,
 	BTRFS_LIST_PATH,
 	BTRFS_LIST_ALL,
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 58e8983..3690ca5 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -267,6 +267,7 @@ static const char * const cmd_subvol_list_usage[] = {
 	"-p           print parent ID",
 	"-a           print all the subvolumes in the filesystem.",
 	"-u           print the uuid of subvolumes (and snapshots)",
+	"-P           print the parent uuid of snapshots",
 	"-t           print the result as a table",
 	"-s           list snapshots only in the filesystem",
 	"-r           list readonly subvolumes (including snapshots)",
@@ -306,7 +307,7 @@ static int cmd_subvol_list(int argc, char **argv)
 	optind = 1;
 	while(1) {
 		c = getopt_long(argc, argv,
-				    "apsurg:c:t", long_options, NULL);
+				    "apsuPrg:c:t", long_options, NULL);
 		if (c < 0)
 			break;
 
@@ -330,6 +331,9 @@ static int cmd_subvol_list(int argc, char **argv)
 		case 'u':
 			btrfs_list_setup_print_column(BTRFS_LIST_UUID);
 			break;
+		case 'P':
+			btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
+			break;
 		case 'r':
 			flags |= BTRFS_ROOT_SUBVOL_RDONLY;
 			break;
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH] Btrfs-progs: add parent uuid for snapshots
  2012-10-04 12:00   ` Dong Robin
@ 2012-10-05  2:27     ` Anand Jain
  0 siblings, 0 replies; 31+ messages in thread
From: Anand Jain @ 2012-10-05  2:27 UTC (permalink / raw)
  To: Dong Robin; +Cc: linux-btrfs


Dong,

>> +       if (puuid)
>> +               memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
>
> I think here is "&ri->puuid"

  Sorry my mistake. Thanks for the review.

-Anand

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots
  2012-10-03  0:03 ` [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Chris Mason
@ 2012-10-09  6:01   ` Miao Xie
  2012-10-09 15:57     ` David Sterba
  0 siblings, 1 reply; 31+ messages in thread
From: Miao Xie @ 2012-10-09  6:01 UTC (permalink / raw)
  To: Chris Mason, Linux Btrfs

On Tue, 2 Oct 2012 20:03:04 -0400, Chris Mason wrote:
> On Tue, Sep 18, 2012 at 04:35:25AM -0600, Miao Xie wrote:
>> We want 'btrfs subvolume list' only to list readonly subvolumes, this patch set
>> introduces a new option 'r' to implement it.
>>
>> You can use the command like that:
>>
>> 	btrfs subvolume list -r <mnt>
>>
>> Changelog v3 -> v4:
>> - modify the check method which is used to check if btrfs_root_item contains otime and uuid
>>   or not.
>> - add filter set and comparer set which are used to manage the filters and comparers specified
>>   by the users.
>> - re-base the read-only subvolume list function.
>>
>> Changelog v2 -> v3:
>> - re-implement list_subvols()
>> - re-implement this read-only subvolume list function based on the new list_subvols()
>>
>> Changelog v1 -> v2:
>> - address the comments from Goffredo Baroncelli
>>   i,   change the changelog of the patches and make them more elaborate.
>>   ii,  move the function declarations to a new head file.
>>   iii, add the introduction of the new option 'r' into the man page
>>
>> We can pull the patches from the URL
>>
>> 	git://github.com/miaoxie/btrfs-progs.git master
>>
>> This patchset is against the patches of Liu Bo, Anand Jain and mine which were sent by several
>> days ago. And we also can pull those patches from the above URL.
> 
> These are all really useful additions!  How do you plan on using the
> table option? (just curious).

It is just used to make the output be read easily. The old output always throw
out lots of inessential repeated strings, it is very ugly and make us giddy. So
we add this option to make the users happy.

Thanks
Miao

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH V2] Btrfs-progs: add parent uuid for snapshots
  2012-10-05  2:25   ` [PATCH V2] Btrfs-progs: add parent uuid for snapshots Anand jain
@ 2012-10-09 15:44     ` David Sterba
  2012-10-16  7:00       ` [PATCH V3] " Anand jain
  2012-10-16  7:03       ` [PATCH V2] Btrfs-progs: add parent uuid for snapshots Anand Jain
  0 siblings, 2 replies; 31+ messages in thread
From: David Sterba @ 2012-10-09 15:44 UTC (permalink / raw)
  To: Anand jain; +Cc: linux-btrfs

On Fri, Oct 05, 2012 at 10:25:22AM +0800, Anand jain wrote:
> @@ -128,6 +129,11 @@ struct {
>  		.need_print	= 0,
>  	},
>  	{
> +		.name		= "puuid",
> +		.column_name	= "PUUID",

the capitalized 'P' looks like it's part of the UUID abbreviation. The
UUIDs are long, I think you can print 'parent UUID' in the header, the
name for command line argument 'puuid' is understable.

> +		.need_print	= 0,
> +	},
> +	{
>  		.name		= "uuid",
>  		.column_name	= "UUID",
>  		.need_print	= 0,

> --- a/cmds-subvolume.c
> +++ b/cmds-subvolume.c
> @@ -267,6 +267,7 @@ static const char * const cmd_subvol_list_usage[] = {
>  	"-p           print parent ID",
>  	"-a           print all the subvolumes in the filesystem.",
>  	"-u           print the uuid of subvolumes (and snapshots)",
> +	"-P           print the parent uuid of snapshots",

This clashes with my efforts to make the options consistent so that we
can have a lowercase for column selection and uppercase for filter. In
case of the parent UUID,  it makes sense to filter by it, eg when we
have a hierarchy of subvolumes that keep the same structure but is
replicated several times.

I suggest to pick a different letter than 'P', say 'q'. (-q is usually
used for 'no verbose output' in utilities, but it does not make much
sense in context of 'subvol list' so I hope it's ok from the UI POV).

>  	"-t           print the result as a table",
>  	"-s           list snapshots only in the filesystem",
>  	"-r           list readonly subvolumes (including snapshots)",

Thanks,
david

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots
  2012-10-09  6:01   ` Miao Xie
@ 2012-10-09 15:57     ` David Sterba
  0 siblings, 0 replies; 31+ messages in thread
From: David Sterba @ 2012-10-09 15:57 UTC (permalink / raw)
  To: Miao Xie; +Cc: Chris Mason, Linux Btrfs

On Tue, Oct 09, 2012 at 02:01:18PM +0800, Miao Xie wrote:
> On Tue, 2 Oct 2012 20:03:04 -0400, Chris Mason wrote:
> > These are all really useful additions!  How do you plan on using the
> > table option? (just curious).
> 
> It is just used to make the output be read easily. The old output always throw
> out lots of inessential repeated strings, it is very ugly and make us giddy. So
> we add this option to make the users happy.

I really like the tabular output, thanks!

david

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH V4 5/7] Btrfs-progs: restructure list_subvolumes
  2012-09-18 11:06 ` [PATCH V4 5/7] Btrfs-progs: restructure list_subvolumes Miao Xie
  2012-09-20 12:09   ` David Sterba
@ 2012-10-09 16:05   ` Alex Lyakas
  2012-10-10  2:12     ` Miao Xie
  1 sibling, 1 reply; 31+ messages in thread
From: Alex Lyakas @ 2012-10-09 16:05 UTC (permalink / raw)
  To: miaox; +Cc: linux-btrfs

Hi Miao,
I have some trouble with this code.

The problem that I see is after subvolume deletion. What happens is
that __list_subvol_search() is called at the point, at which ROOT_ITEM
still exists in the root tree, but ROOT_BACKREF does not exist
anymore. I think this is because btrfs_ioctl_snap_destroy() calls
btrfs_unlink_subvol(), which calls btrfs_del_root_ref(), which deletes
ROOT_BACKREF, but ROOT_ITEM still exists until transaction is
committed.

So what happens is that we end up with struct root_info that has
ref_tree==0 (and dir_id==0).
So later, when __list_subvol_fill_paths() is called, it fails to
perform BTRFS_IOC_INO_LOOKUP because of ref_tree==0. As a result,
subvol_uuid_search_init fails and everything stops.

Previously, before your change, root_info was not added until we see
ROOT_BACKREF in the tree.

How do you think this should be fixed?

Also, is there a way to issue a subvolume deletion and make sure that
it was deleted? I see that btrfs_ioctl_snap_destroy() calls
end_transaction() but not commit_transaction(). Moreover, there is no
way for the caller to know the transid, otherwise we could issue
BTRFS_IOC_WAIT_SYNC.

Thanks,
Alex.



On Tue, Sep 18, 2012 at 1:06 PM, Miao Xie <miaox@cn.fujitsu.com> wrote:
> The current code of list_subvols() has very bad scalability, if we want to
> add new filter conditions or new sort methods, we have to modify lots of code.
>
> Beside that, the most code of list_snapshots() is similar to list_subvols(),
>
> So I restructure list_subvols(), and split the subvolume filter function,
> the subvolume sort function and the output function from list_subvols().
> In order to implement it, we defined some importtant structures:
> struct btrfs_list_filter {
>         btrfs_list_filter_func filter_func;
>         void *data;
> };
>
> struct btrfs_list_comparer {
>         btrfs_list_comp_func comp_func;
>         int is_descending;
> };
>
> struct {
>         char    *name;
>         char    *column_name;
>         int     need_print;
> } btrfs_list_columns[];
>
> If we want to add a new filter condition, we can choose a suitable filter
> function, or implement a new filter function[1], and add it into a set of
> the filters, and then pass the filter set into list_subvols(). We also can
> mix several filters (just add those filters into the set, and pass the set
> into list_subvols()) if the users specify two or more filter conditions.
>
> The subvolume sort function is similar to the subvolume filter function. The
> differentiation is the order of comparers in the array which is passed into
> list_subvols() show us the priority of the sort methods.
>
> The output function is different with the above two functions, we define a
> array to manage all the columns that can be outputed, and use a member variant
> (->need_print) to control the output of the relative column. Some columns are
> outputed by default. But we can change it according to the requirement of the
> users.
>
> After appling this patch, we needn't implement a independent list_snapshots()
> function, just pass a filter function which is used to identify the snapshot
> into list_subvols().
>
> [1]: If we implement new filter functions or compare functions, we must add
> them into the array all_filter_funcs or the array all_comp_funcs, and modify
> the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
>
> Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
> ---
> Changelog v3 -> v4:
> - add the filter set and comparer set which are used to manage the filters and
>   comparers. And the memory space of these two set are allocated dynamically,
>   in this way, the users can specify lots of filters and comparers, not be limited
>   by the size of the array.
>
> Changelog v1 -> v3:
> - new patch.
> ---
>  btrfs-list.c     | 1004 ++++++++++++++++++++++++++++++++----------------------
>  btrfs-list.h     |   73 ++++-
>  cmds-inspect.c   |    2 +-
>  cmds-subvolume.c |   59 +++-
>  send-utils.c     |    3 +-
>  5 files changed, 712 insertions(+), 429 deletions(-)
>
> diff --git a/btrfs-list.c b/btrfs-list.c
> index ed28021..bace903 100644
> --- a/btrfs-list.c
> +++ b/btrfs-list.c
> @@ -37,6 +37,9 @@
>  #include <uuid/uuid.h>
>  #include "btrfs-list.h"
>
> +#define BTRFS_LIST_NFILTERS_INCREASE   (2 * BTRFS_LIST_FILTER_MAX)
> +#define BTRFS_LIST_NCOMPS_INCREASE     (2 * BTRFS_LIST_COMP_MAX)
> +
>  /* we store all the roots we find in an rbtree so that we can
>   * search for them later.
>   */
> @@ -49,19 +52,28 @@ struct root_lookup {
>   */
>  struct root_info {
>         struct rb_node rb_node;
> +       struct rb_node sort_node;
>
>         /* this root's id */
>         u64 root_id;
>
> +       /* equal the offset of the root's key */
> +       u64 root_offset;
> +
>         /* the id of the root that references this one */
>         u64 ref_tree;
>
>         /* the dir id we're in from ref_tree */
>         u64 dir_id;
>
> +       u64 top_id;
> +
>         /* generation when the root is created or last updated */
>         u64 gen;
>
> +       /* creation generation of this root in sec*/
> +       u64 ogen;
> +
>         /* creation time of this root in sec*/
>         time_t otime;
>
> @@ -73,35 +85,254 @@ struct root_info {
>         char *path;
>
>         /* the name of this root in the directory it lives in */
> -       char name[];
> +       char *name;
> +
> +       char *full_path;
>  };
>
> +struct {
> +       char    *name;
> +       char    *column_name;
> +       int     need_print;
> +} btrfs_list_columns[] = {
> +       {
> +               .name           = "ID",
> +               .column_name    = "ID",
> +               .need_print     = 1,
> +       },
> +       {
> +               .name           = "gen",
> +               .column_name    = "Gen",
> +               .need_print     = 1,
> +       },
> +       {
> +               .name           = "cgen",
> +               .column_name    = "CGen",
> +               .need_print     = 0,
> +       },
> +       {
> +               .name           = "parent",
> +               .column_name    = "Parent",
> +               .need_print     = 0,
> +       },
> +       {
> +               .name           = "top level",
> +               .column_name    = "Top Level",
> +               .need_print     = 1,
> +       },
> +       {
> +               .name           = "otime",
> +               .column_name    = "OTime",
> +               .need_print     = 0,
> +       },
> +       {
> +               .name           = "uuid",
> +               .column_name    = "UUID",
> +               .need_print     = 0,
> +       },
> +       {
> +               .name           = "path",
> +               .column_name    = "Path",
> +               .need_print     = 1,
> +       },
> +       {
> +               .name           = NULL,
> +               .column_name    = NULL,
> +               .need_print     = 0,
> +       },
> +};
> +
> +static btrfs_list_filter_func all_filter_funcs[];
> +static btrfs_list_comp_func all_comp_funcs[];
> +
> +void btrfs_list_setup_print_column(enum btrfs_list_column_enum column)
> +{
> +       int i;
> +
> +       BUG_ON(column < 0 || column > BTRFS_LIST_ALL);
> +
> +       if (column < BTRFS_LIST_ALL) {
> +               btrfs_list_columns[column].need_print = 1;
> +               return;
> +       }
> +
> +       for (i = 0; i < BTRFS_LIST_ALL; i++)
> +               btrfs_list_columns[i].need_print = 1;
> +}
> +
>  static void root_lookup_init(struct root_lookup *tree)
>  {
>         tree->root.rb_node = NULL;
>  }
>
> -static int comp_entry(struct root_info *entry, u64 root_id, u64 ref_tree)
> +static int comp_entry_with_rootid(struct root_info *entry1,
> +                                 struct root_info *entry2,
> +                                 int is_descending)
>  {
> -       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;
> +       int ret;
> +
> +       if (entry1->root_id > entry2->root_id)
> +               ret = 1;
> +       else if (entry1->root_id < entry2->root_id)
> +               ret = -1;
> +       else
> +               ret = 0;
> +
> +       return is_descending ? -ret : ret;
> +}
> +
> +static int comp_entry_with_gen(struct root_info *entry1,
> +                              struct root_info *entry2,
> +                              int is_descending)
> +{
> +       int ret;
> +
> +       if (entry1->gen > entry2->gen)
> +               ret = 1;
> +       else if (entry1->gen < entry2->gen)
> +               ret = -1;
> +       else
> +               ret = 0;
> +
> +       return is_descending ? -ret : ret;
> +}
> +
> +static int comp_entry_with_ogen(struct root_info *entry1,
> +                               struct root_info *entry2,
> +                               int is_descending)
> +{
> +       int ret;
> +
> +       if (entry1->ogen > entry2->ogen)
> +               ret = 1;
> +       else if (entry1->ogen < entry2->ogen)
> +               ret = -1;
> +       else
> +               ret = 0;
> +
> +       return is_descending ? -ret : ret;
> +}
> +
> +static btrfs_list_comp_func all_comp_funcs[] = {
> +       [BTRFS_LIST_COMP_ROOTID]        = comp_entry_with_rootid,
> +       [BTRFS_LIST_COMP_OGEN]          = comp_entry_with_ogen,
> +       [BTRFS_LIST_COMP_GEN]           = comp_entry_with_gen,
> +};
> +
> +struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void)
> +{
> +       struct btrfs_list_comparer_set *set;
> +       int size;
> +
> +       size = sizeof(struct btrfs_list_comparer_set) +
> +              BTRFS_LIST_NCOMPS_INCREASE * sizeof(struct btrfs_list_comparer);
> +       set = malloc(size);
> +       if (!set) {
> +               fprintf(stderr, "memory allocation failed\n");
> +               exit(1);
> +       }
> +
> +       memset(set, 0, size);
> +       set->total = BTRFS_LIST_NCOMPS_INCREASE;
> +
> +       return set;
> +}
> +
> +void btrfs_list_free_comparer_set(struct btrfs_list_comparer_set *comp_set)
> +{
> +       free(comp_set);
> +}
> +
> +int btrfs_list_setup_comparer(struct btrfs_list_comparer_set  **comp_set,
> +                             enum btrfs_list_comp_enum comparer,
> +                             int is_descending)
> +{
> +       struct btrfs_list_comparer_set *set = *comp_set;
> +       int size;
> +
> +       BUG_ON(!set);
> +       BUG_ON(comparer >= BTRFS_LIST_COMP_MAX);
> +       BUG_ON(set->ncomps > set->total);
> +
> +       if (set->ncomps == set->total) {
> +               size = set->total + BTRFS_LIST_NCOMPS_INCREASE;
> +               size = sizeof(*set) + size * sizeof(struct btrfs_list_comparer);
> +               set = realloc(set, size);
> +               if (!set) {
> +                       fprintf(stderr, "memory allocation failed\n");
> +                       exit(1);
> +               }
> +
> +               memset(&set->comps[set->total], 0,
> +                      BTRFS_LIST_NCOMPS_INCREASE *
> +                      sizeof(struct btrfs_list_comparer));
> +               set->total += BTRFS_LIST_NCOMPS_INCREASE;
> +               *comp_set = set;
> +       }
> +
> +       BUG_ON(set->comps[set->ncomps].comp_func);
> +
> +       set->comps[set->ncomps].comp_func = all_comp_funcs[comparer];
> +       set->comps[set->ncomps].is_descending = is_descending;
> +       set->ncomps++;
>         return 0;
>  }
>
> -static int comp_entry_with_gen(struct root_info *entry, u64 root_id,
> -                              u64 ref_tree, u64 gen)
> +static int sort_comp(struct root_info *entry1, struct root_info *entry2,
> +                    struct btrfs_list_comparer_set *set)
>  {
> -       if (entry->gen < gen)
> -               return 1;
> -       if (entry->gen > gen)
> -               return -1;
> -       return comp_entry(entry, root_id, ref_tree);
> +       int rootid_compared = 0;
> +       int i, ret = 0;
> +
> +       if (!set || !set->ncomps)
> +               goto comp_rootid;
> +
> +       for (i = 0; i < set->ncomps; i++) {
> +               if (!set->comps[i].comp_func)
> +                       break;
> +
> +               ret = set->comps[i].comp_func(entry1, entry2,
> +                                             set->comps[i].is_descending);
> +               if (ret)
> +                       return ret;
> +
> +               if (set->comps[i].comp_func == comp_entry_with_rootid)
> +                       rootid_compared = 1;
> +       }
> +
> +       if (!rootid_compared) {
> +comp_rootid:
> +               ret = comp_entry_with_rootid(entry1, entry2, 0);
> +       }
> +
> +       return ret;
> +}
> +
> +static int sort_tree_insert(struct root_lookup *sort_tree,
> +                           struct root_info *ins,
> +                           struct btrfs_list_comparer_set *comp_set)
> +{
> +       struct rb_node **p = &sort_tree->root.rb_node;
> +       struct rb_node *parent = NULL;
> +       struct root_info *curr;
> +       int ret;
> +
> +       while (*p) {
> +               parent = *p;
> +               curr = rb_entry(parent, struct root_info, sort_node);
> +
> +               ret = sort_comp(ins, curr, comp_set);
> +               if (ret < 0)
> +                       p = &(*p)->rb_left;
> +               else if (ret > 0)
> +                       p = &(*p)->rb_right;
> +               else
> +                       return -EEXIST;
> +       }
> +
> +       rb_link_node(&ins->sort_node, parent, p);
> +       rb_insert_color(&ins->sort_node, &sort_tree->root);
> +       return 0;
>  }
>
>  /*
> @@ -109,118 +340,165 @@ static int comp_entry_with_gen(struct root_info *entry, u64 root_id,
>   * 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, u64 *gen, struct rb_node *node)
> +static int root_tree_insert(struct root_lookup *root_tree,
> +                           struct root_info *ins)
>  {
> -       struct rb_node ** p = &root->rb_node;
> +       struct rb_node **p = &root_tree->root.rb_node;
>         struct rb_node * parent = NULL;
> -       struct root_info *entry;
> -       int comp;
> +       struct root_info *curr;
> +       int ret;
>
>         while(*p) {
>                 parent = *p;
> -               entry = rb_entry(parent, struct root_info, rb_node);
> +               curr = rb_entry(parent, struct root_info, rb_node);
>
> -               if (!gen)
> -                       comp = comp_entry(entry, root_id, ref_tree);
> -               else
> -                       comp = comp_entry_with_gen(entry, root_id, ref_tree,
> -                                                  *gen);
> -
> -               if (comp < 0)
> +               ret = comp_entry_with_rootid(ins, curr, 0);
> +               if (ret < 0)
>                         p = &(*p)->rb_left;
> -               else if (comp > 0)
> +               else if (ret > 0)
>                         p = &(*p)->rb_right;
>                 else
> -                       return parent;
> +                       return -EEXIST;
>         }
>
> -       entry = rb_entry(parent, struct root_info, rb_node);
> -       rb_link_node(node, parent, p);
> -       rb_insert_color(node, root);
> -       return NULL;
> +       rb_link_node(&ins->rb_node, parent, p);
> +       rb_insert_color(&ins->rb_node, &root_tree->root);
> +       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 struct root_info *root_tree_search(struct root_lookup *root_tree,
> +                                         u64 root_id)
>  {
> -       struct rb_node * n = root->rb_node;
> +       struct rb_node *n = root_tree->root.rb_node;
>         struct root_info *entry;
> +       struct root_info tmp;
> +       int ret;
> +
> +       tmp.root_id = root_id;
>
>         while(n) {
>                 entry = rb_entry(n, struct root_info, rb_node);
>
> -               if (entry->root_id < root_id)
> +               ret = comp_entry_with_rootid(&tmp, entry, 0);
> +               if (ret < 0)
>                         n = n->rb_left;
> -               else if (entry->root_id > root_id)
> +               else if (ret > 0)
>                         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;
> -                       }
> +               else
>                         return entry;
> -               }
>         }
>         return NULL;
>  }
>
> +static int update_root(struct root_lookup *root_lookup,
> +                      u64 root_id, u64 ref_tree, u64 root_offset, u64 dir_id,
> +                      char *name, int name_len, u64 ogen, u64 gen, time_t ot,
> +                      void *uuid)
> +{
> +       struct root_info *ri;
> +
> +       ri = root_tree_search(root_lookup, root_id);
> +       if (!ri || ri->root_id != root_id)
> +               return -ENOENT;
> +       if (name && name_len > 0) {
> +               if (ri->name)
> +                       free(ri->name);
> +
> +               ri->name = malloc(name_len + 1);
> +               if (!ri->name) {
> +                       fprintf(stderr, "memory allocation failed\n");
> +                       exit(1);
> +               }
> +               strncpy(ri->name, name, name_len);
> +               ri->name[name_len] = 0;
> +       }
> +       if (ref_tree)
> +               ri->ref_tree = ref_tree;
> +       if (root_offset)
> +               ri->root_offset = root_offset;
> +       if (dir_id)
> +               ri->dir_id = dir_id;
> +       if (gen)
> +               ri->gen = gen;
> +       if (ogen)
> +               ri->ogen = ogen;
> +       if (!ri->ogen && root_offset)
> +               ri->ogen = root_offset;
> +       if (ot)
> +               ri->otime = ot;
> +       if (uuid)
> +               memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
> +
> +       return 0;
> +}
> +
>  /*
> - * 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
> + * add_root - update the existed root, or allocate a new root and insert it
> + *           into the lookup tree.
> + * root_id: object id of the root
> + * ref_tree: object id of the referring root.
> + * root_offset: offset value of the root'key
> + * dir_id: inode id of the directory in ref_tree where this root can be found.
> + * name: the name of root_id in that directory
> + * name_len: the length of name
> + * ogen: the original generation of the root
> + * gen: the current generation of the root
> + * ot: the original time(create time) of the root
> + * uuid: uuid of the root
>   */
>  static int add_root(struct root_lookup *root_lookup,
> -                   u64 root_id, u64 ref_tree, u64 dir_id, char *name,
> -                   int name_len, u64 *gen, time_t ot, void *uuid)
> +                   u64 root_id, u64 ref_tree, u64 root_offset, u64 dir_id,
> +                   char *name, int name_len, u64 ogen, u64 gen, time_t ot,
> +                   void *uuid)
>  {
>         struct root_info *ri;
> -       struct rb_node *ret;
> -       ri = malloc(sizeof(*ri) + name_len + 1);
> +       int ret;
> +
> +       ret = update_root(root_lookup, root_id, ref_tree, root_offset, dir_id,
> +                         name, name_len, ogen, gen, ot, uuid);
> +       if (!ret)
> +               return 0;
> +
> +       ri = malloc(sizeof(*ri));
>         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;
> +       memset(ri, 0, sizeof(*ri));
>         ri->root_id = root_id;
> -       ri->ref_tree = ref_tree;
> -       if (name)
> +
> +       if (name && name_len > 0) {
> +               ri->name = malloc(name_len + 1);
> +               if (!ri->name) {
> +                       fprintf(stderr, "memory allocation failed\n");
> +                       exit(1);
> +               }
>                 strncpy(ri->name, name, name_len);
> -       if (name_len > 0)
>                 ri->name[name_len] = 0;
> +       }
> +       if (ref_tree)
> +               ri->ref_tree = ref_tree;
> +       if (dir_id)
> +               ri->dir_id = dir_id;
> +       if (root_offset)
> +               ri->root_offset = root_offset;
>         if (gen)
> -               ri->gen = *gen;
> -       ri->otime = ot;
> +               ri->gen = gen;
> +       if (ogen)
> +               ri->ogen = ogen;
> +       if (!ri->ogen && root_offset)
> +               ri->ogen = root_offset;
> +       if (ot)
> +               ri->otime = ot;
>
>         if (uuid)
>                 memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
> -       else
> -               memset(&ri->uuid, 0, BTRFS_UUID_SIZE);
>
> -       ret = tree_insert(&root_lookup->root, root_id, ref_tree, gen,
> -                         &ri->rb_node);
> +       ret = root_tree_insert(root_lookup, ri);
>         if (ret) {
>                 printf("failed to insert tree %llu\n", (unsigned long long)root_id);
>                 exit(1);
> @@ -228,24 +506,33 @@ static int add_root(struct root_lookup *root_lookup,
>         return 0;
>  }
>
> -static int update_root(struct root_lookup *root_lookup, u64 root_id, u64 gen,
> -                       time_t ot, void *uuid)
> +void __free_root_info(struct root_info *ri)
>  {
> -       struct root_info *ri;
> +       if (ri->name)
> +               free(ri->name);
>
> -       ri = tree_search(&root_lookup->root, root_id);
> -       if (!ri || ri->root_id != root_id) {
> -               fprintf(stderr, "could not find subvol %llu\n", root_id);
> -               return -ENOENT;
> -       }
> -       ri->gen = gen;
> -       ri->otime = ot;
> -       if (uuid)
> -               memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
> -       else
> -               memset(&ri->uuid, 0, BTRFS_UUID_SIZE);
> +       if (ri->path)
> +               free(ri->path);
>
> -       return 0;
> +       if (ri->full_path)
> +               free(ri->full_path);
> +
> +       free(ri);
> +}
> +
> +void __free_all_subvolumn(struct root_lookup *root_tree)
> +{
> +       struct root_info *entry;
> +       struct rb_node *n;
> +
> +       n = rb_first(&root_tree->root);
> +       while (n) {
> +               entry = rb_entry(n, struct root_info, rb_node);
> +               rb_erase(n, &root_tree->root);
> +               __free_root_info(entry);
> +
> +               n = rb_first(&root_tree->root);
> +       }
>  }
>
>  /*
> @@ -255,8 +542,7 @@ static int update_root(struct root_lookup *root_lookup, u64 root_id, u64 gen,
>   * 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 *parent_id, u64 *top_id, char **path)
> +static int resolve_root(struct root_lookup *rl, struct root_info *ri)
>  {
>         char *full_path = NULL;
>         int len = 0;
> @@ -266,7 +552,6 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>          * we go backwards from the root_info object and add pathnames
>          * from parent directories as we go.
>          */
> -       *parent_id = 0;
>         found = ri;
>         while (1) {
>                 char *tmp;
> @@ -275,6 +560,10 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>
>                 /* room for / and for null */
>                 tmp = malloc(add_len + 2 + len);
> +               if (!tmp) {
> +                       perror("malloc failed");
> +                       exit(1);
> +               }
>                 if (full_path) {
>                         memcpy(tmp + add_len + 1, full_path, len);
>                         tmp[add_len] = '/';
> @@ -289,13 +578,10 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>                 }
>
>                 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;
> +                       ri->top_id = next;
>                         break;
>                 }
>
> @@ -303,14 +589,14 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>                  * if the ref_tree wasn't in our tree of roots, we're
>                  * at the top
>                  */
> -               found = tree_search(&rl->root, next);
> +               found = root_tree_search(rl, next);
>                 if (!found) {
> -                       *top_id = next;
> +                       ri->top_id = next;
>                         break;
>                 }
>         }
>
> -       *path = full_path;
> +       ri->full_path = full_path;
>
>         return 0;
>  }
> @@ -608,7 +894,7 @@ build:
>         return full;
>  }
>
> -static int get_default_subvolid(int fd, u64 *default_id)
> +int btrfs_list_get_default_subvolume(int fd, u64 *default_id)
>  {
>         struct btrfs_ioctl_search_args args;
>         struct btrfs_ioctl_search_key *sk = &args.key;
> @@ -674,10 +960,9 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>         int name_len;
>         char *name;
>         u64 dir_id;
> -       u8 type;
>         u64 gen = 0;
> +       u64 ogen;
>         int i;
> -       int get_gen = 0;
>         time_t t;
>         u8 uuid[BTRFS_UUID_SIZE];
>
> @@ -692,7 +977,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>          * only send back this type of key now.
>          */
>         sk->max_type = BTRFS_ROOT_BACKREF_KEY;
> -       sk->min_type = BTRFS_ROOT_BACKREF_KEY;
> +       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>
>         sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
>
> @@ -704,7 +989,6 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>         sk->max_offset = (u64)-1;
>         sk->max_transid = (u64)-1;
>
> -again:
>         /* just a big number, doesn't matter much */
>         sk->nr_items = 4096;
>
> @@ -726,28 +1010,32 @@ again:
>                         sh = (struct btrfs_ioctl_search_header *)(args.buf +
>                                                                   off);
>                         off += sizeof(*sh);
> -                       if (!get_gen && sh->type == BTRFS_ROOT_BACKREF_KEY) {
> +                       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, NULL, 0, NULL);
> -                       } else if (get_gen && sh->type == BTRFS_ROOT_ITEM_KEY) {
> +                                        0, dir_id, name, name_len, 0, 0, 0,
> +                                        NULL);
> +                       } else if (sh->type == BTRFS_ROOT_ITEM_KEY) {
>                                 ri = (struct btrfs_root_item *)(args.buf + off);
>                                 gen = btrfs_root_generation(ri);
>                                 if(sh->len >
>                                    sizeof(struct btrfs_root_item_v0)) {
>                                         t = ri->otime.sec;
> +                                       ogen = btrfs_root_otransid(ri);
>                                         memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
>                                 } else {
>                                         t = 0;
> +                                       ogen = 0;
>                                         memset(uuid, 0, BTRFS_UUID_SIZE);
>                                 }
>
> -                               update_root(root_lookup, sh->objectid, gen, t,
> -                                       uuid);
> +                               add_root(root_lookup, sh->objectid, 0,
> +                                        sh->offset, 0, NULL, 0, ogen, gen, t,
> +                                        uuid);
>                         }
>
>                         off += sh->len;
> @@ -761,129 +1049,139 @@ again:
>                         sk->min_offset = sh->offset;
>                 }
>                 sk->nr_items = 4096;
> -               /* this iteration is done, step forward one root for the next
> -                * ioctl
> -                */
> -               if (get_gen)
> -                       type = BTRFS_ROOT_ITEM_KEY;
> +               sk->min_offset++;
> +               if (!sk->min_offset)    /* overflow */
> +                       sk->min_type++;
>                 else
> -                       type = BTRFS_ROOT_BACKREF_KEY;
> +                       continue;
>
> -               if (sk->min_type < type) {
> -                       sk->min_type = type;
> -                       sk->min_offset = 0;
> -               } else  if (sk->min_objectid < BTRFS_LAST_FREE_OBJECTID) {
> +               if (sk->min_type > BTRFS_ROOT_BACKREF_KEY) {
> +                       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>                         sk->min_objectid++;
> -                       sk->min_type = type;
> -                       sk->min_offset = 0;
>                 } else
> +                       continue;
> +
> +               if (sk->min_objectid > sk->max_objectid)
>                         break;
>         }
>
> -       if (!get_gen) {
> -               memset(&args, 0, sizeof(args));
> +       return 0;
> +}
>
> -               sk->tree_id = 1;
> -               sk->max_type = BTRFS_ROOT_ITEM_KEY;
> -               sk->min_type = BTRFS_ROOT_ITEM_KEY;
> +static int filter_by_rootid(struct root_info *ri, void *arg)
> +{
> +       u64 default_root_id = *((u64 *)arg);
>
> -               sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
> +       return ri->root_id == default_root_id;
> +}
>
> -               sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
> -               sk->max_offset = (u64)-1;
> -               sk->max_transid = (u64)-1;
> +static int filter_snapshot(struct root_info *ri, void *arg)
> +{
> +       return !!ri->root_offset;
> +}
> +
> +static btrfs_list_filter_func all_filter_funcs[] = {
> +       [BTRFS_LIST_FILTER_ROOTID]              = filter_by_rootid,
> +       [BTRFS_LIST_FILTER_SNAPSHOT_ONLY]       = filter_snapshot,
> +};
>
> -               get_gen = 1;
> -               goto again;
> +struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void)
> +{
> +       struct btrfs_list_filter_set *set;
> +       int size;
> +
> +       size = sizeof(struct btrfs_list_filter_set) +
> +              BTRFS_LIST_NFILTERS_INCREASE * sizeof(struct btrfs_list_filter);
> +       set = malloc(size);
> +       if (!set) {
> +               fprintf(stderr, "memory allocation failed\n");
> +               exit(1);
>         }
> -       return 0;
> +
> +       memset(set, 0, size);
> +       set->total = BTRFS_LIST_NFILTERS_INCREASE;
> +
> +       return set;
>  }
>
> -static int __list_snapshot_search(int fd, struct root_lookup *root_lookup)
> +void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set)
>  {
> -       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 gen = 0;
> -       int i;
> -
> -       root_lookup_init(root_lookup);
> -       memset(&args, 0, sizeof(args));
> +       free(filter_set);
> +}
>
> -       sk->tree_id = 1;
> -       sk->max_type = BTRFS_ROOT_ITEM_KEY;
> -       sk->min_type = BTRFS_ROOT_ITEM_KEY;
> -       sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
> -       sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
> -       sk->max_offset = (u64)-1;
> -       sk->max_transid = (u64)-1;
> -       sk->nr_items = 4096;
> +int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
> +                           enum btrfs_list_filter_enum filter, void *data)
> +{
> +       struct btrfs_list_filter_set *set = *filter_set;
> +       int size;
> +
> +       BUG_ON(!set);
> +       BUG_ON(filter >= BTRFS_LIST_FILTER_MAX);
> +       BUG_ON(set->nfilters > set->total);
> +
> +       if (set->nfilters == set->total) {
> +               size = set->total + BTRFS_LIST_NFILTERS_INCREASE;
> +               size = sizeof(*set) + size * sizeof(struct btrfs_list_filter);
> +               set = realloc(set, size);
> +               if (!set) {
> +                       fprintf(stderr, "memory allocation failed\n");
> +                       exit(1);
> +               }
>
> -       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;
> +               memset(&set->filters[set->total], 0,
> +                      BTRFS_LIST_NFILTERS_INCREASE *
> +                      sizeof(struct btrfs_list_filter));
> +               set->total += BTRFS_LIST_NFILTERS_INCREASE;
> +               *filter_set = set;
> +       }
>
> -               off = 0;
> +       BUG_ON(set->filters[set->nfilters].filter_func);
>
> -               /*
> -                * 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;
> -                       time_t  t;
> -                       u8 uuid[BTRFS_UUID_SIZE];
> +       set->filters[set->nfilters].filter_func = all_filter_funcs[filter];
> +       set->filters[set->nfilters].data = data;
> +       set->nfilters++;
> +       return 0;
> +}
>
> -                       sh = (struct btrfs_ioctl_search_header *)(args.buf +
> -                                                                 off);
> -                       off += sizeof(*sh);
> -                       if (sh->type == BTRFS_ROOT_ITEM_KEY && sh->offset) {
> -                               item = (struct btrfs_root_item *)(args.buf + off);
> -                               if(sh->len >
> -                                  sizeof(struct btrfs_root_item_v0)) {
> -                                       t = item->otime.sec;
> -                                       memcpy(uuid, item->uuid,
> -                                              BTRFS_UUID_SIZE);
> -                               } else {
> -                                       t = 0;
> -                                       memset(uuid, 0, BTRFS_UUID_SIZE);
> -                               }
> -                               gen = sh->offset;
> +static int filter_root(struct root_info *ri,
> +                      struct btrfs_list_filter_set *set)
> +{
> +       int i, ret;
>
> -                               add_root(root_lookup, sh->objectid, 0,
> -                                        0, NULL, 0, &gen, t, uuid);
> -                       }
> -                       off += sh->len;
> +       if (!set || !set->nfilters)
> +               return 1;
>
> -                       /*
> -                        * 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_ITEM_KEY) {
> -                       sk->min_type = BTRFS_ROOT_ITEM_KEY;
> -                       sk->min_offset = 0;
> -               } else  if (sk->min_objectid < BTRFS_LAST_FREE_OBJECTID) {
> -                       sk->min_objectid++;
> -                       sk->min_type = BTRFS_ROOT_ITEM_KEY;
> -                       sk->min_offset = 0;
> -               } else
> +       for (i = 0; i < set->nfilters; i++) {
> +               if (!set->filters[i].filter_func)
>                         break;
> +               ret = set->filters[i].filter_func(ri, set->filters[i].data);
> +               if (!ret)
> +                       return 0;
> +       }
> +       return 1;
> +}
> +
> +static void __filter_and_sort_subvol(struct root_lookup *all_subvols,
> +                                   struct root_lookup *sort_tree,
> +                                   struct btrfs_list_filter_set *filter_set,
> +                                   struct btrfs_list_comparer_set *comp_set)
> +{
> +       struct rb_node *n;
> +       struct root_info *entry;
> +       int ret;
> +
> +       root_lookup_init(sort_tree);
> +
> +       n = rb_last(&all_subvols->root);
> +       while (n) {
> +               entry = rb_entry(n, struct root_info, rb_node);
> +
> +               resolve_root(all_subvols, entry);
> +               ret = filter_root(entry, filter_set);
> +               if (ret)
> +                       sort_tree_insert(sort_tree, entry, comp_set);
> +               n = rb_prev(n);
>         }
> -       return 0;
>  }
>
>  static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
> @@ -904,128 +1202,91 @@ static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
>         return 0;
>  }
>
> -int list_subvols(int fd, int print_parent, int get_default, int print_uuid)
> +static void print_subvolume_column(struct root_info *subv,
> +                                  enum btrfs_list_column_enum column)
>  {
> -       struct root_lookup root_lookup;
> -       struct rb_node *n;
> -       u64 default_id;
> -       int ret;
> +       char tstr[256];
>         char uuidparse[37];
>
> -       if (get_default) {
> -               ret = get_default_subvolid(fd, &default_id);
> -               if (ret) {
> -                       fprintf(stderr, "ERROR: can't perform the search - %s\n",
> -                               strerror(errno));
> -                       return ret;
> -               }
> -               if (default_id == 0) {
> -                       fprintf(stderr, "ERROR: 'default' dir item not found\n");
> -                       return ret;
> -               }
> -
> -               /* no need to resolve roots if FS_TREE is default */
> -               if (default_id == BTRFS_FS_TREE_OBJECTID) {
> -                       printf("ID 5 (FS_TREE)\n");
> -                       return ret;
> -               }
> -       }
> -
> -       ret = __list_subvol_search(fd, &root_lookup);
> -       if (ret) {
> -               fprintf(stderr, "ERROR: can't perform the search - %s\n",
> -                               strerror(errno));
> -               return ret;
> +       BUG_ON(column >= BTRFS_LIST_ALL || column < 0);
> +
> +       switch (column) {
> +       case BTRFS_LIST_OBJECTID:
> +               printf("%llu", subv->root_id);
> +               break;
> +       case BTRFS_LIST_GENERATION:
> +               printf("%llu", subv->gen);
> +               break;
> +       case BTRFS_LIST_OGENERATION:
> +               printf("%llu", subv->ogen);
> +               break;
> +       case BTRFS_LIST_PARENT:
> +               printf("%llu", subv->ref_tree);
> +               break;
> +       case BTRFS_LIST_TOP_LEVEL:
> +               printf("%llu", subv->top_id);
> +               break;
> +       case BTRFS_LIST_OTIME:
> +               if (subv->otime)
> +                       strftime(tstr, 256, "%Y-%m-%d %X",
> +                                localtime(&subv->otime));
> +               else
> +                       strcpy(tstr, "-");
> +               printf("%s", tstr);
> +               break;
> +       case BTRFS_LIST_UUID:
> +               if (uuid_is_null(subv->uuid))
> +                       strcpy(uuidparse, "-");
> +               else
> +                       uuid_unparse(subv->uuid, uuidparse);
> +               printf("%s", uuidparse);
> +               break;
> +       case BTRFS_LIST_PATH:
> +               BUG_ON(!subv->full_path);
> +               printf("%s", subv->full_path);
> +               break;
> +       default:
> +               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.
> -        */
> -       ret = __list_subvol_fill_paths(fd, &root_lookup);
> -       if (ret < 0)
> -               return ret;
> -
> -       /* now that we have all the subvol-relative paths filled in,
> -        * we have to string the subvols together so that we can get
> -        * a path all the way back to the FS root
> -        */
> -       n = rb_last(&root_lookup.root);
> -       while (n) {
> -               struct root_info *entry;
> -               u64 level;
> -               u64 parent_id;
> -               char *path;
> +static void print_single_volume_info_default(struct root_info *subv)
> +{
> +       int i;
>
> -               entry = rb_entry(n, struct root_info, rb_node);
> -               if (get_default && entry->root_id != default_id) {
> -                       n = rb_prev(n);
> +       for (i = 0; i < BTRFS_LIST_ALL; i++) {
> +               if (!btrfs_list_columns[i].need_print)
>                         continue;
> -               }
>
> -               resolve_root(&root_lookup, entry, &parent_id, &level, &path);
> -               if (print_parent) {
> -                       if (print_uuid) {
> -                               if (uuid_is_null(entry->uuid))
> -                                       strcpy(uuidparse, "-");
> -                               else
> -                                       uuid_unparse(entry->uuid, uuidparse);
> -                               printf("ID %llu gen %llu parent %llu top level %llu"
> -                                       " uuid %s path %s\n",
> -                                       (unsigned long long)entry->root_id,
> -                                       (unsigned long long)entry->gen,
> -                                       (unsigned long long)parent_id,
> -                                       (unsigned long long)level,
> -                                       uuidparse, path);
> -                       } else {
> -                               printf("ID %llu gen %llu parent %llu top level"
> -                                       " %llu path %s\n",
> -                                       (unsigned long long)entry->root_id,
> -                                       (unsigned long long)entry->gen,
> -                                       (unsigned long long)parent_id,
> -                                       (unsigned long long)level, path);
> -                       }
> -               } else {
> -                       if (print_uuid) {
> -                               if (uuid_is_null(entry->uuid))
> -                                       strcpy(uuidparse, "-");
> -                               else
> -                                       uuid_unparse(entry->uuid, uuidparse);
> -                               printf("ID %llu gen %llu top level %llu"
> -                                       " uuid %s path %s\n",
> -                                       (unsigned long long)entry->root_id,
> -                                       (unsigned long long)entry->gen,
> -                                       (unsigned long long)level,
> -                                       uuidparse, path);
> -                       } else {
> -                               printf("ID %llu gen %llu top level %llu path %s\n",
> -                                       (unsigned long long)entry->root_id,
> -                                       (unsigned long long)entry->gen,
> -                                       (unsigned long long)level, path);
> -                       }
> -               }
> +               printf("%s ", btrfs_list_columns[i].name);
> +               print_subvolume_column(subv, i);
>
> -               free(path);
> -               n = rb_prev(n);
> +               if (i != BTRFS_LIST_PATH)
> +                       printf(" ");
>         }
> +       printf("\n");
> +}
>
> -       return ret;
> +static void print_all_volume_info_default(struct root_lookup *sorted_tree)
> +{
> +       struct rb_node *n;
> +       struct root_info *entry;
> +
> +       n = rb_first(&sorted_tree->root);
> +       while (n) {
> +               entry = rb_entry(n, struct root_info, sort_node);
> +               print_single_volume_info_default(entry);
> +               n = rb_next(n);
> +       }
>  }
>
> -int list_snapshots(int fd, int print_parent, int order, int print_uuid)
> +int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
> +                      struct btrfs_list_comparer_set *comp_set)
>  {
>         struct root_lookup root_lookup;
> -       struct root_lookup root_lookup_snap;
> -       struct rb_node *n;
> +       struct root_lookup root_sort;
>         int ret;
>
> -       ret = __list_snapshot_search(fd, &root_lookup_snap);
> -       if (ret) {
> -               fprintf(stderr, "ERROR: can't perform the search - %s\n",
> -                               strerror(errno));
> -               return ret;
> -       }
> -
>         ret = __list_subvol_search(fd, &root_lookup);
>         if (ret) {
>                 fprintf(stderr, "ERROR: can't perform the search - %s\n",
> @@ -1041,86 +1302,11 @@ int list_snapshots(int fd, int print_parent, int order, int print_uuid)
>         if (ret < 0)
>                 return ret;
>
> -       /* now that we have all the subvol-relative paths filled in,
> -        * we have to string the subvols together so that we can get
> -        * a path all the way back to the FS root
> -        */
> -       if (!order)
> -               n = rb_last(&root_lookup_snap.root);
> -       else
> -               n = rb_first(&root_lookup_snap.root);
> -       while (n) {
> -               struct root_info *entry_snap;
> -               struct root_info *entry;
> -               u64 level;
> -               u64 parent_id;
> -               char *path;
> -               time_t t;
> -               char tstr[256];
> -               char uuidparse[37];
> -
> -               entry_snap = rb_entry(n, struct root_info, rb_node);
> -               entry = tree_search(&root_lookup.root, entry_snap->root_id);
> -
> -               resolve_root(&root_lookup, entry, &parent_id, &level, &path);
> -               t = entry->otime;
> -               if(t)
> -                       strftime(tstr,256,"%Y-%m-%d %X",localtime(&t));
> -               else
> -                       strcpy(tstr,"-");
> -               if (print_parent) {
> -                       if (print_uuid) {
> -                               if (uuid_is_null(entry->uuid))
> -                                       strcpy(uuidparse, "-");
> -                               else
> -                                       uuid_unparse(entry->uuid, uuidparse);
> -                               printf("ID %llu gen %llu cgen %llu parent %llu"
> -                                       " top level %llu otime %s uuid %s path %s\n",
> -                                       (unsigned long long)entry->root_id,
> -                                       (unsigned long long)entry->gen,
> -                                       (unsigned long long)entry_snap->gen,
> -                                       (unsigned long long)parent_id,
> -                                       (unsigned long long)level,
> -                                       tstr, uuidparse, path);
> -                       } else {
> -                               printf("ID %llu gen %llu cgen %llu parent %llu"
> -                                       " top level %llu otime %s path %s\n",
> -                                       (unsigned long long)entry->root_id,
> -                                       (unsigned long long)entry->gen,
> -                                       (unsigned long long)entry_snap->gen,
> -                                       (unsigned long long)parent_id,
> -                                       (unsigned long long)level, tstr, path);
> -                       }
> -               } else {
> -                       if (print_uuid) {
> -                               if (uuid_is_null(entry->uuid))
> -                                       strcpy(uuidparse, "-");
> -                               else
> -                                       uuid_unparse(entry->uuid, uuidparse);
> -                               printf("ID %llu gen %llu cgen %llu top level %llu "
> -                                       "otime %s uuid %s path %s\n",
> -                                       (unsigned long long)entry->root_id,
> -                                       (unsigned long long)entry->gen,
> -                                       (unsigned long long)entry_snap->gen,
> -                                       (unsigned long long)level,
> -                                       tstr, uuidparse, path);
> -                       } else {
> -                               printf("ID %llu gen %llu cgen %llu top level %llu "
> -                                       "otime %s path %s\n",
> -                                       (unsigned long long)entry->root_id,
> -                                       (unsigned long long)entry->gen,
> -                                       (unsigned long long)entry_snap->gen,
> -                                       (unsigned long long)level, tstr, path);
> -                       }
> -               }
> -
> -               free(path);
> -               if (!order)
> -                       n = rb_prev(n);
> -               else
> -                       n = rb_next(n);
> -       }
> +       __filter_and_sort_subvol(&root_lookup, &root_sort, filter_set,
> +                                comp_set);
>
> +       print_all_volume_info_default(&root_sort);
> +       __free_all_subvolumn(&root_lookup);
>         return ret;
>  }
>
> @@ -1203,7 +1389,7 @@ static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh,
>         return 0;
>  }
>
> -int find_updated_files(int fd, u64 root_id, u64 oldest_gen)
> +int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen)
>  {
>         int ret;
>         struct btrfs_ioctl_search_args args;
> @@ -1304,7 +1490,7 @@ int find_updated_files(int fd, u64 root_id, u64 oldest_gen)
>         return ret;
>  }
>
> -char *path_for_root(int fd, u64 root)
> +char *btrfs_list_path_for_root(int fd, u64 root)
>  {
>         struct root_lookup root_lookup;
>         struct rb_node *n;
> @@ -1322,19 +1508,17 @@ char *path_for_root(int fd, u64 root)
>         n = rb_last(&root_lookup.root);
>         while (n) {
>                 struct root_info *entry;
> -               u64 parent_id;
> -               u64 level;
> -               char *path;
>
>                 entry = rb_entry(n, struct root_info, rb_node);
> -               resolve_root(&root_lookup, entry, &parent_id, &level, &path);
> -               if (entry->root_id == root)
> -                       ret_path = path;
> -               else
> -                       free(path);
> +               resolve_root(&root_lookup, entry);
> +               if (entry->root_id == root) {
> +                       ret_path = entry->full_path;
> +                       entry->full_path = NULL;
> +               }
>
>                 n = rb_prev(n);
>         }
> +       __free_all_subvolumn(&root_lookup);
>
>         return ret_path;
>  }
> diff --git a/btrfs-list.h b/btrfs-list.h
> index b4a7f30..ca3eb7b 100644
> --- a/btrfs-list.h
> +++ b/btrfs-list.h
> @@ -16,7 +16,72 @@
>   * Boston, MA 021110-1307, USA.
>   */
>
> -int list_subvols(int fd, int print_parent, int get_default, int print_uuid);
> -int list_snapshots(int fd, int print_parent, int order, int print_uuid);
> -int find_updated_files(int fd, u64 root_id, u64 oldest_gen);
> -char *path_for_root(int fd, u64 root);
> +struct root_info;
> +
> +typedef int (*btrfs_list_filter_func)(struct root_info *, void *);
> +typedef int (*btrfs_list_comp_func)(struct root_info *, struct root_info *,
> +                                   int);
> +
> +struct btrfs_list_filter {
> +       btrfs_list_filter_func filter_func;
> +       void *data;
> +};
> +
> +struct btrfs_list_comparer {
> +       btrfs_list_comp_func comp_func;
> +       int is_descending;
> +};
> +
> +struct btrfs_list_filter_set {
> +       int total;
> +       int nfilters;
> +       struct btrfs_list_filter filters[0];
> +};
> +
> +struct btrfs_list_comparer_set {
> +       int total;
> +       int ncomps;
> +       struct btrfs_list_comparer comps[0];
> +};
> +
> +enum btrfs_list_column_enum {
> +       BTRFS_LIST_OBJECTID,
> +       BTRFS_LIST_GENERATION,
> +       BTRFS_LIST_OGENERATION,
> +       BTRFS_LIST_PARENT,
> +       BTRFS_LIST_TOP_LEVEL,
> +       BTRFS_LIST_OTIME,
> +       BTRFS_LIST_UUID,
> +       BTRFS_LIST_PATH,
> +       BTRFS_LIST_ALL,
> +};
> +
> +enum btrfs_list_filter_enum {
> +       BTRFS_LIST_FILTER_ROOTID,
> +       BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
> +       BTRFS_LIST_FILTER_MAX,
> +};
> +
> +enum btrfs_list_comp_enum {
> +       BTRFS_LIST_COMP_ROOTID,
> +       BTRFS_LIST_COMP_OGEN,
> +       BTRFS_LIST_COMP_GEN,
> +       BTRFS_LIST_COMP_MAX,
> +};
> +
> +void btrfs_list_setup_print_column(enum btrfs_list_column_enum column);
> +struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void);
> +void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set);
> +int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
> +                           enum btrfs_list_filter_enum filter, void *data);
> +struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void);
> +void btrfs_list_free_comparer_set(struct btrfs_list_comparer_set *comp_set);
> +int btrfs_list_setup_comparer(struct btrfs_list_comparer_set **comp_set,
> +                             enum btrfs_list_comp_enum comparer,
> +                             int is_descending);
> +
> +int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
> +                      struct btrfs_list_comparer_set *comp_set);
> +int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen);
> +int btrfs_list_get_default_subvolume(int fd, u64 *default_id);
> +char *btrfs_list_path_for_root(int fd, u64 root);
> diff --git a/cmds-inspect.c b/cmds-inspect.c
> index f943ed9..376fab2 100644
> --- a/cmds-inspect.c
> +++ b/cmds-inspect.c
> @@ -194,7 +194,7 @@ static int cmd_logical_resolve(int argc, char **argv)
>                 char *name;
>
>                 if (getpath) {
> -                       name = path_for_root(fd, root);
> +                       name = btrfs_list_path_for_root(fd, root);
>                         if (IS_ERR(name))
>                                 return PTR_ERR(name);
>                         if (!name) {
> diff --git a/cmds-subvolume.c b/cmds-subvolume.c
> index cd4b5a7..b1cf2bd 100644
> --- a/cmds-subvolume.c
> +++ b/cmds-subvolume.c
> @@ -28,6 +28,7 @@
>  #include "ioctl.h"
>  #include "qgroup.h"
>
> +#include "ctree.h"
>  #include "commands.h"
>  #include "btrfs-list.h"
>
> @@ -270,13 +271,15 @@ static const char * const cmd_subvol_list_usage[] = {
>
>  static int cmd_subvol_list(int argc, char **argv)
>  {
> +       struct btrfs_list_filter_set *filter_set;
> +       struct btrfs_list_comparer_set *comparer_set;
>         int fd;
>         int ret;
> -       int print_parent = 0;
> -       int print_snap_only = 0;
> -       int order = 0;
> +       int order;
>         char *subvol;
> -       int print_uuid = 0;
> +
> +       filter_set = btrfs_list_alloc_filter_set();
> +       comparer_set = btrfs_list_alloc_comparer_set();
>
>         optind = 1;
>         while(1) {
> @@ -286,14 +289,21 @@ static int cmd_subvol_list(int argc, char **argv)
>
>                 switch(c) {
>                 case 'p':
> -                       print_parent = 1;
> +                       btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
>                         break;
>                 case 's':
> -                       print_snap_only = 1;
>                         order = atoi(optarg);
> +                       btrfs_list_setup_filter(&filter_set,
> +                                               BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
> +                                               NULL);
> +                       btrfs_list_setup_comparer(&comparer_set,
> +                                                 BTRFS_LIST_COMP_OGEN,
> +                                                 !order);
> +                       btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
> +                       btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
>                         break;
>                 case 'u':
> -                       print_uuid =1;
> +                       btrfs_list_setup_print_column(BTRFS_LIST_UUID);
>                         break;
>                 default:
>                         usage(cmd_subvol_list_usage);
> @@ -320,10 +330,8 @@ static int cmd_subvol_list(int argc, char **argv)
>                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
>                 return 12;
>         }
> -       if (!print_snap_only)
> -               ret = list_subvols(fd, print_parent, 0, print_uuid);
> -       else
> -               ret = list_snapshots(fd, print_parent, order, print_uuid);
> +
> +       ret = btrfs_list_subvols(fd, filter_set, comparer_set);
>         if (ret)
>                 return 19;
>         return 0;
> @@ -483,6 +491,8 @@ static int cmd_subvol_get_default(int argc, char **argv)
>         int fd;
>         int ret;
>         char *subvol;
> +       struct btrfs_list_filter_set *filter_set;
> +       u64 default_id;
>
>         if (check_argc_exact(argc, 2))
>                 usage(cmd_subvol_get_default_usage);
> @@ -504,7 +514,30 @@ static int cmd_subvol_get_default(int argc, char **argv)
>                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
>                 return 12;
>         }
> -       ret = list_subvols(fd, 0, 1, 0);
> +
> +       ret = btrfs_list_get_default_subvolume(fd, &default_id);
> +       if (ret) {
> +               fprintf(stderr, "ERROR: can't perform the search - %s\n",
> +                       strerror(errno));
> +               return ret;
> +       }
> +
> +       if (default_id == 0) {
> +               fprintf(stderr, "ERROR: 'default' dir item not found\n");
> +               return ret;
> +       }
> +
> +       /* no need to resolve roots if FS_TREE is default */
> +       if (default_id == BTRFS_FS_TREE_OBJECTID) {
> +               printf("ID 5 (FS_TREE)\n");
> +               return ret;
> +       }
> +
> +       filter_set = btrfs_list_alloc_filter_set();
> +       btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
> +                               (void *)&default_id);
> +
> +       ret = btrfs_list_subvols(fd, filter_set, NULL);
>         if (ret)
>                 return 19;
>         return 0;
> @@ -585,7 +618,7 @@ static int cmd_find_new(int argc, char **argv)
>                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
>                 return 12;
>         }
> -       ret = find_updated_files(fd, 0, last_gen);
> +       ret = btrfs_list_find_updated_files(fd, 0, last_gen);
>         if (ret)
>                 return 19;
>         return 0;
> diff --git a/send-utils.c b/send-utils.c
> index 096fa02..fcde5c2 100644
> --- a/send-utils.c
> +++ b/send-utils.c
> @@ -244,7 +244,8 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
>                                 if (!root_item_valid)
>                                         goto skip;
>
> -                               path = path_for_root(mnt_fd, sh->objectid);
> +                               path = btrfs_list_path_for_root(mnt_fd,
> +                                                               sh->objectid);
>                                 if (!path)
>                                         path = strdup("");
>                                 if (IS_ERR(path)) {
> --
> 1.7.6.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH V4 5/7] Btrfs-progs: restructure list_subvolumes
  2012-10-09 16:05   ` Alex Lyakas
@ 2012-10-10  2:12     ` Miao Xie
  2012-10-10 19:45       ` Alex Lyakas
  0 siblings, 1 reply; 31+ messages in thread
From: Miao Xie @ 2012-10-10  2:12 UTC (permalink / raw)
  To: Alex Lyakas; +Cc: linux-btrfs

On Tue, 9 Oct 2012 18:05:51 +0200, Alex Lyakas wrote:
> Hi Miao,
> I have some trouble with this code.
> 
> The problem that I see is after subvolume deletion. What happens is
> that __list_subvol_search() is called at the point, at which ROOT_ITEM
> still exists in the root tree, but ROOT_BACKREF does not exist
> anymore. I think this is because btrfs_ioctl_snap_destroy() calls
> btrfs_unlink_subvol(), which calls btrfs_del_root_ref(), which deletes
> ROOT_BACKREF, but ROOT_ITEM still exists until transaction is
> committed.
> 
> So what happens is that we end up with struct root_info that has
> ref_tree==0 (and dir_id==0).
> So later, when __list_subvol_fill_paths() is called, it fails to
> perform BTRFS_IOC_INO_LOOKUP because of ref_tree==0. As a result,
> subvol_uuid_search_init fails and everything stops.
> 
> Previously, before your change, root_info was not added until we see
> ROOT_BACKREF in the tree.

The old code still has the similar problem if we delete the subvolume completely
between __list_subvol_search() and __list_subvol_fill_paths().

> How do you think this should be fixed?

Yes, I think it should be fixed. We can filter the deleted subvolume.

> 
> Also, is there a way to issue a subvolume deletion and make sure that
> it was deleted? I see that btrfs_ioctl_snap_destroy() calls

no ROOT_BACKREF already tells us that the subvolume has been deleted, I think.

> end_transaction() but not commit_transaction(). Moreover, there is no
> way for the caller to know the transid, otherwise we could issue
> BTRFS_IOC_WAIT_SYNC.

we can get the transid by btrfs_ioctl_start_sync()

Thanks
Miao

> 
> Thanks,
> Alex.
> 
> 
> 
> On Tue, Sep 18, 2012 at 1:06 PM, Miao Xie <miaox@cn.fujitsu.com> wrote:
>> The current code of list_subvols() has very bad scalability, if we want to
>> add new filter conditions or new sort methods, we have to modify lots of code.
>>
>> Beside that, the most code of list_snapshots() is similar to list_subvols(),
>>
>> So I restructure list_subvols(), and split the subvolume filter function,
>> the subvolume sort function and the output function from list_subvols().
>> In order to implement it, we defined some importtant structures:
>> struct btrfs_list_filter {
>>         btrfs_list_filter_func filter_func;
>>         void *data;
>> };
>>
>> struct btrfs_list_comparer {
>>         btrfs_list_comp_func comp_func;
>>         int is_descending;
>> };
>>
>> struct {
>>         char    *name;
>>         char    *column_name;
>>         int     need_print;
>> } btrfs_list_columns[];
>>
>> If we want to add a new filter condition, we can choose a suitable filter
>> function, or implement a new filter function[1], and add it into a set of
>> the filters, and then pass the filter set into list_subvols(). We also can
>> mix several filters (just add those filters into the set, and pass the set
>> into list_subvols()) if the users specify two or more filter conditions.
>>
>> The subvolume sort function is similar to the subvolume filter function. The
>> differentiation is the order of comparers in the array which is passed into
>> list_subvols() show us the priority of the sort methods.
>>
>> The output function is different with the above two functions, we define a
>> array to manage all the columns that can be outputed, and use a member variant
>> (->need_print) to control the output of the relative column. Some columns are
>> outputed by default. But we can change it according to the requirement of the
>> users.
>>
>> After appling this patch, we needn't implement a independent list_snapshots()
>> function, just pass a filter function which is used to identify the snapshot
>> into list_subvols().
>>
>> [1]: If we implement new filter functions or compare functions, we must add
>> them into the array all_filter_funcs or the array all_comp_funcs, and modify
>> the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
>>
>> Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
>> ---
>> Changelog v3 -> v4:
>> - add the filter set and comparer set which are used to manage the filters and
>>   comparers. And the memory space of these two set are allocated dynamically,
>>   in this way, the users can specify lots of filters and comparers, not be limited
>>   by the size of the array.
>>
>> Changelog v1 -> v3:
>> - new patch.
>> ---
>>  btrfs-list.c     | 1004 ++++++++++++++++++++++++++++++++----------------------
>>  btrfs-list.h     |   73 ++++-
>>  cmds-inspect.c   |    2 +-
>>  cmds-subvolume.c |   59 +++-
>>  send-utils.c     |    3 +-
>>  5 files changed, 712 insertions(+), 429 deletions(-)
>>
>> diff --git a/btrfs-list.c b/btrfs-list.c
>> index ed28021..bace903 100644
>> --- a/btrfs-list.c
>> +++ b/btrfs-list.c
>> @@ -37,6 +37,9 @@
>>  #include <uuid/uuid.h>
>>  #include "btrfs-list.h"
>>
>> +#define BTRFS_LIST_NFILTERS_INCREASE   (2 * BTRFS_LIST_FILTER_MAX)
>> +#define BTRFS_LIST_NCOMPS_INCREASE     (2 * BTRFS_LIST_COMP_MAX)
>> +
>>  /* we store all the roots we find in an rbtree so that we can
>>   * search for them later.
>>   */
>> @@ -49,19 +52,28 @@ struct root_lookup {
>>   */
>>  struct root_info {
>>         struct rb_node rb_node;
>> +       struct rb_node sort_node;
>>
>>         /* this root's id */
>>         u64 root_id;
>>
>> +       /* equal the offset of the root's key */
>> +       u64 root_offset;
>> +
>>         /* the id of the root that references this one */
>>         u64 ref_tree;
>>
>>         /* the dir id we're in from ref_tree */
>>         u64 dir_id;
>>
>> +       u64 top_id;
>> +
>>         /* generation when the root is created or last updated */
>>         u64 gen;
>>
>> +       /* creation generation of this root in sec*/
>> +       u64 ogen;
>> +
>>         /* creation time of this root in sec*/
>>         time_t otime;
>>
>> @@ -73,35 +85,254 @@ struct root_info {
>>         char *path;
>>
>>         /* the name of this root in the directory it lives in */
>> -       char name[];
>> +       char *name;
>> +
>> +       char *full_path;
>>  };
>>
>> +struct {
>> +       char    *name;
>> +       char    *column_name;
>> +       int     need_print;
>> +} btrfs_list_columns[] = {
>> +       {
>> +               .name           = "ID",
>> +               .column_name    = "ID",
>> +               .need_print     = 1,
>> +       },
>> +       {
>> +               .name           = "gen",
>> +               .column_name    = "Gen",
>> +               .need_print     = 1,
>> +       },
>> +       {
>> +               .name           = "cgen",
>> +               .column_name    = "CGen",
>> +               .need_print     = 0,
>> +       },
>> +       {
>> +               .name           = "parent",
>> +               .column_name    = "Parent",
>> +               .need_print     = 0,
>> +       },
>> +       {
>> +               .name           = "top level",
>> +               .column_name    = "Top Level",
>> +               .need_print     = 1,
>> +       },
>> +       {
>> +               .name           = "otime",
>> +               .column_name    = "OTime",
>> +               .need_print     = 0,
>> +       },
>> +       {
>> +               .name           = "uuid",
>> +               .column_name    = "UUID",
>> +               .need_print     = 0,
>> +       },
>> +       {
>> +               .name           = "path",
>> +               .column_name    = "Path",
>> +               .need_print     = 1,
>> +       },
>> +       {
>> +               .name           = NULL,
>> +               .column_name    = NULL,
>> +               .need_print     = 0,
>> +       },
>> +};
>> +
>> +static btrfs_list_filter_func all_filter_funcs[];
>> +static btrfs_list_comp_func all_comp_funcs[];
>> +
>> +void btrfs_list_setup_print_column(enum btrfs_list_column_enum column)
>> +{
>> +       int i;
>> +
>> +       BUG_ON(column < 0 || column > BTRFS_LIST_ALL);
>> +
>> +       if (column < BTRFS_LIST_ALL) {
>> +               btrfs_list_columns[column].need_print = 1;
>> +               return;
>> +       }
>> +
>> +       for (i = 0; i < BTRFS_LIST_ALL; i++)
>> +               btrfs_list_columns[i].need_print = 1;
>> +}
>> +
>>  static void root_lookup_init(struct root_lookup *tree)
>>  {
>>         tree->root.rb_node = NULL;
>>  }
>>
>> -static int comp_entry(struct root_info *entry, u64 root_id, u64 ref_tree)
>> +static int comp_entry_with_rootid(struct root_info *entry1,
>> +                                 struct root_info *entry2,
>> +                                 int is_descending)
>>  {
>> -       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;
>> +       int ret;
>> +
>> +       if (entry1->root_id > entry2->root_id)
>> +               ret = 1;
>> +       else if (entry1->root_id < entry2->root_id)
>> +               ret = -1;
>> +       else
>> +               ret = 0;
>> +
>> +       return is_descending ? -ret : ret;
>> +}
>> +
>> +static int comp_entry_with_gen(struct root_info *entry1,
>> +                              struct root_info *entry2,
>> +                              int is_descending)
>> +{
>> +       int ret;
>> +
>> +       if (entry1->gen > entry2->gen)
>> +               ret = 1;
>> +       else if (entry1->gen < entry2->gen)
>> +               ret = -1;
>> +       else
>> +               ret = 0;
>> +
>> +       return is_descending ? -ret : ret;
>> +}
>> +
>> +static int comp_entry_with_ogen(struct root_info *entry1,
>> +                               struct root_info *entry2,
>> +                               int is_descending)
>> +{
>> +       int ret;
>> +
>> +       if (entry1->ogen > entry2->ogen)
>> +               ret = 1;
>> +       else if (entry1->ogen < entry2->ogen)
>> +               ret = -1;
>> +       else
>> +               ret = 0;
>> +
>> +       return is_descending ? -ret : ret;
>> +}
>> +
>> +static btrfs_list_comp_func all_comp_funcs[] = {
>> +       [BTRFS_LIST_COMP_ROOTID]        = comp_entry_with_rootid,
>> +       [BTRFS_LIST_COMP_OGEN]          = comp_entry_with_ogen,
>> +       [BTRFS_LIST_COMP_GEN]           = comp_entry_with_gen,
>> +};
>> +
>> +struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void)
>> +{
>> +       struct btrfs_list_comparer_set *set;
>> +       int size;
>> +
>> +       size = sizeof(struct btrfs_list_comparer_set) +
>> +              BTRFS_LIST_NCOMPS_INCREASE * sizeof(struct btrfs_list_comparer);
>> +       set = malloc(size);
>> +       if (!set) {
>> +               fprintf(stderr, "memory allocation failed\n");
>> +               exit(1);
>> +       }
>> +
>> +       memset(set, 0, size);
>> +       set->total = BTRFS_LIST_NCOMPS_INCREASE;
>> +
>> +       return set;
>> +}
>> +
>> +void btrfs_list_free_comparer_set(struct btrfs_list_comparer_set *comp_set)
>> +{
>> +       free(comp_set);
>> +}
>> +
>> +int btrfs_list_setup_comparer(struct btrfs_list_comparer_set  **comp_set,
>> +                             enum btrfs_list_comp_enum comparer,
>> +                             int is_descending)
>> +{
>> +       struct btrfs_list_comparer_set *set = *comp_set;
>> +       int size;
>> +
>> +       BUG_ON(!set);
>> +       BUG_ON(comparer >= BTRFS_LIST_COMP_MAX);
>> +       BUG_ON(set->ncomps > set->total);
>> +
>> +       if (set->ncomps == set->total) {
>> +               size = set->total + BTRFS_LIST_NCOMPS_INCREASE;
>> +               size = sizeof(*set) + size * sizeof(struct btrfs_list_comparer);
>> +               set = realloc(set, size);
>> +               if (!set) {
>> +                       fprintf(stderr, "memory allocation failed\n");
>> +                       exit(1);
>> +               }
>> +
>> +               memset(&set->comps[set->total], 0,
>> +                      BTRFS_LIST_NCOMPS_INCREASE *
>> +                      sizeof(struct btrfs_list_comparer));
>> +               set->total += BTRFS_LIST_NCOMPS_INCREASE;
>> +               *comp_set = set;
>> +       }
>> +
>> +       BUG_ON(set->comps[set->ncomps].comp_func);
>> +
>> +       set->comps[set->ncomps].comp_func = all_comp_funcs[comparer];
>> +       set->comps[set->ncomps].is_descending = is_descending;
>> +       set->ncomps++;
>>         return 0;
>>  }
>>
>> -static int comp_entry_with_gen(struct root_info *entry, u64 root_id,
>> -                              u64 ref_tree, u64 gen)
>> +static int sort_comp(struct root_info *entry1, struct root_info *entry2,
>> +                    struct btrfs_list_comparer_set *set)
>>  {
>> -       if (entry->gen < gen)
>> -               return 1;
>> -       if (entry->gen > gen)
>> -               return -1;
>> -       return comp_entry(entry, root_id, ref_tree);
>> +       int rootid_compared = 0;
>> +       int i, ret = 0;
>> +
>> +       if (!set || !set->ncomps)
>> +               goto comp_rootid;
>> +
>> +       for (i = 0; i < set->ncomps; i++) {
>> +               if (!set->comps[i].comp_func)
>> +                       break;
>> +
>> +               ret = set->comps[i].comp_func(entry1, entry2,
>> +                                             set->comps[i].is_descending);
>> +               if (ret)
>> +                       return ret;
>> +
>> +               if (set->comps[i].comp_func == comp_entry_with_rootid)
>> +                       rootid_compared = 1;
>> +       }
>> +
>> +       if (!rootid_compared) {
>> +comp_rootid:
>> +               ret = comp_entry_with_rootid(entry1, entry2, 0);
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +static int sort_tree_insert(struct root_lookup *sort_tree,
>> +                           struct root_info *ins,
>> +                           struct btrfs_list_comparer_set *comp_set)
>> +{
>> +       struct rb_node **p = &sort_tree->root.rb_node;
>> +       struct rb_node *parent = NULL;
>> +       struct root_info *curr;
>> +       int ret;
>> +
>> +       while (*p) {
>> +               parent = *p;
>> +               curr = rb_entry(parent, struct root_info, sort_node);
>> +
>> +               ret = sort_comp(ins, curr, comp_set);
>> +               if (ret < 0)
>> +                       p = &(*p)->rb_left;
>> +               else if (ret > 0)
>> +                       p = &(*p)->rb_right;
>> +               else
>> +                       return -EEXIST;
>> +       }
>> +
>> +       rb_link_node(&ins->sort_node, parent, p);
>> +       rb_insert_color(&ins->sort_node, &sort_tree->root);
>> +       return 0;
>>  }
>>
>>  /*
>> @@ -109,118 +340,165 @@ static int comp_entry_with_gen(struct root_info *entry, u64 root_id,
>>   * 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, u64 *gen, struct rb_node *node)
>> +static int root_tree_insert(struct root_lookup *root_tree,
>> +                           struct root_info *ins)
>>  {
>> -       struct rb_node ** p = &root->rb_node;
>> +       struct rb_node **p = &root_tree->root.rb_node;
>>         struct rb_node * parent = NULL;
>> -       struct root_info *entry;
>> -       int comp;
>> +       struct root_info *curr;
>> +       int ret;
>>
>>         while(*p) {
>>                 parent = *p;
>> -               entry = rb_entry(parent, struct root_info, rb_node);
>> +               curr = rb_entry(parent, struct root_info, rb_node);
>>
>> -               if (!gen)
>> -                       comp = comp_entry(entry, root_id, ref_tree);
>> -               else
>> -                       comp = comp_entry_with_gen(entry, root_id, ref_tree,
>> -                                                  *gen);
>> -
>> -               if (comp < 0)
>> +               ret = comp_entry_with_rootid(ins, curr, 0);
>> +               if (ret < 0)
>>                         p = &(*p)->rb_left;
>> -               else if (comp > 0)
>> +               else if (ret > 0)
>>                         p = &(*p)->rb_right;
>>                 else
>> -                       return parent;
>> +                       return -EEXIST;
>>         }
>>
>> -       entry = rb_entry(parent, struct root_info, rb_node);
>> -       rb_link_node(node, parent, p);
>> -       rb_insert_color(node, root);
>> -       return NULL;
>> +       rb_link_node(&ins->rb_node, parent, p);
>> +       rb_insert_color(&ins->rb_node, &root_tree->root);
>> +       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 struct root_info *root_tree_search(struct root_lookup *root_tree,
>> +                                         u64 root_id)
>>  {
>> -       struct rb_node * n = root->rb_node;
>> +       struct rb_node *n = root_tree->root.rb_node;
>>         struct root_info *entry;
>> +       struct root_info tmp;
>> +       int ret;
>> +
>> +       tmp.root_id = root_id;
>>
>>         while(n) {
>>                 entry = rb_entry(n, struct root_info, rb_node);
>>
>> -               if (entry->root_id < root_id)
>> +               ret = comp_entry_with_rootid(&tmp, entry, 0);
>> +               if (ret < 0)
>>                         n = n->rb_left;
>> -               else if (entry->root_id > root_id)
>> +               else if (ret > 0)
>>                         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;
>> -                       }
>> +               else
>>                         return entry;
>> -               }
>>         }
>>         return NULL;
>>  }
>>
>> +static int update_root(struct root_lookup *root_lookup,
>> +                      u64 root_id, u64 ref_tree, u64 root_offset, u64 dir_id,
>> +                      char *name, int name_len, u64 ogen, u64 gen, time_t ot,
>> +                      void *uuid)
>> +{
>> +       struct root_info *ri;
>> +
>> +       ri = root_tree_search(root_lookup, root_id);
>> +       if (!ri || ri->root_id != root_id)
>> +               return -ENOENT;
>> +       if (name && name_len > 0) {
>> +               if (ri->name)
>> +                       free(ri->name);
>> +
>> +               ri->name = malloc(name_len + 1);
>> +               if (!ri->name) {
>> +                       fprintf(stderr, "memory allocation failed\n");
>> +                       exit(1);
>> +               }
>> +               strncpy(ri->name, name, name_len);
>> +               ri->name[name_len] = 0;
>> +       }
>> +       if (ref_tree)
>> +               ri->ref_tree = ref_tree;
>> +       if (root_offset)
>> +               ri->root_offset = root_offset;
>> +       if (dir_id)
>> +               ri->dir_id = dir_id;
>> +       if (gen)
>> +               ri->gen = gen;
>> +       if (ogen)
>> +               ri->ogen = ogen;
>> +       if (!ri->ogen && root_offset)
>> +               ri->ogen = root_offset;
>> +       if (ot)
>> +               ri->otime = ot;
>> +       if (uuid)
>> +               memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
>> +
>> +       return 0;
>> +}
>> +
>>  /*
>> - * 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
>> + * add_root - update the existed root, or allocate a new root and insert it
>> + *           into the lookup tree.
>> + * root_id: object id of the root
>> + * ref_tree: object id of the referring root.
>> + * root_offset: offset value of the root'key
>> + * dir_id: inode id of the directory in ref_tree where this root can be found.
>> + * name: the name of root_id in that directory
>> + * name_len: the length of name
>> + * ogen: the original generation of the root
>> + * gen: the current generation of the root
>> + * ot: the original time(create time) of the root
>> + * uuid: uuid of the root
>>   */
>>  static int add_root(struct root_lookup *root_lookup,
>> -                   u64 root_id, u64 ref_tree, u64 dir_id, char *name,
>> -                   int name_len, u64 *gen, time_t ot, void *uuid)
>> +                   u64 root_id, u64 ref_tree, u64 root_offset, u64 dir_id,
>> +                   char *name, int name_len, u64 ogen, u64 gen, time_t ot,
>> +                   void *uuid)
>>  {
>>         struct root_info *ri;
>> -       struct rb_node *ret;
>> -       ri = malloc(sizeof(*ri) + name_len + 1);
>> +       int ret;
>> +
>> +       ret = update_root(root_lookup, root_id, ref_tree, root_offset, dir_id,
>> +                         name, name_len, ogen, gen, ot, uuid);
>> +       if (!ret)
>> +               return 0;
>> +
>> +       ri = malloc(sizeof(*ri));
>>         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;
>> +       memset(ri, 0, sizeof(*ri));
>>         ri->root_id = root_id;
>> -       ri->ref_tree = ref_tree;
>> -       if (name)
>> +
>> +       if (name && name_len > 0) {
>> +               ri->name = malloc(name_len + 1);
>> +               if (!ri->name) {
>> +                       fprintf(stderr, "memory allocation failed\n");
>> +                       exit(1);
>> +               }
>>                 strncpy(ri->name, name, name_len);
>> -       if (name_len > 0)
>>                 ri->name[name_len] = 0;
>> +       }
>> +       if (ref_tree)
>> +               ri->ref_tree = ref_tree;
>> +       if (dir_id)
>> +               ri->dir_id = dir_id;
>> +       if (root_offset)
>> +               ri->root_offset = root_offset;
>>         if (gen)
>> -               ri->gen = *gen;
>> -       ri->otime = ot;
>> +               ri->gen = gen;
>> +       if (ogen)
>> +               ri->ogen = ogen;
>> +       if (!ri->ogen && root_offset)
>> +               ri->ogen = root_offset;
>> +       if (ot)
>> +               ri->otime = ot;
>>
>>         if (uuid)
>>                 memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
>> -       else
>> -               memset(&ri->uuid, 0, BTRFS_UUID_SIZE);
>>
>> -       ret = tree_insert(&root_lookup->root, root_id, ref_tree, gen,
>> -                         &ri->rb_node);
>> +       ret = root_tree_insert(root_lookup, ri);
>>         if (ret) {
>>                 printf("failed to insert tree %llu\n", (unsigned long long)root_id);
>>                 exit(1);
>> @@ -228,24 +506,33 @@ static int add_root(struct root_lookup *root_lookup,
>>         return 0;
>>  }
>>
>> -static int update_root(struct root_lookup *root_lookup, u64 root_id, u64 gen,
>> -                       time_t ot, void *uuid)
>> +void __free_root_info(struct root_info *ri)
>>  {
>> -       struct root_info *ri;
>> +       if (ri->name)
>> +               free(ri->name);
>>
>> -       ri = tree_search(&root_lookup->root, root_id);
>> -       if (!ri || ri->root_id != root_id) {
>> -               fprintf(stderr, "could not find subvol %llu\n", root_id);
>> -               return -ENOENT;
>> -       }
>> -       ri->gen = gen;
>> -       ri->otime = ot;
>> -       if (uuid)
>> -               memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
>> -       else
>> -               memset(&ri->uuid, 0, BTRFS_UUID_SIZE);
>> +       if (ri->path)
>> +               free(ri->path);
>>
>> -       return 0;
>> +       if (ri->full_path)
>> +               free(ri->full_path);
>> +
>> +       free(ri);
>> +}
>> +
>> +void __free_all_subvolumn(struct root_lookup *root_tree)
>> +{
>> +       struct root_info *entry;
>> +       struct rb_node *n;
>> +
>> +       n = rb_first(&root_tree->root);
>> +       while (n) {
>> +               entry = rb_entry(n, struct root_info, rb_node);
>> +               rb_erase(n, &root_tree->root);
>> +               __free_root_info(entry);
>> +
>> +               n = rb_first(&root_tree->root);
>> +       }
>>  }
>>
>>  /*
>> @@ -255,8 +542,7 @@ static int update_root(struct root_lookup *root_lookup, u64 root_id, u64 gen,
>>   * 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 *parent_id, u64 *top_id, char **path)
>> +static int resolve_root(struct root_lookup *rl, struct root_info *ri)
>>  {
>>         char *full_path = NULL;
>>         int len = 0;
>> @@ -266,7 +552,6 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>>          * we go backwards from the root_info object and add pathnames
>>          * from parent directories as we go.
>>          */
>> -       *parent_id = 0;
>>         found = ri;
>>         while (1) {
>>                 char *tmp;
>> @@ -275,6 +560,10 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>>
>>                 /* room for / and for null */
>>                 tmp = malloc(add_len + 2 + len);
>> +               if (!tmp) {
>> +                       perror("malloc failed");
>> +                       exit(1);
>> +               }
>>                 if (full_path) {
>>                         memcpy(tmp + add_len + 1, full_path, len);
>>                         tmp[add_len] = '/';
>> @@ -289,13 +578,10 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>>                 }
>>
>>                 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;
>> +                       ri->top_id = next;
>>                         break;
>>                 }
>>
>> @@ -303,14 +589,14 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>>                  * if the ref_tree wasn't in our tree of roots, we're
>>                  * at the top
>>                  */
>> -               found = tree_search(&rl->root, next);
>> +               found = root_tree_search(rl, next);
>>                 if (!found) {
>> -                       *top_id = next;
>> +                       ri->top_id = next;
>>                         break;
>>                 }
>>         }
>>
>> -       *path = full_path;
>> +       ri->full_path = full_path;
>>
>>         return 0;
>>  }
>> @@ -608,7 +894,7 @@ build:
>>         return full;
>>  }
>>
>> -static int get_default_subvolid(int fd, u64 *default_id)
>> +int btrfs_list_get_default_subvolume(int fd, u64 *default_id)
>>  {
>>         struct btrfs_ioctl_search_args args;
>>         struct btrfs_ioctl_search_key *sk = &args.key;
>> @@ -674,10 +960,9 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>>         int name_len;
>>         char *name;
>>         u64 dir_id;
>> -       u8 type;
>>         u64 gen = 0;
>> +       u64 ogen;
>>         int i;
>> -       int get_gen = 0;
>>         time_t t;
>>         u8 uuid[BTRFS_UUID_SIZE];
>>
>> @@ -692,7 +977,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>>          * only send back this type of key now.
>>          */
>>         sk->max_type = BTRFS_ROOT_BACKREF_KEY;
>> -       sk->min_type = BTRFS_ROOT_BACKREF_KEY;
>> +       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>>
>>         sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
>>
>> @@ -704,7 +989,6 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>>         sk->max_offset = (u64)-1;
>>         sk->max_transid = (u64)-1;
>>
>> -again:
>>         /* just a big number, doesn't matter much */
>>         sk->nr_items = 4096;
>>
>> @@ -726,28 +1010,32 @@ again:
>>                         sh = (struct btrfs_ioctl_search_header *)(args.buf +
>>                                                                   off);
>>                         off += sizeof(*sh);
>> -                       if (!get_gen && sh->type == BTRFS_ROOT_BACKREF_KEY) {
>> +                       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, NULL, 0, NULL);
>> -                       } else if (get_gen && sh->type == BTRFS_ROOT_ITEM_KEY) {
>> +                                        0, dir_id, name, name_len, 0, 0, 0,
>> +                                        NULL);
>> +                       } else if (sh->type == BTRFS_ROOT_ITEM_KEY) {
>>                                 ri = (struct btrfs_root_item *)(args.buf + off);
>>                                 gen = btrfs_root_generation(ri);
>>                                 if(sh->len >
>>                                    sizeof(struct btrfs_root_item_v0)) {
>>                                         t = ri->otime.sec;
>> +                                       ogen = btrfs_root_otransid(ri);
>>                                         memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
>>                                 } else {
>>                                         t = 0;
>> +                                       ogen = 0;
>>                                         memset(uuid, 0, BTRFS_UUID_SIZE);
>>                                 }
>>
>> -                               update_root(root_lookup, sh->objectid, gen, t,
>> -                                       uuid);
>> +                               add_root(root_lookup, sh->objectid, 0,
>> +                                        sh->offset, 0, NULL, 0, ogen, gen, t,
>> +                                        uuid);
>>                         }
>>
>>                         off += sh->len;
>> @@ -761,129 +1049,139 @@ again:
>>                         sk->min_offset = sh->offset;
>>                 }
>>                 sk->nr_items = 4096;
>> -               /* this iteration is done, step forward one root for the next
>> -                * ioctl
>> -                */
>> -               if (get_gen)
>> -                       type = BTRFS_ROOT_ITEM_KEY;
>> +               sk->min_offset++;
>> +               if (!sk->min_offset)    /* overflow */
>> +                       sk->min_type++;
>>                 else
>> -                       type = BTRFS_ROOT_BACKREF_KEY;
>> +                       continue;
>>
>> -               if (sk->min_type < type) {
>> -                       sk->min_type = type;
>> -                       sk->min_offset = 0;
>> -               } else  if (sk->min_objectid < BTRFS_LAST_FREE_OBJECTID) {
>> +               if (sk->min_type > BTRFS_ROOT_BACKREF_KEY) {
>> +                       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>>                         sk->min_objectid++;
>> -                       sk->min_type = type;
>> -                       sk->min_offset = 0;
>>                 } else
>> +                       continue;
>> +
>> +               if (sk->min_objectid > sk->max_objectid)
>>                         break;
>>         }
>>
>> -       if (!get_gen) {
>> -               memset(&args, 0, sizeof(args));
>> +       return 0;
>> +}
>>
>> -               sk->tree_id = 1;
>> -               sk->max_type = BTRFS_ROOT_ITEM_KEY;
>> -               sk->min_type = BTRFS_ROOT_ITEM_KEY;
>> +static int filter_by_rootid(struct root_info *ri, void *arg)
>> +{
>> +       u64 default_root_id = *((u64 *)arg);
>>
>> -               sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
>> +       return ri->root_id == default_root_id;
>> +}
>>
>> -               sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
>> -               sk->max_offset = (u64)-1;
>> -               sk->max_transid = (u64)-1;
>> +static int filter_snapshot(struct root_info *ri, void *arg)
>> +{
>> +       return !!ri->root_offset;
>> +}
>> +
>> +static btrfs_list_filter_func all_filter_funcs[] = {
>> +       [BTRFS_LIST_FILTER_ROOTID]              = filter_by_rootid,
>> +       [BTRFS_LIST_FILTER_SNAPSHOT_ONLY]       = filter_snapshot,
>> +};
>>
>> -               get_gen = 1;
>> -               goto again;
>> +struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void)
>> +{
>> +       struct btrfs_list_filter_set *set;
>> +       int size;
>> +
>> +       size = sizeof(struct btrfs_list_filter_set) +
>> +              BTRFS_LIST_NFILTERS_INCREASE * sizeof(struct btrfs_list_filter);
>> +       set = malloc(size);
>> +       if (!set) {
>> +               fprintf(stderr, "memory allocation failed\n");
>> +               exit(1);
>>         }
>> -       return 0;
>> +
>> +       memset(set, 0, size);
>> +       set->total = BTRFS_LIST_NFILTERS_INCREASE;
>> +
>> +       return set;
>>  }
>>
>> -static int __list_snapshot_search(int fd, struct root_lookup *root_lookup)
>> +void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set)
>>  {
>> -       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 gen = 0;
>> -       int i;
>> -
>> -       root_lookup_init(root_lookup);
>> -       memset(&args, 0, sizeof(args));
>> +       free(filter_set);
>> +}
>>
>> -       sk->tree_id = 1;
>> -       sk->max_type = BTRFS_ROOT_ITEM_KEY;
>> -       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>> -       sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
>> -       sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
>> -       sk->max_offset = (u64)-1;
>> -       sk->max_transid = (u64)-1;
>> -       sk->nr_items = 4096;
>> +int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
>> +                           enum btrfs_list_filter_enum filter, void *data)
>> +{
>> +       struct btrfs_list_filter_set *set = *filter_set;
>> +       int size;
>> +
>> +       BUG_ON(!set);
>> +       BUG_ON(filter >= BTRFS_LIST_FILTER_MAX);
>> +       BUG_ON(set->nfilters > set->total);
>> +
>> +       if (set->nfilters == set->total) {
>> +               size = set->total + BTRFS_LIST_NFILTERS_INCREASE;
>> +               size = sizeof(*set) + size * sizeof(struct btrfs_list_filter);
>> +               set = realloc(set, size);
>> +               if (!set) {
>> +                       fprintf(stderr, "memory allocation failed\n");
>> +                       exit(1);
>> +               }
>>
>> -       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;
>> +               memset(&set->filters[set->total], 0,
>> +                      BTRFS_LIST_NFILTERS_INCREASE *
>> +                      sizeof(struct btrfs_list_filter));
>> +               set->total += BTRFS_LIST_NFILTERS_INCREASE;
>> +               *filter_set = set;
>> +       }
>>
>> -               off = 0;
>> +       BUG_ON(set->filters[set->nfilters].filter_func);
>>
>> -               /*
>> -                * 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;
>> -                       time_t  t;
>> -                       u8 uuid[BTRFS_UUID_SIZE];
>> +       set->filters[set->nfilters].filter_func = all_filter_funcs[filter];
>> +       set->filters[set->nfilters].data = data;
>> +       set->nfilters++;
>> +       return 0;
>> +}
>>
>> -                       sh = (struct btrfs_ioctl_search_header *)(args.buf +
>> -                                                                 off);
>> -                       off += sizeof(*sh);
>> -                       if (sh->type == BTRFS_ROOT_ITEM_KEY && sh->offset) {
>> -                               item = (struct btrfs_root_item *)(args.buf + off);
>> -                               if(sh->len >
>> -                                  sizeof(struct btrfs_root_item_v0)) {
>> -                                       t = item->otime.sec;
>> -                                       memcpy(uuid, item->uuid,
>> -                                              BTRFS_UUID_SIZE);
>> -                               } else {
>> -                                       t = 0;
>> -                                       memset(uuid, 0, BTRFS_UUID_SIZE);
>> -                               }
>> -                               gen = sh->offset;
>> +static int filter_root(struct root_info *ri,
>> +                      struct btrfs_list_filter_set *set)
>> +{
>> +       int i, ret;
>>
>> -                               add_root(root_lookup, sh->objectid, 0,
>> -                                        0, NULL, 0, &gen, t, uuid);
>> -                       }
>> -                       off += sh->len;
>> +       if (!set || !set->nfilters)
>> +               return 1;
>>
>> -                       /*
>> -                        * 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_ITEM_KEY) {
>> -                       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>> -                       sk->min_offset = 0;
>> -               } else  if (sk->min_objectid < BTRFS_LAST_FREE_OBJECTID) {
>> -                       sk->min_objectid++;
>> -                       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>> -                       sk->min_offset = 0;
>> -               } else
>> +       for (i = 0; i < set->nfilters; i++) {
>> +               if (!set->filters[i].filter_func)
>>                         break;
>> +               ret = set->filters[i].filter_func(ri, set->filters[i].data);
>> +               if (!ret)
>> +                       return 0;
>> +       }
>> +       return 1;
>> +}
>> +
>> +static void __filter_and_sort_subvol(struct root_lookup *all_subvols,
>> +                                   struct root_lookup *sort_tree,
>> +                                   struct btrfs_list_filter_set *filter_set,
>> +                                   struct btrfs_list_comparer_set *comp_set)
>> +{
>> +       struct rb_node *n;
>> +       struct root_info *entry;
>> +       int ret;
>> +
>> +       root_lookup_init(sort_tree);
>> +
>> +       n = rb_last(&all_subvols->root);
>> +       while (n) {
>> +               entry = rb_entry(n, struct root_info, rb_node);
>> +
>> +               resolve_root(all_subvols, entry);
>> +               ret = filter_root(entry, filter_set);
>> +               if (ret)
>> +                       sort_tree_insert(sort_tree, entry, comp_set);
>> +               n = rb_prev(n);
>>         }
>> -       return 0;
>>  }
>>
>>  static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
>> @@ -904,128 +1202,91 @@ static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
>>         return 0;
>>  }
>>
>> -int list_subvols(int fd, int print_parent, int get_default, int print_uuid)
>> +static void print_subvolume_column(struct root_info *subv,
>> +                                  enum btrfs_list_column_enum column)
>>  {
>> -       struct root_lookup root_lookup;
>> -       struct rb_node *n;
>> -       u64 default_id;
>> -       int ret;
>> +       char tstr[256];
>>         char uuidparse[37];
>>
>> -       if (get_default) {
>> -               ret = get_default_subvolid(fd, &default_id);
>> -               if (ret) {
>> -                       fprintf(stderr, "ERROR: can't perform the search - %s\n",
>> -                               strerror(errno));
>> -                       return ret;
>> -               }
>> -               if (default_id == 0) {
>> -                       fprintf(stderr, "ERROR: 'default' dir item not found\n");
>> -                       return ret;
>> -               }
>> -
>> -               /* no need to resolve roots if FS_TREE is default */
>> -               if (default_id == BTRFS_FS_TREE_OBJECTID) {
>> -                       printf("ID 5 (FS_TREE)\n");
>> -                       return ret;
>> -               }
>> -       }
>> -
>> -       ret = __list_subvol_search(fd, &root_lookup);
>> -       if (ret) {
>> -               fprintf(stderr, "ERROR: can't perform the search - %s\n",
>> -                               strerror(errno));
>> -               return ret;
>> +       BUG_ON(column >= BTRFS_LIST_ALL || column < 0);
>> +
>> +       switch (column) {
>> +       case BTRFS_LIST_OBJECTID:
>> +               printf("%llu", subv->root_id);
>> +               break;
>> +       case BTRFS_LIST_GENERATION:
>> +               printf("%llu", subv->gen);
>> +               break;
>> +       case BTRFS_LIST_OGENERATION:
>> +               printf("%llu", subv->ogen);
>> +               break;
>> +       case BTRFS_LIST_PARENT:
>> +               printf("%llu", subv->ref_tree);
>> +               break;
>> +       case BTRFS_LIST_TOP_LEVEL:
>> +               printf("%llu", subv->top_id);
>> +               break;
>> +       case BTRFS_LIST_OTIME:
>> +               if (subv->otime)
>> +                       strftime(tstr, 256, "%Y-%m-%d %X",
>> +                                localtime(&subv->otime));
>> +               else
>> +                       strcpy(tstr, "-");
>> +               printf("%s", tstr);
>> +               break;
>> +       case BTRFS_LIST_UUID:
>> +               if (uuid_is_null(subv->uuid))
>> +                       strcpy(uuidparse, "-");
>> +               else
>> +                       uuid_unparse(subv->uuid, uuidparse);
>> +               printf("%s", uuidparse);
>> +               break;
>> +       case BTRFS_LIST_PATH:
>> +               BUG_ON(!subv->full_path);
>> +               printf("%s", subv->full_path);
>> +               break;
>> +       default:
>> +               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.
>> -        */
>> -       ret = __list_subvol_fill_paths(fd, &root_lookup);
>> -       if (ret < 0)
>> -               return ret;
>> -
>> -       /* now that we have all the subvol-relative paths filled in,
>> -        * we have to string the subvols together so that we can get
>> -        * a path all the way back to the FS root
>> -        */
>> -       n = rb_last(&root_lookup.root);
>> -       while (n) {
>> -               struct root_info *entry;
>> -               u64 level;
>> -               u64 parent_id;
>> -               char *path;
>> +static void print_single_volume_info_default(struct root_info *subv)
>> +{
>> +       int i;
>>
>> -               entry = rb_entry(n, struct root_info, rb_node);
>> -               if (get_default && entry->root_id != default_id) {
>> -                       n = rb_prev(n);
>> +       for (i = 0; i < BTRFS_LIST_ALL; i++) {
>> +               if (!btrfs_list_columns[i].need_print)
>>                         continue;
>> -               }
>>
>> -               resolve_root(&root_lookup, entry, &parent_id, &level, &path);
>> -               if (print_parent) {
>> -                       if (print_uuid) {
>> -                               if (uuid_is_null(entry->uuid))
>> -                                       strcpy(uuidparse, "-");
>> -                               else
>> -                                       uuid_unparse(entry->uuid, uuidparse);
>> -                               printf("ID %llu gen %llu parent %llu top level %llu"
>> -                                       " uuid %s path %s\n",
>> -                                       (unsigned long long)entry->root_id,
>> -                                       (unsigned long long)entry->gen,
>> -                                       (unsigned long long)parent_id,
>> -                                       (unsigned long long)level,
>> -                                       uuidparse, path);
>> -                       } else {
>> -                               printf("ID %llu gen %llu parent %llu top level"
>> -                                       " %llu path %s\n",
>> -                                       (unsigned long long)entry->root_id,
>> -                                       (unsigned long long)entry->gen,
>> -                                       (unsigned long long)parent_id,
>> -                                       (unsigned long long)level, path);
>> -                       }
>> -               } else {
>> -                       if (print_uuid) {
>> -                               if (uuid_is_null(entry->uuid))
>> -                                       strcpy(uuidparse, "-");
>> -                               else
>> -                                       uuid_unparse(entry->uuid, uuidparse);
>> -                               printf("ID %llu gen %llu top level %llu"
>> -                                       " uuid %s path %s\n",
>> -                                       (unsigned long long)entry->root_id,
>> -                                       (unsigned long long)entry->gen,
>> -                                       (unsigned long long)level,
>> -                                       uuidparse, path);
>> -                       } else {
>> -                               printf("ID %llu gen %llu top level %llu path %s\n",
>> -                                       (unsigned long long)entry->root_id,
>> -                                       (unsigned long long)entry->gen,
>> -                                       (unsigned long long)level, path);
>> -                       }
>> -               }
>> +               printf("%s ", btrfs_list_columns[i].name);
>> +               print_subvolume_column(subv, i);
>>
>> -               free(path);
>> -               n = rb_prev(n);
>> +               if (i != BTRFS_LIST_PATH)
>> +                       printf(" ");
>>         }
>> +       printf("\n");
>> +}
>>
>> -       return ret;
>> +static void print_all_volume_info_default(struct root_lookup *sorted_tree)
>> +{
>> +       struct rb_node *n;
>> +       struct root_info *entry;
>> +
>> +       n = rb_first(&sorted_tree->root);
>> +       while (n) {
>> +               entry = rb_entry(n, struct root_info, sort_node);
>> +               print_single_volume_info_default(entry);
>> +               n = rb_next(n);
>> +       }
>>  }
>>
>> -int list_snapshots(int fd, int print_parent, int order, int print_uuid)
>> +int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
>> +                      struct btrfs_list_comparer_set *comp_set)
>>  {
>>         struct root_lookup root_lookup;
>> -       struct root_lookup root_lookup_snap;
>> -       struct rb_node *n;
>> +       struct root_lookup root_sort;
>>         int ret;
>>
>> -       ret = __list_snapshot_search(fd, &root_lookup_snap);
>> -       if (ret) {
>> -               fprintf(stderr, "ERROR: can't perform the search - %s\n",
>> -                               strerror(errno));
>> -               return ret;
>> -       }
>> -
>>         ret = __list_subvol_search(fd, &root_lookup);
>>         if (ret) {
>>                 fprintf(stderr, "ERROR: can't perform the search - %s\n",
>> @@ -1041,86 +1302,11 @@ int list_snapshots(int fd, int print_parent, int order, int print_uuid)
>>         if (ret < 0)
>>                 return ret;
>>
>> -       /* now that we have all the subvol-relative paths filled in,
>> -        * we have to string the subvols together so that we can get
>> -        * a path all the way back to the FS root
>> -        */
>> -       if (!order)
>> -               n = rb_last(&root_lookup_snap.root);
>> -       else
>> -               n = rb_first(&root_lookup_snap.root);
>> -       while (n) {
>> -               struct root_info *entry_snap;
>> -               struct root_info *entry;
>> -               u64 level;
>> -               u64 parent_id;
>> -               char *path;
>> -               time_t t;
>> -               char tstr[256];
>> -               char uuidparse[37];
>> -
>> -               entry_snap = rb_entry(n, struct root_info, rb_node);
>> -               entry = tree_search(&root_lookup.root, entry_snap->root_id);
>> -
>> -               resolve_root(&root_lookup, entry, &parent_id, &level, &path);
>> -               t = entry->otime;
>> -               if(t)
>> -                       strftime(tstr,256,"%Y-%m-%d %X",localtime(&t));
>> -               else
>> -                       strcpy(tstr,"-");
>> -               if (print_parent) {
>> -                       if (print_uuid) {
>> -                               if (uuid_is_null(entry->uuid))
>> -                                       strcpy(uuidparse, "-");
>> -                               else
>> -                                       uuid_unparse(entry->uuid, uuidparse);
>> -                               printf("ID %llu gen %llu cgen %llu parent %llu"
>> -                                       " top level %llu otime %s uuid %s path %s\n",
>> -                                       (unsigned long long)entry->root_id,
>> -                                       (unsigned long long)entry->gen,
>> -                                       (unsigned long long)entry_snap->gen,
>> -                                       (unsigned long long)parent_id,
>> -                                       (unsigned long long)level,
>> -                                       tstr, uuidparse, path);
>> -                       } else {
>> -                               printf("ID %llu gen %llu cgen %llu parent %llu"
>> -                                       " top level %llu otime %s path %s\n",
>> -                                       (unsigned long long)entry->root_id,
>> -                                       (unsigned long long)entry->gen,
>> -                                       (unsigned long long)entry_snap->gen,
>> -                                       (unsigned long long)parent_id,
>> -                                       (unsigned long long)level, tstr, path);
>> -                       }
>> -               } else {
>> -                       if (print_uuid) {
>> -                               if (uuid_is_null(entry->uuid))
>> -                                       strcpy(uuidparse, "-");
>> -                               else
>> -                                       uuid_unparse(entry->uuid, uuidparse);
>> -                               printf("ID %llu gen %llu cgen %llu top level %llu "
>> -                                       "otime %s uuid %s path %s\n",
>> -                                       (unsigned long long)entry->root_id,
>> -                                       (unsigned long long)entry->gen,
>> -                                       (unsigned long long)entry_snap->gen,
>> -                                       (unsigned long long)level,
>> -                                       tstr, uuidparse, path);
>> -                       } else {
>> -                               printf("ID %llu gen %llu cgen %llu top level %llu "
>> -                                       "otime %s path %s\n",
>> -                                       (unsigned long long)entry->root_id,
>> -                                       (unsigned long long)entry->gen,
>> -                                       (unsigned long long)entry_snap->gen,
>> -                                       (unsigned long long)level, tstr, path);
>> -                       }
>> -               }
>> -
>> -               free(path);
>> -               if (!order)
>> -                       n = rb_prev(n);
>> -               else
>> -                       n = rb_next(n);
>> -       }
>> +       __filter_and_sort_subvol(&root_lookup, &root_sort, filter_set,
>> +                                comp_set);
>>
>> +       print_all_volume_info_default(&root_sort);
>> +       __free_all_subvolumn(&root_lookup);
>>         return ret;
>>  }
>>
>> @@ -1203,7 +1389,7 @@ static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh,
>>         return 0;
>>  }
>>
>> -int find_updated_files(int fd, u64 root_id, u64 oldest_gen)
>> +int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen)
>>  {
>>         int ret;
>>         struct btrfs_ioctl_search_args args;
>> @@ -1304,7 +1490,7 @@ int find_updated_files(int fd, u64 root_id, u64 oldest_gen)
>>         return ret;
>>  }
>>
>> -char *path_for_root(int fd, u64 root)
>> +char *btrfs_list_path_for_root(int fd, u64 root)
>>  {
>>         struct root_lookup root_lookup;
>>         struct rb_node *n;
>> @@ -1322,19 +1508,17 @@ char *path_for_root(int fd, u64 root)
>>         n = rb_last(&root_lookup.root);
>>         while (n) {
>>                 struct root_info *entry;
>> -               u64 parent_id;
>> -               u64 level;
>> -               char *path;
>>
>>                 entry = rb_entry(n, struct root_info, rb_node);
>> -               resolve_root(&root_lookup, entry, &parent_id, &level, &path);
>> -               if (entry->root_id == root)
>> -                       ret_path = path;
>> -               else
>> -                       free(path);
>> +               resolve_root(&root_lookup, entry);
>> +               if (entry->root_id == root) {
>> +                       ret_path = entry->full_path;
>> +                       entry->full_path = NULL;
>> +               }
>>
>>                 n = rb_prev(n);
>>         }
>> +       __free_all_subvolumn(&root_lookup);
>>
>>         return ret_path;
>>  }
>> diff --git a/btrfs-list.h b/btrfs-list.h
>> index b4a7f30..ca3eb7b 100644
>> --- a/btrfs-list.h
>> +++ b/btrfs-list.h
>> @@ -16,7 +16,72 @@
>>   * Boston, MA 021110-1307, USA.
>>   */
>>
>> -int list_subvols(int fd, int print_parent, int get_default, int print_uuid);
>> -int list_snapshots(int fd, int print_parent, int order, int print_uuid);
>> -int find_updated_files(int fd, u64 root_id, u64 oldest_gen);
>> -char *path_for_root(int fd, u64 root);
>> +struct root_info;
>> +
>> +typedef int (*btrfs_list_filter_func)(struct root_info *, void *);
>> +typedef int (*btrfs_list_comp_func)(struct root_info *, struct root_info *,
>> +                                   int);
>> +
>> +struct btrfs_list_filter {
>> +       btrfs_list_filter_func filter_func;
>> +       void *data;
>> +};
>> +
>> +struct btrfs_list_comparer {
>> +       btrfs_list_comp_func comp_func;
>> +       int is_descending;
>> +};
>> +
>> +struct btrfs_list_filter_set {
>> +       int total;
>> +       int nfilters;
>> +       struct btrfs_list_filter filters[0];
>> +};
>> +
>> +struct btrfs_list_comparer_set {
>> +       int total;
>> +       int ncomps;
>> +       struct btrfs_list_comparer comps[0];
>> +};
>> +
>> +enum btrfs_list_column_enum {
>> +       BTRFS_LIST_OBJECTID,
>> +       BTRFS_LIST_GENERATION,
>> +       BTRFS_LIST_OGENERATION,
>> +       BTRFS_LIST_PARENT,
>> +       BTRFS_LIST_TOP_LEVEL,
>> +       BTRFS_LIST_OTIME,
>> +       BTRFS_LIST_UUID,
>> +       BTRFS_LIST_PATH,
>> +       BTRFS_LIST_ALL,
>> +};
>> +
>> +enum btrfs_list_filter_enum {
>> +       BTRFS_LIST_FILTER_ROOTID,
>> +       BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
>> +       BTRFS_LIST_FILTER_MAX,
>> +};
>> +
>> +enum btrfs_list_comp_enum {
>> +       BTRFS_LIST_COMP_ROOTID,
>> +       BTRFS_LIST_COMP_OGEN,
>> +       BTRFS_LIST_COMP_GEN,
>> +       BTRFS_LIST_COMP_MAX,
>> +};
>> +
>> +void btrfs_list_setup_print_column(enum btrfs_list_column_enum column);
>> +struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void);
>> +void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set);
>> +int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
>> +                           enum btrfs_list_filter_enum filter, void *data);
>> +struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void);
>> +void btrfs_list_free_comparer_set(struct btrfs_list_comparer_set *comp_set);
>> +int btrfs_list_setup_comparer(struct btrfs_list_comparer_set **comp_set,
>> +                             enum btrfs_list_comp_enum comparer,
>> +                             int is_descending);
>> +
>> +int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
>> +                      struct btrfs_list_comparer_set *comp_set);
>> +int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen);
>> +int btrfs_list_get_default_subvolume(int fd, u64 *default_id);
>> +char *btrfs_list_path_for_root(int fd, u64 root);
>> diff --git a/cmds-inspect.c b/cmds-inspect.c
>> index f943ed9..376fab2 100644
>> --- a/cmds-inspect.c
>> +++ b/cmds-inspect.c
>> @@ -194,7 +194,7 @@ static int cmd_logical_resolve(int argc, char **argv)
>>                 char *name;
>>
>>                 if (getpath) {
>> -                       name = path_for_root(fd, root);
>> +                       name = btrfs_list_path_for_root(fd, root);
>>                         if (IS_ERR(name))
>>                                 return PTR_ERR(name);
>>                         if (!name) {
>> diff --git a/cmds-subvolume.c b/cmds-subvolume.c
>> index cd4b5a7..b1cf2bd 100644
>> --- a/cmds-subvolume.c
>> +++ b/cmds-subvolume.c
>> @@ -28,6 +28,7 @@
>>  #include "ioctl.h"
>>  #include "qgroup.h"
>>
>> +#include "ctree.h"
>>  #include "commands.h"
>>  #include "btrfs-list.h"
>>
>> @@ -270,13 +271,15 @@ static const char * const cmd_subvol_list_usage[] = {
>>
>>  static int cmd_subvol_list(int argc, char **argv)
>>  {
>> +       struct btrfs_list_filter_set *filter_set;
>> +       struct btrfs_list_comparer_set *comparer_set;
>>         int fd;
>>         int ret;
>> -       int print_parent = 0;
>> -       int print_snap_only = 0;
>> -       int order = 0;
>> +       int order;
>>         char *subvol;
>> -       int print_uuid = 0;
>> +
>> +       filter_set = btrfs_list_alloc_filter_set();
>> +       comparer_set = btrfs_list_alloc_comparer_set();
>>
>>         optind = 1;
>>         while(1) {
>> @@ -286,14 +289,21 @@ static int cmd_subvol_list(int argc, char **argv)
>>
>>                 switch(c) {
>>                 case 'p':
>> -                       print_parent = 1;
>> +                       btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
>>                         break;
>>                 case 's':
>> -                       print_snap_only = 1;
>>                         order = atoi(optarg);
>> +                       btrfs_list_setup_filter(&filter_set,
>> +                                               BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
>> +                                               NULL);
>> +                       btrfs_list_setup_comparer(&comparer_set,
>> +                                                 BTRFS_LIST_COMP_OGEN,
>> +                                                 !order);
>> +                       btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
>> +                       btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
>>                         break;
>>                 case 'u':
>> -                       print_uuid =1;
>> +                       btrfs_list_setup_print_column(BTRFS_LIST_UUID);
>>                         break;
>>                 default:
>>                         usage(cmd_subvol_list_usage);
>> @@ -320,10 +330,8 @@ static int cmd_subvol_list(int argc, char **argv)
>>                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
>>                 return 12;
>>         }
>> -       if (!print_snap_only)
>> -               ret = list_subvols(fd, print_parent, 0, print_uuid);
>> -       else
>> -               ret = list_snapshots(fd, print_parent, order, print_uuid);
>> +
>> +       ret = btrfs_list_subvols(fd, filter_set, comparer_set);
>>         if (ret)
>>                 return 19;
>>         return 0;
>> @@ -483,6 +491,8 @@ static int cmd_subvol_get_default(int argc, char **argv)
>>         int fd;
>>         int ret;
>>         char *subvol;
>> +       struct btrfs_list_filter_set *filter_set;
>> +       u64 default_id;
>>
>>         if (check_argc_exact(argc, 2))
>>                 usage(cmd_subvol_get_default_usage);
>> @@ -504,7 +514,30 @@ static int cmd_subvol_get_default(int argc, char **argv)
>>                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
>>                 return 12;
>>         }
>> -       ret = list_subvols(fd, 0, 1, 0);
>> +
>> +       ret = btrfs_list_get_default_subvolume(fd, &default_id);
>> +       if (ret) {
>> +               fprintf(stderr, "ERROR: can't perform the search - %s\n",
>> +                       strerror(errno));
>> +               return ret;
>> +       }
>> +
>> +       if (default_id == 0) {
>> +               fprintf(stderr, "ERROR: 'default' dir item not found\n");
>> +               return ret;
>> +       }
>> +
>> +       /* no need to resolve roots if FS_TREE is default */
>> +       if (default_id == BTRFS_FS_TREE_OBJECTID) {
>> +               printf("ID 5 (FS_TREE)\n");
>> +               return ret;
>> +       }
>> +
>> +       filter_set = btrfs_list_alloc_filter_set();
>> +       btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
>> +                               (void *)&default_id);
>> +
>> +       ret = btrfs_list_subvols(fd, filter_set, NULL);
>>         if (ret)
>>                 return 19;
>>         return 0;
>> @@ -585,7 +618,7 @@ static int cmd_find_new(int argc, char **argv)
>>                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
>>                 return 12;
>>         }
>> -       ret = find_updated_files(fd, 0, last_gen);
>> +       ret = btrfs_list_find_updated_files(fd, 0, last_gen);
>>         if (ret)
>>                 return 19;
>>         return 0;
>> diff --git a/send-utils.c b/send-utils.c
>> index 096fa02..fcde5c2 100644
>> --- a/send-utils.c
>> +++ b/send-utils.c
>> @@ -244,7 +244,8 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
>>                                 if (!root_item_valid)
>>                                         goto skip;
>>
>> -                               path = path_for_root(mnt_fd, sh->objectid);
>> +                               path = btrfs_list_path_for_root(mnt_fd,
>> +                                                               sh->objectid);
>>                                 if (!path)
>>                                         path = strdup("");
>>                                 if (IS_ERR(path)) {
>> --
>> 1.7.6.5
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH V4 5/7] Btrfs-progs: restructure list_subvolumes
  2012-10-10  2:12     ` Miao Xie
@ 2012-10-10 19:45       ` Alex Lyakas
  2012-10-15  4:06         ` Miao Xie
  0 siblings, 1 reply; 31+ messages in thread
From: Alex Lyakas @ 2012-10-10 19:45 UTC (permalink / raw)
  To: miaox; +Cc: linux-btrfs

Hi Miao,
you are absolutely right, it could have happened with the old code as well.

On Wed, Oct 10, 2012 at 4:12 AM, Miao Xie <miaox@cn.fujitsu.com> wrote:
> On Tue, 9 Oct 2012 18:05:51 +0200, Alex Lyakas wrote:
>> Hi Miao,
>> I have some trouble with this code.
>>
>> The problem that I see is after subvolume deletion. What happens is
>> that __list_subvol_search() is called at the point, at which ROOT_ITEM
>> still exists in the root tree, but ROOT_BACKREF does not exist
>> anymore. I think this is because btrfs_ioctl_snap_destroy() calls
>> btrfs_unlink_subvol(), which calls btrfs_del_root_ref(), which deletes
>> ROOT_BACKREF, but ROOT_ITEM still exists until transaction is
>> committed.
>>
>> So what happens is that we end up with struct root_info that has
>> ref_tree==0 (and dir_id==0).
>> So later, when __list_subvol_fill_paths() is called, it fails to
>> perform BTRFS_IOC_INO_LOOKUP because of ref_tree==0. As a result,
>> subvol_uuid_search_init fails and everything stops.
>>
>> Previously, before your change, root_info was not added until we see
>> ROOT_BACKREF in the tree.
>
> The old code still has the similar problem if we delete the subvolume completely
> between __list_subvol_search() and __list_subvol_fill_paths().
>
>> How do you think this should be fixed?
>
> Yes, I think it should be fixed. We can filter the deleted subvolume.

Something like this?

diff --git a/btrfs-list.c b/btrfs-list.c
index e5f0f96..5fd262c 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -1004,6 +1004,23 @@ out:
        return 0;
 }

+static void __drop_deleting_roots(struct root_lookup *root_lookup)
+{
+       struct rb_node *n;
+
+again:
+       n = rb_first(&root_lookup->root);
+       while (n) {
+               struct root_info *entry = rb_entry(n, struct
root_info, rb_node);
+               if (!entry->ref_tree) {
+                       rb_erase(n, &root_lookup->root);
+                       free(entry);
+                       goto again;
+               }
+               n = rb_next(n);
+       }
+}
+
 static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 {
        int ret;
@@ -1123,6 +1140,8 @@ static int __list_subvol_search(int fd, struct
root_lookup *root_lookup)
                        break;
        }

+       __drop_deleting_roots(root_lookup);
+
        return 0;
 }








>
>>
>> Also, is there a way to issue a subvolume deletion and make sure that
>> it was deleted? I see that btrfs_ioctl_snap_destroy() calls
>
> no ROOT_BACKREF already tells us that the subvolume has been deleted, I think.
>
>> end_transaction() but not commit_transaction(). Moreover, there is no
>> way for the caller to know the transid, otherwise we could issue
>> BTRFS_IOC_WAIT_SYNC.
>
> we can get the transid by btrfs_ioctl_start_sync()
>
> Thanks
> Miao
>
>>
>> Thanks,
>> Alex.
>>
>>
>>
>> On Tue, Sep 18, 2012 at 1:06 PM, Miao Xie <miaox@cn.fujitsu.com> wrote:
>>> The current code of list_subvols() has very bad scalability, if we want to
>>> add new filter conditions or new sort methods, we have to modify lots of code.
>>>
>>> Beside that, the most code of list_snapshots() is similar to list_subvols(),
>>>
>>> So I restructure list_subvols(), and split the subvolume filter function,
>>> the subvolume sort function and the output function from list_subvols().
>>> In order to implement it, we defined some importtant structures:
>>> struct btrfs_list_filter {
>>>         btrfs_list_filter_func filter_func;
>>>         void *data;
>>> };
>>>
>>> struct btrfs_list_comparer {
>>>         btrfs_list_comp_func comp_func;
>>>         int is_descending;
>>> };
>>>
>>> struct {
>>>         char    *name;
>>>         char    *column_name;
>>>         int     need_print;
>>> } btrfs_list_columns[];
>>>
>>> If we want to add a new filter condition, we can choose a suitable filter
>>> function, or implement a new filter function[1], and add it into a set of
>>> the filters, and then pass the filter set into list_subvols(). We also can
>>> mix several filters (just add those filters into the set, and pass the set
>>> into list_subvols()) if the users specify two or more filter conditions.
>>>
>>> The subvolume sort function is similar to the subvolume filter function. The
>>> differentiation is the order of comparers in the array which is passed into
>>> list_subvols() show us the priority of the sort methods.
>>>
>>> The output function is different with the above two functions, we define a
>>> array to manage all the columns that can be outputed, and use a member variant
>>> (->need_print) to control the output of the relative column. Some columns are
>>> outputed by default. But we can change it according to the requirement of the
>>> users.
>>>
>>> After appling this patch, we needn't implement a independent list_snapshots()
>>> function, just pass a filter function which is used to identify the snapshot
>>> into list_subvols().
>>>
>>> [1]: If we implement new filter functions or compare functions, we must add
>>> them into the array all_filter_funcs or the array all_comp_funcs, and modify
>>> the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
>>>
>>> Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
>>> ---
>>> Changelog v3 -> v4:
>>> - add the filter set and comparer set which are used to manage the filters and
>>>   comparers. And the memory space of these two set are allocated dynamically,
>>>   in this way, the users can specify lots of filters and comparers, not be limited
>>>   by the size of the array.
>>>
>>> Changelog v1 -> v3:
>>> - new patch.
>>> ---
>>>  btrfs-list.c     | 1004 ++++++++++++++++++++++++++++++++----------------------
>>>  btrfs-list.h     |   73 ++++-
>>>  cmds-inspect.c   |    2 +-
>>>  cmds-subvolume.c |   59 +++-
>>>  send-utils.c     |    3 +-
>>>  5 files changed, 712 insertions(+), 429 deletions(-)
>>>
>>> diff --git a/btrfs-list.c b/btrfs-list.c
>>> index ed28021..bace903 100644
>>> --- a/btrfs-list.c
>>> +++ b/btrfs-list.c
>>> @@ -37,6 +37,9 @@
>>>  #include <uuid/uuid.h>
>>>  #include "btrfs-list.h"
>>>
>>> +#define BTRFS_LIST_NFILTERS_INCREASE   (2 * BTRFS_LIST_FILTER_MAX)
>>> +#define BTRFS_LIST_NCOMPS_INCREASE     (2 * BTRFS_LIST_COMP_MAX)
>>> +
>>>  /* we store all the roots we find in an rbtree so that we can
>>>   * search for them later.
>>>   */
>>> @@ -49,19 +52,28 @@ struct root_lookup {
>>>   */
>>>  struct root_info {
>>>         struct rb_node rb_node;
>>> +       struct rb_node sort_node;
>>>
>>>         /* this root's id */
>>>         u64 root_id;
>>>
>>> +       /* equal the offset of the root's key */
>>> +       u64 root_offset;
>>> +
>>>         /* the id of the root that references this one */
>>>         u64 ref_tree;
>>>
>>>         /* the dir id we're in from ref_tree */
>>>         u64 dir_id;
>>>
>>> +       u64 top_id;
>>> +
>>>         /* generation when the root is created or last updated */
>>>         u64 gen;
>>>
>>> +       /* creation generation of this root in sec*/
>>> +       u64 ogen;
>>> +
>>>         /* creation time of this root in sec*/
>>>         time_t otime;
>>>
>>> @@ -73,35 +85,254 @@ struct root_info {
>>>         char *path;
>>>
>>>         /* the name of this root in the directory it lives in */
>>> -       char name[];
>>> +       char *name;
>>> +
>>> +       char *full_path;
>>>  };
>>>
>>> +struct {
>>> +       char    *name;
>>> +       char    *column_name;
>>> +       int     need_print;
>>> +} btrfs_list_columns[] = {
>>> +       {
>>> +               .name           = "ID",
>>> +               .column_name    = "ID",
>>> +               .need_print     = 1,
>>> +       },
>>> +       {
>>> +               .name           = "gen",
>>> +               .column_name    = "Gen",
>>> +               .need_print     = 1,
>>> +       },
>>> +       {
>>> +               .name           = "cgen",
>>> +               .column_name    = "CGen",
>>> +               .need_print     = 0,
>>> +       },
>>> +       {
>>> +               .name           = "parent",
>>> +               .column_name    = "Parent",
>>> +               .need_print     = 0,
>>> +       },
>>> +       {
>>> +               .name           = "top level",
>>> +               .column_name    = "Top Level",
>>> +               .need_print     = 1,
>>> +       },
>>> +       {
>>> +               .name           = "otime",
>>> +               .column_name    = "OTime",
>>> +               .need_print     = 0,
>>> +       },
>>> +       {
>>> +               .name           = "uuid",
>>> +               .column_name    = "UUID",
>>> +               .need_print     = 0,
>>> +       },
>>> +       {
>>> +               .name           = "path",
>>> +               .column_name    = "Path",
>>> +               .need_print     = 1,
>>> +       },
>>> +       {
>>> +               .name           = NULL,
>>> +               .column_name    = NULL,
>>> +               .need_print     = 0,
>>> +       },
>>> +};
>>> +
>>> +static btrfs_list_filter_func all_filter_funcs[];
>>> +static btrfs_list_comp_func all_comp_funcs[];
>>> +
>>> +void btrfs_list_setup_print_column(enum btrfs_list_column_enum column)
>>> +{
>>> +       int i;
>>> +
>>> +       BUG_ON(column < 0 || column > BTRFS_LIST_ALL);
>>> +
>>> +       if (column < BTRFS_LIST_ALL) {
>>> +               btrfs_list_columns[column].need_print = 1;
>>> +               return;
>>> +       }
>>> +
>>> +       for (i = 0; i < BTRFS_LIST_ALL; i++)
>>> +               btrfs_list_columns[i].need_print = 1;
>>> +}
>>> +
>>>  static void root_lookup_init(struct root_lookup *tree)
>>>  {
>>>         tree->root.rb_node = NULL;
>>>  }
>>>
>>> -static int comp_entry(struct root_info *entry, u64 root_id, u64 ref_tree)
>>> +static int comp_entry_with_rootid(struct root_info *entry1,
>>> +                                 struct root_info *entry2,
>>> +                                 int is_descending)
>>>  {
>>> -       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;
>>> +       int ret;
>>> +
>>> +       if (entry1->root_id > entry2->root_id)
>>> +               ret = 1;
>>> +       else if (entry1->root_id < entry2->root_id)
>>> +               ret = -1;
>>> +       else
>>> +               ret = 0;
>>> +
>>> +       return is_descending ? -ret : ret;
>>> +}
>>> +
>>> +static int comp_entry_with_gen(struct root_info *entry1,
>>> +                              struct root_info *entry2,
>>> +                              int is_descending)
>>> +{
>>> +       int ret;
>>> +
>>> +       if (entry1->gen > entry2->gen)
>>> +               ret = 1;
>>> +       else if (entry1->gen < entry2->gen)
>>> +               ret = -1;
>>> +       else
>>> +               ret = 0;
>>> +
>>> +       return is_descending ? -ret : ret;
>>> +}
>>> +
>>> +static int comp_entry_with_ogen(struct root_info *entry1,
>>> +                               struct root_info *entry2,
>>> +                               int is_descending)
>>> +{
>>> +       int ret;
>>> +
>>> +       if (entry1->ogen > entry2->ogen)
>>> +               ret = 1;
>>> +       else if (entry1->ogen < entry2->ogen)
>>> +               ret = -1;
>>> +       else
>>> +               ret = 0;
>>> +
>>> +       return is_descending ? -ret : ret;
>>> +}
>>> +
>>> +static btrfs_list_comp_func all_comp_funcs[] = {
>>> +       [BTRFS_LIST_COMP_ROOTID]        = comp_entry_with_rootid,
>>> +       [BTRFS_LIST_COMP_OGEN]          = comp_entry_with_ogen,
>>> +       [BTRFS_LIST_COMP_GEN]           = comp_entry_with_gen,
>>> +};
>>> +
>>> +struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void)
>>> +{
>>> +       struct btrfs_list_comparer_set *set;
>>> +       int size;
>>> +
>>> +       size = sizeof(struct btrfs_list_comparer_set) +
>>> +              BTRFS_LIST_NCOMPS_INCREASE * sizeof(struct btrfs_list_comparer);
>>> +       set = malloc(size);
>>> +       if (!set) {
>>> +               fprintf(stderr, "memory allocation failed\n");
>>> +               exit(1);
>>> +       }
>>> +
>>> +       memset(set, 0, size);
>>> +       set->total = BTRFS_LIST_NCOMPS_INCREASE;
>>> +
>>> +       return set;
>>> +}
>>> +
>>> +void btrfs_list_free_comparer_set(struct btrfs_list_comparer_set *comp_set)
>>> +{
>>> +       free(comp_set);
>>> +}
>>> +
>>> +int btrfs_list_setup_comparer(struct btrfs_list_comparer_set  **comp_set,
>>> +                             enum btrfs_list_comp_enum comparer,
>>> +                             int is_descending)
>>> +{
>>> +       struct btrfs_list_comparer_set *set = *comp_set;
>>> +       int size;
>>> +
>>> +       BUG_ON(!set);
>>> +       BUG_ON(comparer >= BTRFS_LIST_COMP_MAX);
>>> +       BUG_ON(set->ncomps > set->total);
>>> +
>>> +       if (set->ncomps == set->total) {
>>> +               size = set->total + BTRFS_LIST_NCOMPS_INCREASE;
>>> +               size = sizeof(*set) + size * sizeof(struct btrfs_list_comparer);
>>> +               set = realloc(set, size);
>>> +               if (!set) {
>>> +                       fprintf(stderr, "memory allocation failed\n");
>>> +                       exit(1);
>>> +               }
>>> +
>>> +               memset(&set->comps[set->total], 0,
>>> +                      BTRFS_LIST_NCOMPS_INCREASE *
>>> +                      sizeof(struct btrfs_list_comparer));
>>> +               set->total += BTRFS_LIST_NCOMPS_INCREASE;
>>> +               *comp_set = set;
>>> +       }
>>> +
>>> +       BUG_ON(set->comps[set->ncomps].comp_func);
>>> +
>>> +       set->comps[set->ncomps].comp_func = all_comp_funcs[comparer];
>>> +       set->comps[set->ncomps].is_descending = is_descending;
>>> +       set->ncomps++;
>>>         return 0;
>>>  }
>>>
>>> -static int comp_entry_with_gen(struct root_info *entry, u64 root_id,
>>> -                              u64 ref_tree, u64 gen)
>>> +static int sort_comp(struct root_info *entry1, struct root_info *entry2,
>>> +                    struct btrfs_list_comparer_set *set)
>>>  {
>>> -       if (entry->gen < gen)
>>> -               return 1;
>>> -       if (entry->gen > gen)
>>> -               return -1;
>>> -       return comp_entry(entry, root_id, ref_tree);
>>> +       int rootid_compared = 0;
>>> +       int i, ret = 0;
>>> +
>>> +       if (!set || !set->ncomps)
>>> +               goto comp_rootid;
>>> +
>>> +       for (i = 0; i < set->ncomps; i++) {
>>> +               if (!set->comps[i].comp_func)
>>> +                       break;
>>> +
>>> +               ret = set->comps[i].comp_func(entry1, entry2,
>>> +                                             set->comps[i].is_descending);
>>> +               if (ret)
>>> +                       return ret;
>>> +
>>> +               if (set->comps[i].comp_func == comp_entry_with_rootid)
>>> +                       rootid_compared = 1;
>>> +       }
>>> +
>>> +       if (!rootid_compared) {
>>> +comp_rootid:
>>> +               ret = comp_entry_with_rootid(entry1, entry2, 0);
>>> +       }
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +static int sort_tree_insert(struct root_lookup *sort_tree,
>>> +                           struct root_info *ins,
>>> +                           struct btrfs_list_comparer_set *comp_set)
>>> +{
>>> +       struct rb_node **p = &sort_tree->root.rb_node;
>>> +       struct rb_node *parent = NULL;
>>> +       struct root_info *curr;
>>> +       int ret;
>>> +
>>> +       while (*p) {
>>> +               parent = *p;
>>> +               curr = rb_entry(parent, struct root_info, sort_node);
>>> +
>>> +               ret = sort_comp(ins, curr, comp_set);
>>> +               if (ret < 0)
>>> +                       p = &(*p)->rb_left;
>>> +               else if (ret > 0)
>>> +                       p = &(*p)->rb_right;
>>> +               else
>>> +                       return -EEXIST;
>>> +       }
>>> +
>>> +       rb_link_node(&ins->sort_node, parent, p);
>>> +       rb_insert_color(&ins->sort_node, &sort_tree->root);
>>> +       return 0;
>>>  }
>>>
>>>  /*
>>> @@ -109,118 +340,165 @@ static int comp_entry_with_gen(struct root_info *entry, u64 root_id,
>>>   * 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, u64 *gen, struct rb_node *node)
>>> +static int root_tree_insert(struct root_lookup *root_tree,
>>> +                           struct root_info *ins)
>>>  {
>>> -       struct rb_node ** p = &root->rb_node;
>>> +       struct rb_node **p = &root_tree->root.rb_node;
>>>         struct rb_node * parent = NULL;
>>> -       struct root_info *entry;
>>> -       int comp;
>>> +       struct root_info *curr;
>>> +       int ret;
>>>
>>>         while(*p) {
>>>                 parent = *p;
>>> -               entry = rb_entry(parent, struct root_info, rb_node);
>>> +               curr = rb_entry(parent, struct root_info, rb_node);
>>>
>>> -               if (!gen)
>>> -                       comp = comp_entry(entry, root_id, ref_tree);
>>> -               else
>>> -                       comp = comp_entry_with_gen(entry, root_id, ref_tree,
>>> -                                                  *gen);
>>> -
>>> -               if (comp < 0)
>>> +               ret = comp_entry_with_rootid(ins, curr, 0);
>>> +               if (ret < 0)
>>>                         p = &(*p)->rb_left;
>>> -               else if (comp > 0)
>>> +               else if (ret > 0)
>>>                         p = &(*p)->rb_right;
>>>                 else
>>> -                       return parent;
>>> +                       return -EEXIST;
>>>         }
>>>
>>> -       entry = rb_entry(parent, struct root_info, rb_node);
>>> -       rb_link_node(node, parent, p);
>>> -       rb_insert_color(node, root);
>>> -       return NULL;
>>> +       rb_link_node(&ins->rb_node, parent, p);
>>> +       rb_insert_color(&ins->rb_node, &root_tree->root);
>>> +       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 struct root_info *root_tree_search(struct root_lookup *root_tree,
>>> +                                         u64 root_id)
>>>  {
>>> -       struct rb_node * n = root->rb_node;
>>> +       struct rb_node *n = root_tree->root.rb_node;
>>>         struct root_info *entry;
>>> +       struct root_info tmp;
>>> +       int ret;
>>> +
>>> +       tmp.root_id = root_id;
>>>
>>>         while(n) {
>>>                 entry = rb_entry(n, struct root_info, rb_node);
>>>
>>> -               if (entry->root_id < root_id)
>>> +               ret = comp_entry_with_rootid(&tmp, entry, 0);
>>> +               if (ret < 0)
>>>                         n = n->rb_left;
>>> -               else if (entry->root_id > root_id)
>>> +               else if (ret > 0)
>>>                         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;
>>> -                       }
>>> +               else
>>>                         return entry;
>>> -               }
>>>         }
>>>         return NULL;
>>>  }
>>>
>>> +static int update_root(struct root_lookup *root_lookup,
>>> +                      u64 root_id, u64 ref_tree, u64 root_offset, u64 dir_id,
>>> +                      char *name, int name_len, u64 ogen, u64 gen, time_t ot,
>>> +                      void *uuid)
>>> +{
>>> +       struct root_info *ri;
>>> +
>>> +       ri = root_tree_search(root_lookup, root_id);
>>> +       if (!ri || ri->root_id != root_id)
>>> +               return -ENOENT;
>>> +       if (name && name_len > 0) {
>>> +               if (ri->name)
>>> +                       free(ri->name);
>>> +
>>> +               ri->name = malloc(name_len + 1);
>>> +               if (!ri->name) {
>>> +                       fprintf(stderr, "memory allocation failed\n");
>>> +                       exit(1);
>>> +               }
>>> +               strncpy(ri->name, name, name_len);
>>> +               ri->name[name_len] = 0;
>>> +       }
>>> +       if (ref_tree)
>>> +               ri->ref_tree = ref_tree;
>>> +       if (root_offset)
>>> +               ri->root_offset = root_offset;
>>> +       if (dir_id)
>>> +               ri->dir_id = dir_id;
>>> +       if (gen)
>>> +               ri->gen = gen;
>>> +       if (ogen)
>>> +               ri->ogen = ogen;
>>> +       if (!ri->ogen && root_offset)
>>> +               ri->ogen = root_offset;
>>> +       if (ot)
>>> +               ri->otime = ot;
>>> +       if (uuid)
>>> +               memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>>  /*
>>> - * 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
>>> + * add_root - update the existed root, or allocate a new root and insert it
>>> + *           into the lookup tree.
>>> + * root_id: object id of the root
>>> + * ref_tree: object id of the referring root.
>>> + * root_offset: offset value of the root'key
>>> + * dir_id: inode id of the directory in ref_tree where this root can be found.
>>> + * name: the name of root_id in that directory
>>> + * name_len: the length of name
>>> + * ogen: the original generation of the root
>>> + * gen: the current generation of the root
>>> + * ot: the original time(create time) of the root
>>> + * uuid: uuid of the root
>>>   */
>>>  static int add_root(struct root_lookup *root_lookup,
>>> -                   u64 root_id, u64 ref_tree, u64 dir_id, char *name,
>>> -                   int name_len, u64 *gen, time_t ot, void *uuid)
>>> +                   u64 root_id, u64 ref_tree, u64 root_offset, u64 dir_id,
>>> +                   char *name, int name_len, u64 ogen, u64 gen, time_t ot,
>>> +                   void *uuid)
>>>  {
>>>         struct root_info *ri;
>>> -       struct rb_node *ret;
>>> -       ri = malloc(sizeof(*ri) + name_len + 1);
>>> +       int ret;
>>> +
>>> +       ret = update_root(root_lookup, root_id, ref_tree, root_offset, dir_id,
>>> +                         name, name_len, ogen, gen, ot, uuid);
>>> +       if (!ret)
>>> +               return 0;
>>> +
>>> +       ri = malloc(sizeof(*ri));
>>>         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;
>>> +       memset(ri, 0, sizeof(*ri));
>>>         ri->root_id = root_id;
>>> -       ri->ref_tree = ref_tree;
>>> -       if (name)
>>> +
>>> +       if (name && name_len > 0) {
>>> +               ri->name = malloc(name_len + 1);
>>> +               if (!ri->name) {
>>> +                       fprintf(stderr, "memory allocation failed\n");
>>> +                       exit(1);
>>> +               }
>>>                 strncpy(ri->name, name, name_len);
>>> -       if (name_len > 0)
>>>                 ri->name[name_len] = 0;
>>> +       }
>>> +       if (ref_tree)
>>> +               ri->ref_tree = ref_tree;
>>> +       if (dir_id)
>>> +               ri->dir_id = dir_id;
>>> +       if (root_offset)
>>> +               ri->root_offset = root_offset;
>>>         if (gen)
>>> -               ri->gen = *gen;
>>> -       ri->otime = ot;
>>> +               ri->gen = gen;
>>> +       if (ogen)
>>> +               ri->ogen = ogen;
>>> +       if (!ri->ogen && root_offset)
>>> +               ri->ogen = root_offset;
>>> +       if (ot)
>>> +               ri->otime = ot;
>>>
>>>         if (uuid)
>>>                 memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
>>> -       else
>>> -               memset(&ri->uuid, 0, BTRFS_UUID_SIZE);
>>>
>>> -       ret = tree_insert(&root_lookup->root, root_id, ref_tree, gen,
>>> -                         &ri->rb_node);
>>> +       ret = root_tree_insert(root_lookup, ri);
>>>         if (ret) {
>>>                 printf("failed to insert tree %llu\n", (unsigned long long)root_id);
>>>                 exit(1);
>>> @@ -228,24 +506,33 @@ static int add_root(struct root_lookup *root_lookup,
>>>         return 0;
>>>  }
>>>
>>> -static int update_root(struct root_lookup *root_lookup, u64 root_id, u64 gen,
>>> -                       time_t ot, void *uuid)
>>> +void __free_root_info(struct root_info *ri)
>>>  {
>>> -       struct root_info *ri;
>>> +       if (ri->name)
>>> +               free(ri->name);
>>>
>>> -       ri = tree_search(&root_lookup->root, root_id);
>>> -       if (!ri || ri->root_id != root_id) {
>>> -               fprintf(stderr, "could not find subvol %llu\n", root_id);
>>> -               return -ENOENT;
>>> -       }
>>> -       ri->gen = gen;
>>> -       ri->otime = ot;
>>> -       if (uuid)
>>> -               memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
>>> -       else
>>> -               memset(&ri->uuid, 0, BTRFS_UUID_SIZE);
>>> +       if (ri->path)
>>> +               free(ri->path);
>>>
>>> -       return 0;
>>> +       if (ri->full_path)
>>> +               free(ri->full_path);
>>> +
>>> +       free(ri);
>>> +}
>>> +
>>> +void __free_all_subvolumn(struct root_lookup *root_tree)
>>> +{
>>> +       struct root_info *entry;
>>> +       struct rb_node *n;
>>> +
>>> +       n = rb_first(&root_tree->root);
>>> +       while (n) {
>>> +               entry = rb_entry(n, struct root_info, rb_node);
>>> +               rb_erase(n, &root_tree->root);
>>> +               __free_root_info(entry);
>>> +
>>> +               n = rb_first(&root_tree->root);
>>> +       }
>>>  }
>>>
>>>  /*
>>> @@ -255,8 +542,7 @@ static int update_root(struct root_lookup *root_lookup, u64 root_id, u64 gen,
>>>   * 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 *parent_id, u64 *top_id, char **path)
>>> +static int resolve_root(struct root_lookup *rl, struct root_info *ri)
>>>  {
>>>         char *full_path = NULL;
>>>         int len = 0;
>>> @@ -266,7 +552,6 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>>>          * we go backwards from the root_info object and add pathnames
>>>          * from parent directories as we go.
>>>          */
>>> -       *parent_id = 0;
>>>         found = ri;
>>>         while (1) {
>>>                 char *tmp;
>>> @@ -275,6 +560,10 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>>>
>>>                 /* room for / and for null */
>>>                 tmp = malloc(add_len + 2 + len);
>>> +               if (!tmp) {
>>> +                       perror("malloc failed");
>>> +                       exit(1);
>>> +               }
>>>                 if (full_path) {
>>>                         memcpy(tmp + add_len + 1, full_path, len);
>>>                         tmp[add_len] = '/';
>>> @@ -289,13 +578,10 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>>>                 }
>>>
>>>                 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;
>>> +                       ri->top_id = next;
>>>                         break;
>>>                 }
>>>
>>> @@ -303,14 +589,14 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>>>                  * if the ref_tree wasn't in our tree of roots, we're
>>>                  * at the top
>>>                  */
>>> -               found = tree_search(&rl->root, next);
>>> +               found = root_tree_search(rl, next);
>>>                 if (!found) {
>>> -                       *top_id = next;
>>> +                       ri->top_id = next;
>>>                         break;
>>>                 }
>>>         }
>>>
>>> -       *path = full_path;
>>> +       ri->full_path = full_path;
>>>
>>>         return 0;
>>>  }
>>> @@ -608,7 +894,7 @@ build:
>>>         return full;
>>>  }
>>>
>>> -static int get_default_subvolid(int fd, u64 *default_id)
>>> +int btrfs_list_get_default_subvolume(int fd, u64 *default_id)
>>>  {
>>>         struct btrfs_ioctl_search_args args;
>>>         struct btrfs_ioctl_search_key *sk = &args.key;
>>> @@ -674,10 +960,9 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>>>         int name_len;
>>>         char *name;
>>>         u64 dir_id;
>>> -       u8 type;
>>>         u64 gen = 0;
>>> +       u64 ogen;
>>>         int i;
>>> -       int get_gen = 0;
>>>         time_t t;
>>>         u8 uuid[BTRFS_UUID_SIZE];
>>>
>>> @@ -692,7 +977,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>>>          * only send back this type of key now.
>>>          */
>>>         sk->max_type = BTRFS_ROOT_BACKREF_KEY;
>>> -       sk->min_type = BTRFS_ROOT_BACKREF_KEY;
>>> +       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>>>
>>>         sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
>>>
>>> @@ -704,7 +989,6 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>>>         sk->max_offset = (u64)-1;
>>>         sk->max_transid = (u64)-1;
>>>
>>> -again:
>>>         /* just a big number, doesn't matter much */
>>>         sk->nr_items = 4096;
>>>
>>> @@ -726,28 +1010,32 @@ again:
>>>                         sh = (struct btrfs_ioctl_search_header *)(args.buf +
>>>                                                                   off);
>>>                         off += sizeof(*sh);
>>> -                       if (!get_gen && sh->type == BTRFS_ROOT_BACKREF_KEY) {
>>> +                       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, NULL, 0, NULL);
>>> -                       } else if (get_gen && sh->type == BTRFS_ROOT_ITEM_KEY) {
>>> +                                        0, dir_id, name, name_len, 0, 0, 0,
>>> +                                        NULL);
>>> +                       } else if (sh->type == BTRFS_ROOT_ITEM_KEY) {
>>>                                 ri = (struct btrfs_root_item *)(args.buf + off);
>>>                                 gen = btrfs_root_generation(ri);
>>>                                 if(sh->len >
>>>                                    sizeof(struct btrfs_root_item_v0)) {
>>>                                         t = ri->otime.sec;
>>> +                                       ogen = btrfs_root_otransid(ri);
>>>                                         memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
>>>                                 } else {
>>>                                         t = 0;
>>> +                                       ogen = 0;
>>>                                         memset(uuid, 0, BTRFS_UUID_SIZE);
>>>                                 }
>>>
>>> -                               update_root(root_lookup, sh->objectid, gen, t,
>>> -                                       uuid);
>>> +                               add_root(root_lookup, sh->objectid, 0,
>>> +                                        sh->offset, 0, NULL, 0, ogen, gen, t,
>>> +                                        uuid);
>>>                         }
>>>
>>>                         off += sh->len;
>>> @@ -761,129 +1049,139 @@ again:
>>>                         sk->min_offset = sh->offset;
>>>                 }
>>>                 sk->nr_items = 4096;
>>> -               /* this iteration is done, step forward one root for the next
>>> -                * ioctl
>>> -                */
>>> -               if (get_gen)
>>> -                       type = BTRFS_ROOT_ITEM_KEY;
>>> +               sk->min_offset++;
>>> +               if (!sk->min_offset)    /* overflow */
>>> +                       sk->min_type++;
>>>                 else
>>> -                       type = BTRFS_ROOT_BACKREF_KEY;
>>> +                       continue;
>>>
>>> -               if (sk->min_type < type) {
>>> -                       sk->min_type = type;
>>> -                       sk->min_offset = 0;
>>> -               } else  if (sk->min_objectid < BTRFS_LAST_FREE_OBJECTID) {
>>> +               if (sk->min_type > BTRFS_ROOT_BACKREF_KEY) {
>>> +                       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>>>                         sk->min_objectid++;
>>> -                       sk->min_type = type;
>>> -                       sk->min_offset = 0;
>>>                 } else
>>> +                       continue;
>>> +
>>> +               if (sk->min_objectid > sk->max_objectid)
>>>                         break;
>>>         }
>>>
>>> -       if (!get_gen) {
>>> -               memset(&args, 0, sizeof(args));
>>> +       return 0;
>>> +}
>>>
>>> -               sk->tree_id = 1;
>>> -               sk->max_type = BTRFS_ROOT_ITEM_KEY;
>>> -               sk->min_type = BTRFS_ROOT_ITEM_KEY;
>>> +static int filter_by_rootid(struct root_info *ri, void *arg)
>>> +{
>>> +       u64 default_root_id = *((u64 *)arg);
>>>
>>> -               sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
>>> +       return ri->root_id == default_root_id;
>>> +}
>>>
>>> -               sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
>>> -               sk->max_offset = (u64)-1;
>>> -               sk->max_transid = (u64)-1;
>>> +static int filter_snapshot(struct root_info *ri, void *arg)
>>> +{
>>> +       return !!ri->root_offset;
>>> +}
>>> +
>>> +static btrfs_list_filter_func all_filter_funcs[] = {
>>> +       [BTRFS_LIST_FILTER_ROOTID]              = filter_by_rootid,
>>> +       [BTRFS_LIST_FILTER_SNAPSHOT_ONLY]       = filter_snapshot,
>>> +};
>>>
>>> -               get_gen = 1;
>>> -               goto again;
>>> +struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void)
>>> +{
>>> +       struct btrfs_list_filter_set *set;
>>> +       int size;
>>> +
>>> +       size = sizeof(struct btrfs_list_filter_set) +
>>> +              BTRFS_LIST_NFILTERS_INCREASE * sizeof(struct btrfs_list_filter);
>>> +       set = malloc(size);
>>> +       if (!set) {
>>> +               fprintf(stderr, "memory allocation failed\n");
>>> +               exit(1);
>>>         }
>>> -       return 0;
>>> +
>>> +       memset(set, 0, size);
>>> +       set->total = BTRFS_LIST_NFILTERS_INCREASE;
>>> +
>>> +       return set;
>>>  }
>>>
>>> -static int __list_snapshot_search(int fd, struct root_lookup *root_lookup)
>>> +void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set)
>>>  {
>>> -       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 gen = 0;
>>> -       int i;
>>> -
>>> -       root_lookup_init(root_lookup);
>>> -       memset(&args, 0, sizeof(args));
>>> +       free(filter_set);
>>> +}
>>>
>>> -       sk->tree_id = 1;
>>> -       sk->max_type = BTRFS_ROOT_ITEM_KEY;
>>> -       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>>> -       sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
>>> -       sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
>>> -       sk->max_offset = (u64)-1;
>>> -       sk->max_transid = (u64)-1;
>>> -       sk->nr_items = 4096;
>>> +int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
>>> +                           enum btrfs_list_filter_enum filter, void *data)
>>> +{
>>> +       struct btrfs_list_filter_set *set = *filter_set;
>>> +       int size;
>>> +
>>> +       BUG_ON(!set);
>>> +       BUG_ON(filter >= BTRFS_LIST_FILTER_MAX);
>>> +       BUG_ON(set->nfilters > set->total);
>>> +
>>> +       if (set->nfilters == set->total) {
>>> +               size = set->total + BTRFS_LIST_NFILTERS_INCREASE;
>>> +               size = sizeof(*set) + size * sizeof(struct btrfs_list_filter);
>>> +               set = realloc(set, size);
>>> +               if (!set) {
>>> +                       fprintf(stderr, "memory allocation failed\n");
>>> +                       exit(1);
>>> +               }
>>>
>>> -       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;
>>> +               memset(&set->filters[set->total], 0,
>>> +                      BTRFS_LIST_NFILTERS_INCREASE *
>>> +                      sizeof(struct btrfs_list_filter));
>>> +               set->total += BTRFS_LIST_NFILTERS_INCREASE;
>>> +               *filter_set = set;
>>> +       }
>>>
>>> -               off = 0;
>>> +       BUG_ON(set->filters[set->nfilters].filter_func);
>>>
>>> -               /*
>>> -                * 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;
>>> -                       time_t  t;
>>> -                       u8 uuid[BTRFS_UUID_SIZE];
>>> +       set->filters[set->nfilters].filter_func = all_filter_funcs[filter];
>>> +       set->filters[set->nfilters].data = data;
>>> +       set->nfilters++;
>>> +       return 0;
>>> +}
>>>
>>> -                       sh = (struct btrfs_ioctl_search_header *)(args.buf +
>>> -                                                                 off);
>>> -                       off += sizeof(*sh);
>>> -                       if (sh->type == BTRFS_ROOT_ITEM_KEY && sh->offset) {
>>> -                               item = (struct btrfs_root_item *)(args.buf + off);
>>> -                               if(sh->len >
>>> -                                  sizeof(struct btrfs_root_item_v0)) {
>>> -                                       t = item->otime.sec;
>>> -                                       memcpy(uuid, item->uuid,
>>> -                                              BTRFS_UUID_SIZE);
>>> -                               } else {
>>> -                                       t = 0;
>>> -                                       memset(uuid, 0, BTRFS_UUID_SIZE);
>>> -                               }
>>> -                               gen = sh->offset;
>>> +static int filter_root(struct root_info *ri,
>>> +                      struct btrfs_list_filter_set *set)
>>> +{
>>> +       int i, ret;
>>>
>>> -                               add_root(root_lookup, sh->objectid, 0,
>>> -                                        0, NULL, 0, &gen, t, uuid);
>>> -                       }
>>> -                       off += sh->len;
>>> +       if (!set || !set->nfilters)
>>> +               return 1;
>>>
>>> -                       /*
>>> -                        * 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_ITEM_KEY) {
>>> -                       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>>> -                       sk->min_offset = 0;
>>> -               } else  if (sk->min_objectid < BTRFS_LAST_FREE_OBJECTID) {
>>> -                       sk->min_objectid++;
>>> -                       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>>> -                       sk->min_offset = 0;
>>> -               } else
>>> +       for (i = 0; i < set->nfilters; i++) {
>>> +               if (!set->filters[i].filter_func)
>>>                         break;
>>> +               ret = set->filters[i].filter_func(ri, set->filters[i].data);
>>> +               if (!ret)
>>> +                       return 0;
>>> +       }
>>> +       return 1;
>>> +}
>>> +
>>> +static void __filter_and_sort_subvol(struct root_lookup *all_subvols,
>>> +                                   struct root_lookup *sort_tree,
>>> +                                   struct btrfs_list_filter_set *filter_set,
>>> +                                   struct btrfs_list_comparer_set *comp_set)
>>> +{
>>> +       struct rb_node *n;
>>> +       struct root_info *entry;
>>> +       int ret;
>>> +
>>> +       root_lookup_init(sort_tree);
>>> +
>>> +       n = rb_last(&all_subvols->root);
>>> +       while (n) {
>>> +               entry = rb_entry(n, struct root_info, rb_node);
>>> +
>>> +               resolve_root(all_subvols, entry);
>>> +               ret = filter_root(entry, filter_set);
>>> +               if (ret)
>>> +                       sort_tree_insert(sort_tree, entry, comp_set);
>>> +               n = rb_prev(n);
>>>         }
>>> -       return 0;
>>>  }
>>>
>>>  static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
>>> @@ -904,128 +1202,91 @@ static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
>>>         return 0;
>>>  }
>>>
>>> -int list_subvols(int fd, int print_parent, int get_default, int print_uuid)
>>> +static void print_subvolume_column(struct root_info *subv,
>>> +                                  enum btrfs_list_column_enum column)
>>>  {
>>> -       struct root_lookup root_lookup;
>>> -       struct rb_node *n;
>>> -       u64 default_id;
>>> -       int ret;
>>> +       char tstr[256];
>>>         char uuidparse[37];
>>>
>>> -       if (get_default) {
>>> -               ret = get_default_subvolid(fd, &default_id);
>>> -               if (ret) {
>>> -                       fprintf(stderr, "ERROR: can't perform the search - %s\n",
>>> -                               strerror(errno));
>>> -                       return ret;
>>> -               }
>>> -               if (default_id == 0) {
>>> -                       fprintf(stderr, "ERROR: 'default' dir item not found\n");
>>> -                       return ret;
>>> -               }
>>> -
>>> -               /* no need to resolve roots if FS_TREE is default */
>>> -               if (default_id == BTRFS_FS_TREE_OBJECTID) {
>>> -                       printf("ID 5 (FS_TREE)\n");
>>> -                       return ret;
>>> -               }
>>> -       }
>>> -
>>> -       ret = __list_subvol_search(fd, &root_lookup);
>>> -       if (ret) {
>>> -               fprintf(stderr, "ERROR: can't perform the search - %s\n",
>>> -                               strerror(errno));
>>> -               return ret;
>>> +       BUG_ON(column >= BTRFS_LIST_ALL || column < 0);
>>> +
>>> +       switch (column) {
>>> +       case BTRFS_LIST_OBJECTID:
>>> +               printf("%llu", subv->root_id);
>>> +               break;
>>> +       case BTRFS_LIST_GENERATION:
>>> +               printf("%llu", subv->gen);
>>> +               break;
>>> +       case BTRFS_LIST_OGENERATION:
>>> +               printf("%llu", subv->ogen);
>>> +               break;
>>> +       case BTRFS_LIST_PARENT:
>>> +               printf("%llu", subv->ref_tree);
>>> +               break;
>>> +       case BTRFS_LIST_TOP_LEVEL:
>>> +               printf("%llu", subv->top_id);
>>> +               break;
>>> +       case BTRFS_LIST_OTIME:
>>> +               if (subv->otime)
>>> +                       strftime(tstr, 256, "%Y-%m-%d %X",
>>> +                                localtime(&subv->otime));
>>> +               else
>>> +                       strcpy(tstr, "-");
>>> +               printf("%s", tstr);
>>> +               break;
>>> +       case BTRFS_LIST_UUID:
>>> +               if (uuid_is_null(subv->uuid))
>>> +                       strcpy(uuidparse, "-");
>>> +               else
>>> +                       uuid_unparse(subv->uuid, uuidparse);
>>> +               printf("%s", uuidparse);
>>> +               break;
>>> +       case BTRFS_LIST_PATH:
>>> +               BUG_ON(!subv->full_path);
>>> +               printf("%s", subv->full_path);
>>> +               break;
>>> +       default:
>>> +               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.
>>> -        */
>>> -       ret = __list_subvol_fill_paths(fd, &root_lookup);
>>> -       if (ret < 0)
>>> -               return ret;
>>> -
>>> -       /* now that we have all the subvol-relative paths filled in,
>>> -        * we have to string the subvols together so that we can get
>>> -        * a path all the way back to the FS root
>>> -        */
>>> -       n = rb_last(&root_lookup.root);
>>> -       while (n) {
>>> -               struct root_info *entry;
>>> -               u64 level;
>>> -               u64 parent_id;
>>> -               char *path;
>>> +static void print_single_volume_info_default(struct root_info *subv)
>>> +{
>>> +       int i;
>>>
>>> -               entry = rb_entry(n, struct root_info, rb_node);
>>> -               if (get_default && entry->root_id != default_id) {
>>> -                       n = rb_prev(n);
>>> +       for (i = 0; i < BTRFS_LIST_ALL; i++) {
>>> +               if (!btrfs_list_columns[i].need_print)
>>>                         continue;
>>> -               }
>>>
>>> -               resolve_root(&root_lookup, entry, &parent_id, &level, &path);
>>> -               if (print_parent) {
>>> -                       if (print_uuid) {
>>> -                               if (uuid_is_null(entry->uuid))
>>> -                                       strcpy(uuidparse, "-");
>>> -                               else
>>> -                                       uuid_unparse(entry->uuid, uuidparse);
>>> -                               printf("ID %llu gen %llu parent %llu top level %llu"
>>> -                                       " uuid %s path %s\n",
>>> -                                       (unsigned long long)entry->root_id,
>>> -                                       (unsigned long long)entry->gen,
>>> -                                       (unsigned long long)parent_id,
>>> -                                       (unsigned long long)level,
>>> -                                       uuidparse, path);
>>> -                       } else {
>>> -                               printf("ID %llu gen %llu parent %llu top level"
>>> -                                       " %llu path %s\n",
>>> -                                       (unsigned long long)entry->root_id,
>>> -                                       (unsigned long long)entry->gen,
>>> -                                       (unsigned long long)parent_id,
>>> -                                       (unsigned long long)level, path);
>>> -                       }
>>> -               } else {
>>> -                       if (print_uuid) {
>>> -                               if (uuid_is_null(entry->uuid))
>>> -                                       strcpy(uuidparse, "-");
>>> -                               else
>>> -                                       uuid_unparse(entry->uuid, uuidparse);
>>> -                               printf("ID %llu gen %llu top level %llu"
>>> -                                       " uuid %s path %s\n",
>>> -                                       (unsigned long long)entry->root_id,
>>> -                                       (unsigned long long)entry->gen,
>>> -                                       (unsigned long long)level,
>>> -                                       uuidparse, path);
>>> -                       } else {
>>> -                               printf("ID %llu gen %llu top level %llu path %s\n",
>>> -                                       (unsigned long long)entry->root_id,
>>> -                                       (unsigned long long)entry->gen,
>>> -                                       (unsigned long long)level, path);
>>> -                       }
>>> -               }
>>> +               printf("%s ", btrfs_list_columns[i].name);
>>> +               print_subvolume_column(subv, i);
>>>
>>> -               free(path);
>>> -               n = rb_prev(n);
>>> +               if (i != BTRFS_LIST_PATH)
>>> +                       printf(" ");
>>>         }
>>> +       printf("\n");
>>> +}
>>>
>>> -       return ret;
>>> +static void print_all_volume_info_default(struct root_lookup *sorted_tree)
>>> +{
>>> +       struct rb_node *n;
>>> +       struct root_info *entry;
>>> +
>>> +       n = rb_first(&sorted_tree->root);
>>> +       while (n) {
>>> +               entry = rb_entry(n, struct root_info, sort_node);
>>> +               print_single_volume_info_default(entry);
>>> +               n = rb_next(n);
>>> +       }
>>>  }
>>>
>>> -int list_snapshots(int fd, int print_parent, int order, int print_uuid)
>>> +int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
>>> +                      struct btrfs_list_comparer_set *comp_set)
>>>  {
>>>         struct root_lookup root_lookup;
>>> -       struct root_lookup root_lookup_snap;
>>> -       struct rb_node *n;
>>> +       struct root_lookup root_sort;
>>>         int ret;
>>>
>>> -       ret = __list_snapshot_search(fd, &root_lookup_snap);
>>> -       if (ret) {
>>> -               fprintf(stderr, "ERROR: can't perform the search - %s\n",
>>> -                               strerror(errno));
>>> -               return ret;
>>> -       }
>>> -
>>>         ret = __list_subvol_search(fd, &root_lookup);
>>>         if (ret) {
>>>                 fprintf(stderr, "ERROR: can't perform the search - %s\n",
>>> @@ -1041,86 +1302,11 @@ int list_snapshots(int fd, int print_parent, int order, int print_uuid)
>>>         if (ret < 0)
>>>                 return ret;
>>>
>>> -       /* now that we have all the subvol-relative paths filled in,
>>> -        * we have to string the subvols together so that we can get
>>> -        * a path all the way back to the FS root
>>> -        */
>>> -       if (!order)
>>> -               n = rb_last(&root_lookup_snap.root);
>>> -       else
>>> -               n = rb_first(&root_lookup_snap.root);
>>> -       while (n) {
>>> -               struct root_info *entry_snap;
>>> -               struct root_info *entry;
>>> -               u64 level;
>>> -               u64 parent_id;
>>> -               char *path;
>>> -               time_t t;
>>> -               char tstr[256];
>>> -               char uuidparse[37];
>>> -
>>> -               entry_snap = rb_entry(n, struct root_info, rb_node);
>>> -               entry = tree_search(&root_lookup.root, entry_snap->root_id);
>>> -
>>> -               resolve_root(&root_lookup, entry, &parent_id, &level, &path);
>>> -               t = entry->otime;
>>> -               if(t)
>>> -                       strftime(tstr,256,"%Y-%m-%d %X",localtime(&t));
>>> -               else
>>> -                       strcpy(tstr,"-");
>>> -               if (print_parent) {
>>> -                       if (print_uuid) {
>>> -                               if (uuid_is_null(entry->uuid))
>>> -                                       strcpy(uuidparse, "-");
>>> -                               else
>>> -                                       uuid_unparse(entry->uuid, uuidparse);
>>> -                               printf("ID %llu gen %llu cgen %llu parent %llu"
>>> -                                       " top level %llu otime %s uuid %s path %s\n",
>>> -                                       (unsigned long long)entry->root_id,
>>> -                                       (unsigned long long)entry->gen,
>>> -                                       (unsigned long long)entry_snap->gen,
>>> -                                       (unsigned long long)parent_id,
>>> -                                       (unsigned long long)level,
>>> -                                       tstr, uuidparse, path);
>>> -                       } else {
>>> -                               printf("ID %llu gen %llu cgen %llu parent %llu"
>>> -                                       " top level %llu otime %s path %s\n",
>>> -                                       (unsigned long long)entry->root_id,
>>> -                                       (unsigned long long)entry->gen,
>>> -                                       (unsigned long long)entry_snap->gen,
>>> -                                       (unsigned long long)parent_id,
>>> -                                       (unsigned long long)level, tstr, path);
>>> -                       }
>>> -               } else {
>>> -                       if (print_uuid) {
>>> -                               if (uuid_is_null(entry->uuid))
>>> -                                       strcpy(uuidparse, "-");
>>> -                               else
>>> -                                       uuid_unparse(entry->uuid, uuidparse);
>>> -                               printf("ID %llu gen %llu cgen %llu top level %llu "
>>> -                                       "otime %s uuid %s path %s\n",
>>> -                                       (unsigned long long)entry->root_id,
>>> -                                       (unsigned long long)entry->gen,
>>> -                                       (unsigned long long)entry_snap->gen,
>>> -                                       (unsigned long long)level,
>>> -                                       tstr, uuidparse, path);
>>> -                       } else {
>>> -                               printf("ID %llu gen %llu cgen %llu top level %llu "
>>> -                                       "otime %s path %s\n",
>>> -                                       (unsigned long long)entry->root_id,
>>> -                                       (unsigned long long)entry->gen,
>>> -                                       (unsigned long long)entry_snap->gen,
>>> -                                       (unsigned long long)level, tstr, path);
>>> -                       }
>>> -               }
>>> -
>>> -               free(path);
>>> -               if (!order)
>>> -                       n = rb_prev(n);
>>> -               else
>>> -                       n = rb_next(n);
>>> -       }
>>> +       __filter_and_sort_subvol(&root_lookup, &root_sort, filter_set,
>>> +                                comp_set);
>>>
>>> +       print_all_volume_info_default(&root_sort);
>>> +       __free_all_subvolumn(&root_lookup);
>>>         return ret;
>>>  }
>>>
>>> @@ -1203,7 +1389,7 @@ static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh,
>>>         return 0;
>>>  }
>>>
>>> -int find_updated_files(int fd, u64 root_id, u64 oldest_gen)
>>> +int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen)
>>>  {
>>>         int ret;
>>>         struct btrfs_ioctl_search_args args;
>>> @@ -1304,7 +1490,7 @@ int find_updated_files(int fd, u64 root_id, u64 oldest_gen)
>>>         return ret;
>>>  }
>>>
>>> -char *path_for_root(int fd, u64 root)
>>> +char *btrfs_list_path_for_root(int fd, u64 root)
>>>  {
>>>         struct root_lookup root_lookup;
>>>         struct rb_node *n;
>>> @@ -1322,19 +1508,17 @@ char *path_for_root(int fd, u64 root)
>>>         n = rb_last(&root_lookup.root);
>>>         while (n) {
>>>                 struct root_info *entry;
>>> -               u64 parent_id;
>>> -               u64 level;
>>> -               char *path;
>>>
>>>                 entry = rb_entry(n, struct root_info, rb_node);
>>> -               resolve_root(&root_lookup, entry, &parent_id, &level, &path);
>>> -               if (entry->root_id == root)
>>> -                       ret_path = path;
>>> -               else
>>> -                       free(path);
>>> +               resolve_root(&root_lookup, entry);
>>> +               if (entry->root_id == root) {
>>> +                       ret_path = entry->full_path;
>>> +                       entry->full_path = NULL;
>>> +               }
>>>
>>>                 n = rb_prev(n);
>>>         }
>>> +       __free_all_subvolumn(&root_lookup);
>>>
>>>         return ret_path;
>>>  }
>>> diff --git a/btrfs-list.h b/btrfs-list.h
>>> index b4a7f30..ca3eb7b 100644
>>> --- a/btrfs-list.h
>>> +++ b/btrfs-list.h
>>> @@ -16,7 +16,72 @@
>>>   * Boston, MA 021110-1307, USA.
>>>   */
>>>
>>> -int list_subvols(int fd, int print_parent, int get_default, int print_uuid);
>>> -int list_snapshots(int fd, int print_parent, int order, int print_uuid);
>>> -int find_updated_files(int fd, u64 root_id, u64 oldest_gen);
>>> -char *path_for_root(int fd, u64 root);
>>> +struct root_info;
>>> +
>>> +typedef int (*btrfs_list_filter_func)(struct root_info *, void *);
>>> +typedef int (*btrfs_list_comp_func)(struct root_info *, struct root_info *,
>>> +                                   int);
>>> +
>>> +struct btrfs_list_filter {
>>> +       btrfs_list_filter_func filter_func;
>>> +       void *data;
>>> +};
>>> +
>>> +struct btrfs_list_comparer {
>>> +       btrfs_list_comp_func comp_func;
>>> +       int is_descending;
>>> +};
>>> +
>>> +struct btrfs_list_filter_set {
>>> +       int total;
>>> +       int nfilters;
>>> +       struct btrfs_list_filter filters[0];
>>> +};
>>> +
>>> +struct btrfs_list_comparer_set {
>>> +       int total;
>>> +       int ncomps;
>>> +       struct btrfs_list_comparer comps[0];
>>> +};
>>> +
>>> +enum btrfs_list_column_enum {
>>> +       BTRFS_LIST_OBJECTID,
>>> +       BTRFS_LIST_GENERATION,
>>> +       BTRFS_LIST_OGENERATION,
>>> +       BTRFS_LIST_PARENT,
>>> +       BTRFS_LIST_TOP_LEVEL,
>>> +       BTRFS_LIST_OTIME,
>>> +       BTRFS_LIST_UUID,
>>> +       BTRFS_LIST_PATH,
>>> +       BTRFS_LIST_ALL,
>>> +};
>>> +
>>> +enum btrfs_list_filter_enum {
>>> +       BTRFS_LIST_FILTER_ROOTID,
>>> +       BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
>>> +       BTRFS_LIST_FILTER_MAX,
>>> +};
>>> +
>>> +enum btrfs_list_comp_enum {
>>> +       BTRFS_LIST_COMP_ROOTID,
>>> +       BTRFS_LIST_COMP_OGEN,
>>> +       BTRFS_LIST_COMP_GEN,
>>> +       BTRFS_LIST_COMP_MAX,
>>> +};
>>> +
>>> +void btrfs_list_setup_print_column(enum btrfs_list_column_enum column);
>>> +struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void);
>>> +void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set);
>>> +int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
>>> +                           enum btrfs_list_filter_enum filter, void *data);
>>> +struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void);
>>> +void btrfs_list_free_comparer_set(struct btrfs_list_comparer_set *comp_set);
>>> +int btrfs_list_setup_comparer(struct btrfs_list_comparer_set **comp_set,
>>> +                             enum btrfs_list_comp_enum comparer,
>>> +                             int is_descending);
>>> +
>>> +int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
>>> +                      struct btrfs_list_comparer_set *comp_set);
>>> +int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen);
>>> +int btrfs_list_get_default_subvolume(int fd, u64 *default_id);
>>> +char *btrfs_list_path_for_root(int fd, u64 root);
>>> diff --git a/cmds-inspect.c b/cmds-inspect.c
>>> index f943ed9..376fab2 100644
>>> --- a/cmds-inspect.c
>>> +++ b/cmds-inspect.c
>>> @@ -194,7 +194,7 @@ static int cmd_logical_resolve(int argc, char **argv)
>>>                 char *name;
>>>
>>>                 if (getpath) {
>>> -                       name = path_for_root(fd, root);
>>> +                       name = btrfs_list_path_for_root(fd, root);
>>>                         if (IS_ERR(name))
>>>                                 return PTR_ERR(name);
>>>                         if (!name) {
>>> diff --git a/cmds-subvolume.c b/cmds-subvolume.c
>>> index cd4b5a7..b1cf2bd 100644
>>> --- a/cmds-subvolume.c
>>> +++ b/cmds-subvolume.c
>>> @@ -28,6 +28,7 @@
>>>  #include "ioctl.h"
>>>  #include "qgroup.h"
>>>
>>> +#include "ctree.h"
>>>  #include "commands.h"
>>>  #include "btrfs-list.h"
>>>
>>> @@ -270,13 +271,15 @@ static const char * const cmd_subvol_list_usage[] = {
>>>
>>>  static int cmd_subvol_list(int argc, char **argv)
>>>  {
>>> +       struct btrfs_list_filter_set *filter_set;
>>> +       struct btrfs_list_comparer_set *comparer_set;
>>>         int fd;
>>>         int ret;
>>> -       int print_parent = 0;
>>> -       int print_snap_only = 0;
>>> -       int order = 0;
>>> +       int order;
>>>         char *subvol;
>>> -       int print_uuid = 0;
>>> +
>>> +       filter_set = btrfs_list_alloc_filter_set();
>>> +       comparer_set = btrfs_list_alloc_comparer_set();
>>>
>>>         optind = 1;
>>>         while(1) {
>>> @@ -286,14 +289,21 @@ static int cmd_subvol_list(int argc, char **argv)
>>>
>>>                 switch(c) {
>>>                 case 'p':
>>> -                       print_parent = 1;
>>> +                       btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
>>>                         break;
>>>                 case 's':
>>> -                       print_snap_only = 1;
>>>                         order = atoi(optarg);
>>> +                       btrfs_list_setup_filter(&filter_set,
>>> +                                               BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
>>> +                                               NULL);
>>> +                       btrfs_list_setup_comparer(&comparer_set,
>>> +                                                 BTRFS_LIST_COMP_OGEN,
>>> +                                                 !order);
>>> +                       btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
>>> +                       btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
>>>                         break;
>>>                 case 'u':
>>> -                       print_uuid =1;
>>> +                       btrfs_list_setup_print_column(BTRFS_LIST_UUID);
>>>                         break;
>>>                 default:
>>>                         usage(cmd_subvol_list_usage);
>>> @@ -320,10 +330,8 @@ static int cmd_subvol_list(int argc, char **argv)
>>>                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
>>>                 return 12;
>>>         }
>>> -       if (!print_snap_only)
>>> -               ret = list_subvols(fd, print_parent, 0, print_uuid);
>>> -       else
>>> -               ret = list_snapshots(fd, print_parent, order, print_uuid);
>>> +
>>> +       ret = btrfs_list_subvols(fd, filter_set, comparer_set);
>>>         if (ret)
>>>                 return 19;
>>>         return 0;
>>> @@ -483,6 +491,8 @@ static int cmd_subvol_get_default(int argc, char **argv)
>>>         int fd;
>>>         int ret;
>>>         char *subvol;
>>> +       struct btrfs_list_filter_set *filter_set;
>>> +       u64 default_id;
>>>
>>>         if (check_argc_exact(argc, 2))
>>>                 usage(cmd_subvol_get_default_usage);
>>> @@ -504,7 +514,30 @@ static int cmd_subvol_get_default(int argc, char **argv)
>>>                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
>>>                 return 12;
>>>         }
>>> -       ret = list_subvols(fd, 0, 1, 0);
>>> +
>>> +       ret = btrfs_list_get_default_subvolume(fd, &default_id);
>>> +       if (ret) {
>>> +               fprintf(stderr, "ERROR: can't perform the search - %s\n",
>>> +                       strerror(errno));
>>> +               return ret;
>>> +       }
>>> +
>>> +       if (default_id == 0) {
>>> +               fprintf(stderr, "ERROR: 'default' dir item not found\n");
>>> +               return ret;
>>> +       }
>>> +
>>> +       /* no need to resolve roots if FS_TREE is default */
>>> +       if (default_id == BTRFS_FS_TREE_OBJECTID) {
>>> +               printf("ID 5 (FS_TREE)\n");
>>> +               return ret;
>>> +       }
>>> +
>>> +       filter_set = btrfs_list_alloc_filter_set();
>>> +       btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
>>> +                               (void *)&default_id);
>>> +
>>> +       ret = btrfs_list_subvols(fd, filter_set, NULL);
>>>         if (ret)
>>>                 return 19;
>>>         return 0;
>>> @@ -585,7 +618,7 @@ static int cmd_find_new(int argc, char **argv)
>>>                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
>>>                 return 12;
>>>         }
>>> -       ret = find_updated_files(fd, 0, last_gen);
>>> +       ret = btrfs_list_find_updated_files(fd, 0, last_gen);
>>>         if (ret)
>>>                 return 19;
>>>         return 0;
>>> diff --git a/send-utils.c b/send-utils.c
>>> index 096fa02..fcde5c2 100644
>>> --- a/send-utils.c
>>> +++ b/send-utils.c
>>> @@ -244,7 +244,8 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
>>>                                 if (!root_item_valid)
>>>                                         goto skip;
>>>
>>> -                               path = path_for_root(mnt_fd, sh->objectid);
>>> +                               path = btrfs_list_path_for_root(mnt_fd,
>>> +                                                               sh->objectid);
>>>                                 if (!path)
>>>                                         path = strdup("");
>>>                                 if (IS_ERR(path)) {
>>> --
>>> 1.7.6.5
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
>>> the body of a message to majordomo@vger.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>
>

Alex.

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH V4 5/7] Btrfs-progs: restructure list_subvolumes
  2012-10-10 19:45       ` Alex Lyakas
@ 2012-10-15  4:06         ` Miao Xie
  0 siblings, 0 replies; 31+ messages in thread
From: Miao Xie @ 2012-10-15  4:06 UTC (permalink / raw)
  To: Alex Lyakas; +Cc: linux-btrfs

On Wed, 10 Oct 2012 21:45:31 +0200, Alex Lyakas wrote:
>>> The problem that I see is after subvolume deletion. What happens is
>>> that __list_subvol_search() is called at the point, at which ROOT_ITEM
>>> still exists in the root tree, but ROOT_BACKREF does not exist
>>> anymore. I think this is because btrfs_ioctl_snap_destroy() calls
>>> btrfs_unlink_subvol(), which calls btrfs_del_root_ref(), which deletes
>>> ROOT_BACKREF, but ROOT_ITEM still exists until transaction is
>>> committed.
>>>
>>> So what happens is that we end up with struct root_info that has
>>> ref_tree==0 (and dir_id==0).
>>> So later, when __list_subvol_fill_paths() is called, it fails to
>>> perform BTRFS_IOC_INO_LOOKUP because of ref_tree==0. As a result,
>>> subvol_uuid_search_init fails and everything stops.
>>>
>>> Previously, before your change, root_info was not added until we see
>>> ROOT_BACKREF in the tree.
>>
>> The old code still has the similar problem if we delete the subvolume completely
>> between __list_subvol_search() and __list_subvol_fill_paths().
>>
>>> How do you think this should be fixed?
>>
>> Yes, I think it should be fixed. We can filter the deleted subvolume.
> 
> Something like this?
> 
> diff --git a/btrfs-list.c b/btrfs-list.c
> index e5f0f96..5fd262c 100644
> --- a/btrfs-list.c
> +++ b/btrfs-list.c
> @@ -1004,6 +1004,23 @@ out:
>         return 0;
>  }
> 
> +static void __drop_deleting_roots(struct root_lookup *root_lookup)
> +{
> +       struct rb_node *n;
> +
> +again:
> +       n = rb_first(&root_lookup->root);
> +       while (n) {
> +               struct root_info *entry = rb_entry(n, struct
> root_info, rb_node);
> +               if (!entry->ref_tree) {
> +                       rb_erase(n, &root_lookup->root);
> +                       free(entry);
> +                       goto again;
> +               }
> +               n = rb_next(n);
> +       }
> +}

It is better that we do it in lookup_ino_path(), or we can not avoid the problem
that I said above.(delete the subvolume completely between __list_subvol_search() and
__list_subvol_fill_paths().)

Thanks
Miao

>  static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>  {
>         int ret;
> @@ -1123,6 +1140,8 @@ static int __list_subvol_search(int fd, struct
> root_lookup *root_lookup)
>                         break;
>         }
> 
> +       __drop_deleting_roots(root_lookup);
> +
>         return 0;
>  }
> 
> 
> 
> 
> 
> 
> 
> 
>>
>>>
>>> Also, is there a way to issue a subvolume deletion and make sure that
>>> it was deleted? I see that btrfs_ioctl_snap_destroy() calls
>>
>> no ROOT_BACKREF already tells us that the subvolume has been deleted, I think.
>>
>>> end_transaction() but not commit_transaction(). Moreover, there is no
>>> way for the caller to know the transid, otherwise we could issue
>>> BTRFS_IOC_WAIT_SYNC.
>>
>> we can get the transid by btrfs_ioctl_start_sync()
>>
>> Thanks
>> Miao
>>
>>>
>>> Thanks,
>>> Alex.
>>>
>>>
>>>
>>> On Tue, Sep 18, 2012 at 1:06 PM, Miao Xie <miaox@cn.fujitsu.com> wrote:
>>>> The current code of list_subvols() has very bad scalability, if we want to
>>>> add new filter conditions or new sort methods, we have to modify lots of code.
>>>>
>>>> Beside that, the most code of list_snapshots() is similar to list_subvols(),
>>>>
>>>> So I restructure list_subvols(), and split the subvolume filter function,
>>>> the subvolume sort function and the output function from list_subvols().
>>>> In order to implement it, we defined some importtant structures:
>>>> struct btrfs_list_filter {
>>>>         btrfs_list_filter_func filter_func;
>>>>         void *data;
>>>> };
>>>>
>>>> struct btrfs_list_comparer {
>>>>         btrfs_list_comp_func comp_func;
>>>>         int is_descending;
>>>> };
>>>>
>>>> struct {
>>>>         char    *name;
>>>>         char    *column_name;
>>>>         int     need_print;
>>>> } btrfs_list_columns[];
>>>>
>>>> If we want to add a new filter condition, we can choose a suitable filter
>>>> function, or implement a new filter function[1], and add it into a set of
>>>> the filters, and then pass the filter set into list_subvols(). We also can
>>>> mix several filters (just add those filters into the set, and pass the set
>>>> into list_subvols()) if the users specify two or more filter conditions.
>>>>
>>>> The subvolume sort function is similar to the subvolume filter function. The
>>>> differentiation is the order of comparers in the array which is passed into
>>>> list_subvols() show us the priority of the sort methods.
>>>>
>>>> The output function is different with the above two functions, we define a
>>>> array to manage all the columns that can be outputed, and use a member variant
>>>> (->need_print) to control the output of the relative column. Some columns are
>>>> outputed by default. But we can change it according to the requirement of the
>>>> users.
>>>>
>>>> After appling this patch, we needn't implement a independent list_snapshots()
>>>> function, just pass a filter function which is used to identify the snapshot
>>>> into list_subvols().
>>>>
>>>> [1]: If we implement new filter functions or compare functions, we must add
>>>> them into the array all_filter_funcs or the array all_comp_funcs, and modify
>>>> the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
>>>>
>>>> Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
>>>> ---
>>>> Changelog v3 -> v4:
>>>> - add the filter set and comparer set which are used to manage the filters and
>>>>   comparers. And the memory space of these two set are allocated dynamically,
>>>>   in this way, the users can specify lots of filters and comparers, not be limited
>>>>   by the size of the array.
>>>>
>>>> Changelog v1 -> v3:
>>>> - new patch.
>>>> ---
>>>>  btrfs-list.c     | 1004 ++++++++++++++++++++++++++++++++----------------------
>>>>  btrfs-list.h     |   73 ++++-
>>>>  cmds-inspect.c   |    2 +-
>>>>  cmds-subvolume.c |   59 +++-
>>>>  send-utils.c     |    3 +-
>>>>  5 files changed, 712 insertions(+), 429 deletions(-)
>>>>
>>>> diff --git a/btrfs-list.c b/btrfs-list.c
>>>> index ed28021..bace903 100644
>>>> --- a/btrfs-list.c
>>>> +++ b/btrfs-list.c
>>>> @@ -37,6 +37,9 @@
>>>>  #include <uuid/uuid.h>
>>>>  #include "btrfs-list.h"
>>>>
>>>> +#define BTRFS_LIST_NFILTERS_INCREASE   (2 * BTRFS_LIST_FILTER_MAX)
>>>> +#define BTRFS_LIST_NCOMPS_INCREASE     (2 * BTRFS_LIST_COMP_MAX)
>>>> +
>>>>  /* we store all the roots we find in an rbtree so that we can
>>>>   * search for them later.
>>>>   */
>>>> @@ -49,19 +52,28 @@ struct root_lookup {
>>>>   */
>>>>  struct root_info {
>>>>         struct rb_node rb_node;
>>>> +       struct rb_node sort_node;
>>>>
>>>>         /* this root's id */
>>>>         u64 root_id;
>>>>
>>>> +       /* equal the offset of the root's key */
>>>> +       u64 root_offset;
>>>> +
>>>>         /* the id of the root that references this one */
>>>>         u64 ref_tree;
>>>>
>>>>         /* the dir id we're in from ref_tree */
>>>>         u64 dir_id;
>>>>
>>>> +       u64 top_id;
>>>> +
>>>>         /* generation when the root is created or last updated */
>>>>         u64 gen;
>>>>
>>>> +       /* creation generation of this root in sec*/
>>>> +       u64 ogen;
>>>> +
>>>>         /* creation time of this root in sec*/
>>>>         time_t otime;
>>>>
>>>> @@ -73,35 +85,254 @@ struct root_info {
>>>>         char *path;
>>>>
>>>>         /* the name of this root in the directory it lives in */
>>>> -       char name[];
>>>> +       char *name;
>>>> +
>>>> +       char *full_path;
>>>>  };
>>>>
>>>> +struct {
>>>> +       char    *name;
>>>> +       char    *column_name;
>>>> +       int     need_print;
>>>> +} btrfs_list_columns[] = {
>>>> +       {
>>>> +               .name           = "ID",
>>>> +               .column_name    = "ID",
>>>> +               .need_print     = 1,
>>>> +       },
>>>> +       {
>>>> +               .name           = "gen",
>>>> +               .column_name    = "Gen",
>>>> +               .need_print     = 1,
>>>> +       },
>>>> +       {
>>>> +               .name           = "cgen",
>>>> +               .column_name    = "CGen",
>>>> +               .need_print     = 0,
>>>> +       },
>>>> +       {
>>>> +               .name           = "parent",
>>>> +               .column_name    = "Parent",
>>>> +               .need_print     = 0,
>>>> +       },
>>>> +       {
>>>> +               .name           = "top level",
>>>> +               .column_name    = "Top Level",
>>>> +               .need_print     = 1,
>>>> +       },
>>>> +       {
>>>> +               .name           = "otime",
>>>> +               .column_name    = "OTime",
>>>> +               .need_print     = 0,
>>>> +       },
>>>> +       {
>>>> +               .name           = "uuid",
>>>> +               .column_name    = "UUID",
>>>> +               .need_print     = 0,
>>>> +       },
>>>> +       {
>>>> +               .name           = "path",
>>>> +               .column_name    = "Path",
>>>> +               .need_print     = 1,
>>>> +       },
>>>> +       {
>>>> +               .name           = NULL,
>>>> +               .column_name    = NULL,
>>>> +               .need_print     = 0,
>>>> +       },
>>>> +};
>>>> +
>>>> +static btrfs_list_filter_func all_filter_funcs[];
>>>> +static btrfs_list_comp_func all_comp_funcs[];
>>>> +
>>>> +void btrfs_list_setup_print_column(enum btrfs_list_column_enum column)
>>>> +{
>>>> +       int i;
>>>> +
>>>> +       BUG_ON(column < 0 || column > BTRFS_LIST_ALL);
>>>> +
>>>> +       if (column < BTRFS_LIST_ALL) {
>>>> +               btrfs_list_columns[column].need_print = 1;
>>>> +               return;
>>>> +       }
>>>> +
>>>> +       for (i = 0; i < BTRFS_LIST_ALL; i++)
>>>> +               btrfs_list_columns[i].need_print = 1;
>>>> +}
>>>> +
>>>>  static void root_lookup_init(struct root_lookup *tree)
>>>>  {
>>>>         tree->root.rb_node = NULL;
>>>>  }
>>>>
>>>> -static int comp_entry(struct root_info *entry, u64 root_id, u64 ref_tree)
>>>> +static int comp_entry_with_rootid(struct root_info *entry1,
>>>> +                                 struct root_info *entry2,
>>>> +                                 int is_descending)
>>>>  {
>>>> -       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;
>>>> +       int ret;
>>>> +
>>>> +       if (entry1->root_id > entry2->root_id)
>>>> +               ret = 1;
>>>> +       else if (entry1->root_id < entry2->root_id)
>>>> +               ret = -1;
>>>> +       else
>>>> +               ret = 0;
>>>> +
>>>> +       return is_descending ? -ret : ret;
>>>> +}
>>>> +
>>>> +static int comp_entry_with_gen(struct root_info *entry1,
>>>> +                              struct root_info *entry2,
>>>> +                              int is_descending)
>>>> +{
>>>> +       int ret;
>>>> +
>>>> +       if (entry1->gen > entry2->gen)
>>>> +               ret = 1;
>>>> +       else if (entry1->gen < entry2->gen)
>>>> +               ret = -1;
>>>> +       else
>>>> +               ret = 0;
>>>> +
>>>> +       return is_descending ? -ret : ret;
>>>> +}
>>>> +
>>>> +static int comp_entry_with_ogen(struct root_info *entry1,
>>>> +                               struct root_info *entry2,
>>>> +                               int is_descending)
>>>> +{
>>>> +       int ret;
>>>> +
>>>> +       if (entry1->ogen > entry2->ogen)
>>>> +               ret = 1;
>>>> +       else if (entry1->ogen < entry2->ogen)
>>>> +               ret = -1;
>>>> +       else
>>>> +               ret = 0;
>>>> +
>>>> +       return is_descending ? -ret : ret;
>>>> +}
>>>> +
>>>> +static btrfs_list_comp_func all_comp_funcs[] = {
>>>> +       [BTRFS_LIST_COMP_ROOTID]        = comp_entry_with_rootid,
>>>> +       [BTRFS_LIST_COMP_OGEN]          = comp_entry_with_ogen,
>>>> +       [BTRFS_LIST_COMP_GEN]           = comp_entry_with_gen,
>>>> +};
>>>> +
>>>> +struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void)
>>>> +{
>>>> +       struct btrfs_list_comparer_set *set;
>>>> +       int size;
>>>> +
>>>> +       size = sizeof(struct btrfs_list_comparer_set) +
>>>> +              BTRFS_LIST_NCOMPS_INCREASE * sizeof(struct btrfs_list_comparer);
>>>> +       set = malloc(size);
>>>> +       if (!set) {
>>>> +               fprintf(stderr, "memory allocation failed\n");
>>>> +               exit(1);
>>>> +       }
>>>> +
>>>> +       memset(set, 0, size);
>>>> +       set->total = BTRFS_LIST_NCOMPS_INCREASE;
>>>> +
>>>> +       return set;
>>>> +}
>>>> +
>>>> +void btrfs_list_free_comparer_set(struct btrfs_list_comparer_set *comp_set)
>>>> +{
>>>> +       free(comp_set);
>>>> +}
>>>> +
>>>> +int btrfs_list_setup_comparer(struct btrfs_list_comparer_set  **comp_set,
>>>> +                             enum btrfs_list_comp_enum comparer,
>>>> +                             int is_descending)
>>>> +{
>>>> +       struct btrfs_list_comparer_set *set = *comp_set;
>>>> +       int size;
>>>> +
>>>> +       BUG_ON(!set);
>>>> +       BUG_ON(comparer >= BTRFS_LIST_COMP_MAX);
>>>> +       BUG_ON(set->ncomps > set->total);
>>>> +
>>>> +       if (set->ncomps == set->total) {
>>>> +               size = set->total + BTRFS_LIST_NCOMPS_INCREASE;
>>>> +               size = sizeof(*set) + size * sizeof(struct btrfs_list_comparer);
>>>> +               set = realloc(set, size);
>>>> +               if (!set) {
>>>> +                       fprintf(stderr, "memory allocation failed\n");
>>>> +                       exit(1);
>>>> +               }
>>>> +
>>>> +               memset(&set->comps[set->total], 0,
>>>> +                      BTRFS_LIST_NCOMPS_INCREASE *
>>>> +                      sizeof(struct btrfs_list_comparer));
>>>> +               set->total += BTRFS_LIST_NCOMPS_INCREASE;
>>>> +               *comp_set = set;
>>>> +       }
>>>> +
>>>> +       BUG_ON(set->comps[set->ncomps].comp_func);
>>>> +
>>>> +       set->comps[set->ncomps].comp_func = all_comp_funcs[comparer];
>>>> +       set->comps[set->ncomps].is_descending = is_descending;
>>>> +       set->ncomps++;
>>>>         return 0;
>>>>  }
>>>>
>>>> -static int comp_entry_with_gen(struct root_info *entry, u64 root_id,
>>>> -                              u64 ref_tree, u64 gen)
>>>> +static int sort_comp(struct root_info *entry1, struct root_info *entry2,
>>>> +                    struct btrfs_list_comparer_set *set)
>>>>  {
>>>> -       if (entry->gen < gen)
>>>> -               return 1;
>>>> -       if (entry->gen > gen)
>>>> -               return -1;
>>>> -       return comp_entry(entry, root_id, ref_tree);
>>>> +       int rootid_compared = 0;
>>>> +       int i, ret = 0;
>>>> +
>>>> +       if (!set || !set->ncomps)
>>>> +               goto comp_rootid;
>>>> +
>>>> +       for (i = 0; i < set->ncomps; i++) {
>>>> +               if (!set->comps[i].comp_func)
>>>> +                       break;
>>>> +
>>>> +               ret = set->comps[i].comp_func(entry1, entry2,
>>>> +                                             set->comps[i].is_descending);
>>>> +               if (ret)
>>>> +                       return ret;
>>>> +
>>>> +               if (set->comps[i].comp_func == comp_entry_with_rootid)
>>>> +                       rootid_compared = 1;
>>>> +       }
>>>> +
>>>> +       if (!rootid_compared) {
>>>> +comp_rootid:
>>>> +               ret = comp_entry_with_rootid(entry1, entry2, 0);
>>>> +       }
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +static int sort_tree_insert(struct root_lookup *sort_tree,
>>>> +                           struct root_info *ins,
>>>> +                           struct btrfs_list_comparer_set *comp_set)
>>>> +{
>>>> +       struct rb_node **p = &sort_tree->root.rb_node;
>>>> +       struct rb_node *parent = NULL;
>>>> +       struct root_info *curr;
>>>> +       int ret;
>>>> +
>>>> +       while (*p) {
>>>> +               parent = *p;
>>>> +               curr = rb_entry(parent, struct root_info, sort_node);
>>>> +
>>>> +               ret = sort_comp(ins, curr, comp_set);
>>>> +               if (ret < 0)
>>>> +                       p = &(*p)->rb_left;
>>>> +               else if (ret > 0)
>>>> +                       p = &(*p)->rb_right;
>>>> +               else
>>>> +                       return -EEXIST;
>>>> +       }
>>>> +
>>>> +       rb_link_node(&ins->sort_node, parent, p);
>>>> +       rb_insert_color(&ins->sort_node, &sort_tree->root);
>>>> +       return 0;
>>>>  }
>>>>
>>>>  /*
>>>> @@ -109,118 +340,165 @@ static int comp_entry_with_gen(struct root_info *entry, u64 root_id,
>>>>   * 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, u64 *gen, struct rb_node *node)
>>>> +static int root_tree_insert(struct root_lookup *root_tree,
>>>> +                           struct root_info *ins)
>>>>  {
>>>> -       struct rb_node ** p = &root->rb_node;
>>>> +       struct rb_node **p = &root_tree->root.rb_node;
>>>>         struct rb_node * parent = NULL;
>>>> -       struct root_info *entry;
>>>> -       int comp;
>>>> +       struct root_info *curr;
>>>> +       int ret;
>>>>
>>>>         while(*p) {
>>>>                 parent = *p;
>>>> -               entry = rb_entry(parent, struct root_info, rb_node);
>>>> +               curr = rb_entry(parent, struct root_info, rb_node);
>>>>
>>>> -               if (!gen)
>>>> -                       comp = comp_entry(entry, root_id, ref_tree);
>>>> -               else
>>>> -                       comp = comp_entry_with_gen(entry, root_id, ref_tree,
>>>> -                                                  *gen);
>>>> -
>>>> -               if (comp < 0)
>>>> +               ret = comp_entry_with_rootid(ins, curr, 0);
>>>> +               if (ret < 0)
>>>>                         p = &(*p)->rb_left;
>>>> -               else if (comp > 0)
>>>> +               else if (ret > 0)
>>>>                         p = &(*p)->rb_right;
>>>>                 else
>>>> -                       return parent;
>>>> +                       return -EEXIST;
>>>>         }
>>>>
>>>> -       entry = rb_entry(parent, struct root_info, rb_node);
>>>> -       rb_link_node(node, parent, p);
>>>> -       rb_insert_color(node, root);
>>>> -       return NULL;
>>>> +       rb_link_node(&ins->rb_node, parent, p);
>>>> +       rb_insert_color(&ins->rb_node, &root_tree->root);
>>>> +       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 struct root_info *root_tree_search(struct root_lookup *root_tree,
>>>> +                                         u64 root_id)
>>>>  {
>>>> -       struct rb_node * n = root->rb_node;
>>>> +       struct rb_node *n = root_tree->root.rb_node;
>>>>         struct root_info *entry;
>>>> +       struct root_info tmp;
>>>> +       int ret;
>>>> +
>>>> +       tmp.root_id = root_id;
>>>>
>>>>         while(n) {
>>>>                 entry = rb_entry(n, struct root_info, rb_node);
>>>>
>>>> -               if (entry->root_id < root_id)
>>>> +               ret = comp_entry_with_rootid(&tmp, entry, 0);
>>>> +               if (ret < 0)
>>>>                         n = n->rb_left;
>>>> -               else if (entry->root_id > root_id)
>>>> +               else if (ret > 0)
>>>>                         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;
>>>> -                       }
>>>> +               else
>>>>                         return entry;
>>>> -               }
>>>>         }
>>>>         return NULL;
>>>>  }
>>>>
>>>> +static int update_root(struct root_lookup *root_lookup,
>>>> +                      u64 root_id, u64 ref_tree, u64 root_offset, u64 dir_id,
>>>> +                      char *name, int name_len, u64 ogen, u64 gen, time_t ot,
>>>> +                      void *uuid)
>>>> +{
>>>> +       struct root_info *ri;
>>>> +
>>>> +       ri = root_tree_search(root_lookup, root_id);
>>>> +       if (!ri || ri->root_id != root_id)
>>>> +               return -ENOENT;
>>>> +       if (name && name_len > 0) {
>>>> +               if (ri->name)
>>>> +                       free(ri->name);
>>>> +
>>>> +               ri->name = malloc(name_len + 1);
>>>> +               if (!ri->name) {
>>>> +                       fprintf(stderr, "memory allocation failed\n");
>>>> +                       exit(1);
>>>> +               }
>>>> +               strncpy(ri->name, name, name_len);
>>>> +               ri->name[name_len] = 0;
>>>> +       }
>>>> +       if (ref_tree)
>>>> +               ri->ref_tree = ref_tree;
>>>> +       if (root_offset)
>>>> +               ri->root_offset = root_offset;
>>>> +       if (dir_id)
>>>> +               ri->dir_id = dir_id;
>>>> +       if (gen)
>>>> +               ri->gen = gen;
>>>> +       if (ogen)
>>>> +               ri->ogen = ogen;
>>>> +       if (!ri->ogen && root_offset)
>>>> +               ri->ogen = root_offset;
>>>> +       if (ot)
>>>> +               ri->otime = ot;
>>>> +       if (uuid)
>>>> +               memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>>  /*
>>>> - * 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
>>>> + * add_root - update the existed root, or allocate a new root and insert it
>>>> + *           into the lookup tree.
>>>> + * root_id: object id of the root
>>>> + * ref_tree: object id of the referring root.
>>>> + * root_offset: offset value of the root'key
>>>> + * dir_id: inode id of the directory in ref_tree where this root can be found.
>>>> + * name: the name of root_id in that directory
>>>> + * name_len: the length of name
>>>> + * ogen: the original generation of the root
>>>> + * gen: the current generation of the root
>>>> + * ot: the original time(create time) of the root
>>>> + * uuid: uuid of the root
>>>>   */
>>>>  static int add_root(struct root_lookup *root_lookup,
>>>> -                   u64 root_id, u64 ref_tree, u64 dir_id, char *name,
>>>> -                   int name_len, u64 *gen, time_t ot, void *uuid)
>>>> +                   u64 root_id, u64 ref_tree, u64 root_offset, u64 dir_id,
>>>> +                   char *name, int name_len, u64 ogen, u64 gen, time_t ot,
>>>> +                   void *uuid)
>>>>  {
>>>>         struct root_info *ri;
>>>> -       struct rb_node *ret;
>>>> -       ri = malloc(sizeof(*ri) + name_len + 1);
>>>> +       int ret;
>>>> +
>>>> +       ret = update_root(root_lookup, root_id, ref_tree, root_offset, dir_id,
>>>> +                         name, name_len, ogen, gen, ot, uuid);
>>>> +       if (!ret)
>>>> +               return 0;
>>>> +
>>>> +       ri = malloc(sizeof(*ri));
>>>>         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;
>>>> +       memset(ri, 0, sizeof(*ri));
>>>>         ri->root_id = root_id;
>>>> -       ri->ref_tree = ref_tree;
>>>> -       if (name)
>>>> +
>>>> +       if (name && name_len > 0) {
>>>> +               ri->name = malloc(name_len + 1);
>>>> +               if (!ri->name) {
>>>> +                       fprintf(stderr, "memory allocation failed\n");
>>>> +                       exit(1);
>>>> +               }
>>>>                 strncpy(ri->name, name, name_len);
>>>> -       if (name_len > 0)
>>>>                 ri->name[name_len] = 0;
>>>> +       }
>>>> +       if (ref_tree)
>>>> +               ri->ref_tree = ref_tree;
>>>> +       if (dir_id)
>>>> +               ri->dir_id = dir_id;
>>>> +       if (root_offset)
>>>> +               ri->root_offset = root_offset;
>>>>         if (gen)
>>>> -               ri->gen = *gen;
>>>> -       ri->otime = ot;
>>>> +               ri->gen = gen;
>>>> +       if (ogen)
>>>> +               ri->ogen = ogen;
>>>> +       if (!ri->ogen && root_offset)
>>>> +               ri->ogen = root_offset;
>>>> +       if (ot)
>>>> +               ri->otime = ot;
>>>>
>>>>         if (uuid)
>>>>                 memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
>>>> -       else
>>>> -               memset(&ri->uuid, 0, BTRFS_UUID_SIZE);
>>>>
>>>> -       ret = tree_insert(&root_lookup->root, root_id, ref_tree, gen,
>>>> -                         &ri->rb_node);
>>>> +       ret = root_tree_insert(root_lookup, ri);
>>>>         if (ret) {
>>>>                 printf("failed to insert tree %llu\n", (unsigned long long)root_id);
>>>>                 exit(1);
>>>> @@ -228,24 +506,33 @@ static int add_root(struct root_lookup *root_lookup,
>>>>         return 0;
>>>>  }
>>>>
>>>> -static int update_root(struct root_lookup *root_lookup, u64 root_id, u64 gen,
>>>> -                       time_t ot, void *uuid)
>>>> +void __free_root_info(struct root_info *ri)
>>>>  {
>>>> -       struct root_info *ri;
>>>> +       if (ri->name)
>>>> +               free(ri->name);
>>>>
>>>> -       ri = tree_search(&root_lookup->root, root_id);
>>>> -       if (!ri || ri->root_id != root_id) {
>>>> -               fprintf(stderr, "could not find subvol %llu\n", root_id);
>>>> -               return -ENOENT;
>>>> -       }
>>>> -       ri->gen = gen;
>>>> -       ri->otime = ot;
>>>> -       if (uuid)
>>>> -               memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
>>>> -       else
>>>> -               memset(&ri->uuid, 0, BTRFS_UUID_SIZE);
>>>> +       if (ri->path)
>>>> +               free(ri->path);
>>>>
>>>> -       return 0;
>>>> +       if (ri->full_path)
>>>> +               free(ri->full_path);
>>>> +
>>>> +       free(ri);
>>>> +}
>>>> +
>>>> +void __free_all_subvolumn(struct root_lookup *root_tree)
>>>> +{
>>>> +       struct root_info *entry;
>>>> +       struct rb_node *n;
>>>> +
>>>> +       n = rb_first(&root_tree->root);
>>>> +       while (n) {
>>>> +               entry = rb_entry(n, struct root_info, rb_node);
>>>> +               rb_erase(n, &root_tree->root);
>>>> +               __free_root_info(entry);
>>>> +
>>>> +               n = rb_first(&root_tree->root);
>>>> +       }
>>>>  }
>>>>
>>>>  /*
>>>> @@ -255,8 +542,7 @@ static int update_root(struct root_lookup *root_lookup, u64 root_id, u64 gen,
>>>>   * 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 *parent_id, u64 *top_id, char **path)
>>>> +static int resolve_root(struct root_lookup *rl, struct root_info *ri)
>>>>  {
>>>>         char *full_path = NULL;
>>>>         int len = 0;
>>>> @@ -266,7 +552,6 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>>>>          * we go backwards from the root_info object and add pathnames
>>>>          * from parent directories as we go.
>>>>          */
>>>> -       *parent_id = 0;
>>>>         found = ri;
>>>>         while (1) {
>>>>                 char *tmp;
>>>> @@ -275,6 +560,10 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>>>>
>>>>                 /* room for / and for null */
>>>>                 tmp = malloc(add_len + 2 + len);
>>>> +               if (!tmp) {
>>>> +                       perror("malloc failed");
>>>> +                       exit(1);
>>>> +               }
>>>>                 if (full_path) {
>>>>                         memcpy(tmp + add_len + 1, full_path, len);
>>>>                         tmp[add_len] = '/';
>>>> @@ -289,13 +578,10 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>>>>                 }
>>>>
>>>>                 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;
>>>> +                       ri->top_id = next;
>>>>                         break;
>>>>                 }
>>>>
>>>> @@ -303,14 +589,14 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
>>>>                  * if the ref_tree wasn't in our tree of roots, we're
>>>>                  * at the top
>>>>                  */
>>>> -               found = tree_search(&rl->root, next);
>>>> +               found = root_tree_search(rl, next);
>>>>                 if (!found) {
>>>> -                       *top_id = next;
>>>> +                       ri->top_id = next;
>>>>                         break;
>>>>                 }
>>>>         }
>>>>
>>>> -       *path = full_path;
>>>> +       ri->full_path = full_path;
>>>>
>>>>         return 0;
>>>>  }
>>>> @@ -608,7 +894,7 @@ build:
>>>>         return full;
>>>>  }
>>>>
>>>> -static int get_default_subvolid(int fd, u64 *default_id)
>>>> +int btrfs_list_get_default_subvolume(int fd, u64 *default_id)
>>>>  {
>>>>         struct btrfs_ioctl_search_args args;
>>>>         struct btrfs_ioctl_search_key *sk = &args.key;
>>>> @@ -674,10 +960,9 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>>>>         int name_len;
>>>>         char *name;
>>>>         u64 dir_id;
>>>> -       u8 type;
>>>>         u64 gen = 0;
>>>> +       u64 ogen;
>>>>         int i;
>>>> -       int get_gen = 0;
>>>>         time_t t;
>>>>         u8 uuid[BTRFS_UUID_SIZE];
>>>>
>>>> @@ -692,7 +977,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>>>>          * only send back this type of key now.
>>>>          */
>>>>         sk->max_type = BTRFS_ROOT_BACKREF_KEY;
>>>> -       sk->min_type = BTRFS_ROOT_BACKREF_KEY;
>>>> +       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>>>>
>>>>         sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
>>>>
>>>> @@ -704,7 +989,6 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
>>>>         sk->max_offset = (u64)-1;
>>>>         sk->max_transid = (u64)-1;
>>>>
>>>> -again:
>>>>         /* just a big number, doesn't matter much */
>>>>         sk->nr_items = 4096;
>>>>
>>>> @@ -726,28 +1010,32 @@ again:
>>>>                         sh = (struct btrfs_ioctl_search_header *)(args.buf +
>>>>                                                                   off);
>>>>                         off += sizeof(*sh);
>>>> -                       if (!get_gen && sh->type == BTRFS_ROOT_BACKREF_KEY) {
>>>> +                       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, NULL, 0, NULL);
>>>> -                       } else if (get_gen && sh->type == BTRFS_ROOT_ITEM_KEY) {
>>>> +                                        0, dir_id, name, name_len, 0, 0, 0,
>>>> +                                        NULL);
>>>> +                       } else if (sh->type == BTRFS_ROOT_ITEM_KEY) {
>>>>                                 ri = (struct btrfs_root_item *)(args.buf + off);
>>>>                                 gen = btrfs_root_generation(ri);
>>>>                                 if(sh->len >
>>>>                                    sizeof(struct btrfs_root_item_v0)) {
>>>>                                         t = ri->otime.sec;
>>>> +                                       ogen = btrfs_root_otransid(ri);
>>>>                                         memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
>>>>                                 } else {
>>>>                                         t = 0;
>>>> +                                       ogen = 0;
>>>>                                         memset(uuid, 0, BTRFS_UUID_SIZE);
>>>>                                 }
>>>>
>>>> -                               update_root(root_lookup, sh->objectid, gen, t,
>>>> -                                       uuid);
>>>> +                               add_root(root_lookup, sh->objectid, 0,
>>>> +                                        sh->offset, 0, NULL, 0, ogen, gen, t,
>>>> +                                        uuid);
>>>>                         }
>>>>
>>>>                         off += sh->len;
>>>> @@ -761,129 +1049,139 @@ again:
>>>>                         sk->min_offset = sh->offset;
>>>>                 }
>>>>                 sk->nr_items = 4096;
>>>> -               /* this iteration is done, step forward one root for the next
>>>> -                * ioctl
>>>> -                */
>>>> -               if (get_gen)
>>>> -                       type = BTRFS_ROOT_ITEM_KEY;
>>>> +               sk->min_offset++;
>>>> +               if (!sk->min_offset)    /* overflow */
>>>> +                       sk->min_type++;
>>>>                 else
>>>> -                       type = BTRFS_ROOT_BACKREF_KEY;
>>>> +                       continue;
>>>>
>>>> -               if (sk->min_type < type) {
>>>> -                       sk->min_type = type;
>>>> -                       sk->min_offset = 0;
>>>> -               } else  if (sk->min_objectid < BTRFS_LAST_FREE_OBJECTID) {
>>>> +               if (sk->min_type > BTRFS_ROOT_BACKREF_KEY) {
>>>> +                       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>>>>                         sk->min_objectid++;
>>>> -                       sk->min_type = type;
>>>> -                       sk->min_offset = 0;
>>>>                 } else
>>>> +                       continue;
>>>> +
>>>> +               if (sk->min_objectid > sk->max_objectid)
>>>>                         break;
>>>>         }
>>>>
>>>> -       if (!get_gen) {
>>>> -               memset(&args, 0, sizeof(args));
>>>> +       return 0;
>>>> +}
>>>>
>>>> -               sk->tree_id = 1;
>>>> -               sk->max_type = BTRFS_ROOT_ITEM_KEY;
>>>> -               sk->min_type = BTRFS_ROOT_ITEM_KEY;
>>>> +static int filter_by_rootid(struct root_info *ri, void *arg)
>>>> +{
>>>> +       u64 default_root_id = *((u64 *)arg);
>>>>
>>>> -               sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
>>>> +       return ri->root_id == default_root_id;
>>>> +}
>>>>
>>>> -               sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
>>>> -               sk->max_offset = (u64)-1;
>>>> -               sk->max_transid = (u64)-1;
>>>> +static int filter_snapshot(struct root_info *ri, void *arg)
>>>> +{
>>>> +       return !!ri->root_offset;
>>>> +}
>>>> +
>>>> +static btrfs_list_filter_func all_filter_funcs[] = {
>>>> +       [BTRFS_LIST_FILTER_ROOTID]              = filter_by_rootid,
>>>> +       [BTRFS_LIST_FILTER_SNAPSHOT_ONLY]       = filter_snapshot,
>>>> +};
>>>>
>>>> -               get_gen = 1;
>>>> -               goto again;
>>>> +struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void)
>>>> +{
>>>> +       struct btrfs_list_filter_set *set;
>>>> +       int size;
>>>> +
>>>> +       size = sizeof(struct btrfs_list_filter_set) +
>>>> +              BTRFS_LIST_NFILTERS_INCREASE * sizeof(struct btrfs_list_filter);
>>>> +       set = malloc(size);
>>>> +       if (!set) {
>>>> +               fprintf(stderr, "memory allocation failed\n");
>>>> +               exit(1);
>>>>         }
>>>> -       return 0;
>>>> +
>>>> +       memset(set, 0, size);
>>>> +       set->total = BTRFS_LIST_NFILTERS_INCREASE;
>>>> +
>>>> +       return set;
>>>>  }
>>>>
>>>> -static int __list_snapshot_search(int fd, struct root_lookup *root_lookup)
>>>> +void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set)
>>>>  {
>>>> -       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 gen = 0;
>>>> -       int i;
>>>> -
>>>> -       root_lookup_init(root_lookup);
>>>> -       memset(&args, 0, sizeof(args));
>>>> +       free(filter_set);
>>>> +}
>>>>
>>>> -       sk->tree_id = 1;
>>>> -       sk->max_type = BTRFS_ROOT_ITEM_KEY;
>>>> -       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>>>> -       sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
>>>> -       sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
>>>> -       sk->max_offset = (u64)-1;
>>>> -       sk->max_transid = (u64)-1;
>>>> -       sk->nr_items = 4096;
>>>> +int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
>>>> +                           enum btrfs_list_filter_enum filter, void *data)
>>>> +{
>>>> +       struct btrfs_list_filter_set *set = *filter_set;
>>>> +       int size;
>>>> +
>>>> +       BUG_ON(!set);
>>>> +       BUG_ON(filter >= BTRFS_LIST_FILTER_MAX);
>>>> +       BUG_ON(set->nfilters > set->total);
>>>> +
>>>> +       if (set->nfilters == set->total) {
>>>> +               size = set->total + BTRFS_LIST_NFILTERS_INCREASE;
>>>> +               size = sizeof(*set) + size * sizeof(struct btrfs_list_filter);
>>>> +               set = realloc(set, size);
>>>> +               if (!set) {
>>>> +                       fprintf(stderr, "memory allocation failed\n");
>>>> +                       exit(1);
>>>> +               }
>>>>
>>>> -       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;
>>>> +               memset(&set->filters[set->total], 0,
>>>> +                      BTRFS_LIST_NFILTERS_INCREASE *
>>>> +                      sizeof(struct btrfs_list_filter));
>>>> +               set->total += BTRFS_LIST_NFILTERS_INCREASE;
>>>> +               *filter_set = set;
>>>> +       }
>>>>
>>>> -               off = 0;
>>>> +       BUG_ON(set->filters[set->nfilters].filter_func);
>>>>
>>>> -               /*
>>>> -                * 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;
>>>> -                       time_t  t;
>>>> -                       u8 uuid[BTRFS_UUID_SIZE];
>>>> +       set->filters[set->nfilters].filter_func = all_filter_funcs[filter];
>>>> +       set->filters[set->nfilters].data = data;
>>>> +       set->nfilters++;
>>>> +       return 0;
>>>> +}
>>>>
>>>> -                       sh = (struct btrfs_ioctl_search_header *)(args.buf +
>>>> -                                                                 off);
>>>> -                       off += sizeof(*sh);
>>>> -                       if (sh->type == BTRFS_ROOT_ITEM_KEY && sh->offset) {
>>>> -                               item = (struct btrfs_root_item *)(args.buf + off);
>>>> -                               if(sh->len >
>>>> -                                  sizeof(struct btrfs_root_item_v0)) {
>>>> -                                       t = item->otime.sec;
>>>> -                                       memcpy(uuid, item->uuid,
>>>> -                                              BTRFS_UUID_SIZE);
>>>> -                               } else {
>>>> -                                       t = 0;
>>>> -                                       memset(uuid, 0, BTRFS_UUID_SIZE);
>>>> -                               }
>>>> -                               gen = sh->offset;
>>>> +static int filter_root(struct root_info *ri,
>>>> +                      struct btrfs_list_filter_set *set)
>>>> +{
>>>> +       int i, ret;
>>>>
>>>> -                               add_root(root_lookup, sh->objectid, 0,
>>>> -                                        0, NULL, 0, &gen, t, uuid);
>>>> -                       }
>>>> -                       off += sh->len;
>>>> +       if (!set || !set->nfilters)
>>>> +               return 1;
>>>>
>>>> -                       /*
>>>> -                        * 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_ITEM_KEY) {
>>>> -                       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>>>> -                       sk->min_offset = 0;
>>>> -               } else  if (sk->min_objectid < BTRFS_LAST_FREE_OBJECTID) {
>>>> -                       sk->min_objectid++;
>>>> -                       sk->min_type = BTRFS_ROOT_ITEM_KEY;
>>>> -                       sk->min_offset = 0;
>>>> -               } else
>>>> +       for (i = 0; i < set->nfilters; i++) {
>>>> +               if (!set->filters[i].filter_func)
>>>>                         break;
>>>> +               ret = set->filters[i].filter_func(ri, set->filters[i].data);
>>>> +               if (!ret)
>>>> +                       return 0;
>>>> +       }
>>>> +       return 1;
>>>> +}
>>>> +
>>>> +static void __filter_and_sort_subvol(struct root_lookup *all_subvols,
>>>> +                                   struct root_lookup *sort_tree,
>>>> +                                   struct btrfs_list_filter_set *filter_set,
>>>> +                                   struct btrfs_list_comparer_set *comp_set)
>>>> +{
>>>> +       struct rb_node *n;
>>>> +       struct root_info *entry;
>>>> +       int ret;
>>>> +
>>>> +       root_lookup_init(sort_tree);
>>>> +
>>>> +       n = rb_last(&all_subvols->root);
>>>> +       while (n) {
>>>> +               entry = rb_entry(n, struct root_info, rb_node);
>>>> +
>>>> +               resolve_root(all_subvols, entry);
>>>> +               ret = filter_root(entry, filter_set);
>>>> +               if (ret)
>>>> +                       sort_tree_insert(sort_tree, entry, comp_set);
>>>> +               n = rb_prev(n);
>>>>         }
>>>> -       return 0;
>>>>  }
>>>>
>>>>  static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
>>>> @@ -904,128 +1202,91 @@ static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
>>>>         return 0;
>>>>  }
>>>>
>>>> -int list_subvols(int fd, int print_parent, int get_default, int print_uuid)
>>>> +static void print_subvolume_column(struct root_info *subv,
>>>> +                                  enum btrfs_list_column_enum column)
>>>>  {
>>>> -       struct root_lookup root_lookup;
>>>> -       struct rb_node *n;
>>>> -       u64 default_id;
>>>> -       int ret;
>>>> +       char tstr[256];
>>>>         char uuidparse[37];
>>>>
>>>> -       if (get_default) {
>>>> -               ret = get_default_subvolid(fd, &default_id);
>>>> -               if (ret) {
>>>> -                       fprintf(stderr, "ERROR: can't perform the search - %s\n",
>>>> -                               strerror(errno));
>>>> -                       return ret;
>>>> -               }
>>>> -               if (default_id == 0) {
>>>> -                       fprintf(stderr, "ERROR: 'default' dir item not found\n");
>>>> -                       return ret;
>>>> -               }
>>>> -
>>>> -               /* no need to resolve roots if FS_TREE is default */
>>>> -               if (default_id == BTRFS_FS_TREE_OBJECTID) {
>>>> -                       printf("ID 5 (FS_TREE)\n");
>>>> -                       return ret;
>>>> -               }
>>>> -       }
>>>> -
>>>> -       ret = __list_subvol_search(fd, &root_lookup);
>>>> -       if (ret) {
>>>> -               fprintf(stderr, "ERROR: can't perform the search - %s\n",
>>>> -                               strerror(errno));
>>>> -               return ret;
>>>> +       BUG_ON(column >= BTRFS_LIST_ALL || column < 0);
>>>> +
>>>> +       switch (column) {
>>>> +       case BTRFS_LIST_OBJECTID:
>>>> +               printf("%llu", subv->root_id);
>>>> +               break;
>>>> +       case BTRFS_LIST_GENERATION:
>>>> +               printf("%llu", subv->gen);
>>>> +               break;
>>>> +       case BTRFS_LIST_OGENERATION:
>>>> +               printf("%llu", subv->ogen);
>>>> +               break;
>>>> +       case BTRFS_LIST_PARENT:
>>>> +               printf("%llu", subv->ref_tree);
>>>> +               break;
>>>> +       case BTRFS_LIST_TOP_LEVEL:
>>>> +               printf("%llu", subv->top_id);
>>>> +               break;
>>>> +       case BTRFS_LIST_OTIME:
>>>> +               if (subv->otime)
>>>> +                       strftime(tstr, 256, "%Y-%m-%d %X",
>>>> +                                localtime(&subv->otime));
>>>> +               else
>>>> +                       strcpy(tstr, "-");
>>>> +               printf("%s", tstr);
>>>> +               break;
>>>> +       case BTRFS_LIST_UUID:
>>>> +               if (uuid_is_null(subv->uuid))
>>>> +                       strcpy(uuidparse, "-");
>>>> +               else
>>>> +                       uuid_unparse(subv->uuid, uuidparse);
>>>> +               printf("%s", uuidparse);
>>>> +               break;
>>>> +       case BTRFS_LIST_PATH:
>>>> +               BUG_ON(!subv->full_path);
>>>> +               printf("%s", subv->full_path);
>>>> +               break;
>>>> +       default:
>>>> +               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.
>>>> -        */
>>>> -       ret = __list_subvol_fill_paths(fd, &root_lookup);
>>>> -       if (ret < 0)
>>>> -               return ret;
>>>> -
>>>> -       /* now that we have all the subvol-relative paths filled in,
>>>> -        * we have to string the subvols together so that we can get
>>>> -        * a path all the way back to the FS root
>>>> -        */
>>>> -       n = rb_last(&root_lookup.root);
>>>> -       while (n) {
>>>> -               struct root_info *entry;
>>>> -               u64 level;
>>>> -               u64 parent_id;
>>>> -               char *path;
>>>> +static void print_single_volume_info_default(struct root_info *subv)
>>>> +{
>>>> +       int i;
>>>>
>>>> -               entry = rb_entry(n, struct root_info, rb_node);
>>>> -               if (get_default && entry->root_id != default_id) {
>>>> -                       n = rb_prev(n);
>>>> +       for (i = 0; i < BTRFS_LIST_ALL; i++) {
>>>> +               if (!btrfs_list_columns[i].need_print)
>>>>                         continue;
>>>> -               }
>>>>
>>>> -               resolve_root(&root_lookup, entry, &parent_id, &level, &path);
>>>> -               if (print_parent) {
>>>> -                       if (print_uuid) {
>>>> -                               if (uuid_is_null(entry->uuid))
>>>> -                                       strcpy(uuidparse, "-");
>>>> -                               else
>>>> -                                       uuid_unparse(entry->uuid, uuidparse);
>>>> -                               printf("ID %llu gen %llu parent %llu top level %llu"
>>>> -                                       " uuid %s path %s\n",
>>>> -                                       (unsigned long long)entry->root_id,
>>>> -                                       (unsigned long long)entry->gen,
>>>> -                                       (unsigned long long)parent_id,
>>>> -                                       (unsigned long long)level,
>>>> -                                       uuidparse, path);
>>>> -                       } else {
>>>> -                               printf("ID %llu gen %llu parent %llu top level"
>>>> -                                       " %llu path %s\n",
>>>> -                                       (unsigned long long)entry->root_id,
>>>> -                                       (unsigned long long)entry->gen,
>>>> -                                       (unsigned long long)parent_id,
>>>> -                                       (unsigned long long)level, path);
>>>> -                       }
>>>> -               } else {
>>>> -                       if (print_uuid) {
>>>> -                               if (uuid_is_null(entry->uuid))
>>>> -                                       strcpy(uuidparse, "-");
>>>> -                               else
>>>> -                                       uuid_unparse(entry->uuid, uuidparse);
>>>> -                               printf("ID %llu gen %llu top level %llu"
>>>> -                                       " uuid %s path %s\n",
>>>> -                                       (unsigned long long)entry->root_id,
>>>> -                                       (unsigned long long)entry->gen,
>>>> -                                       (unsigned long long)level,
>>>> -                                       uuidparse, path);
>>>> -                       } else {
>>>> -                               printf("ID %llu gen %llu top level %llu path %s\n",
>>>> -                                       (unsigned long long)entry->root_id,
>>>> -                                       (unsigned long long)entry->gen,
>>>> -                                       (unsigned long long)level, path);
>>>> -                       }
>>>> -               }
>>>> +               printf("%s ", btrfs_list_columns[i].name);
>>>> +               print_subvolume_column(subv, i);
>>>>
>>>> -               free(path);
>>>> -               n = rb_prev(n);
>>>> +               if (i != BTRFS_LIST_PATH)
>>>> +                       printf(" ");
>>>>         }
>>>> +       printf("\n");
>>>> +}
>>>>
>>>> -       return ret;
>>>> +static void print_all_volume_info_default(struct root_lookup *sorted_tree)
>>>> +{
>>>> +       struct rb_node *n;
>>>> +       struct root_info *entry;
>>>> +
>>>> +       n = rb_first(&sorted_tree->root);
>>>> +       while (n) {
>>>> +               entry = rb_entry(n, struct root_info, sort_node);
>>>> +               print_single_volume_info_default(entry);
>>>> +               n = rb_next(n);
>>>> +       }
>>>>  }
>>>>
>>>> -int list_snapshots(int fd, int print_parent, int order, int print_uuid)
>>>> +int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
>>>> +                      struct btrfs_list_comparer_set *comp_set)
>>>>  {
>>>>         struct root_lookup root_lookup;
>>>> -       struct root_lookup root_lookup_snap;
>>>> -       struct rb_node *n;
>>>> +       struct root_lookup root_sort;
>>>>         int ret;
>>>>
>>>> -       ret = __list_snapshot_search(fd, &root_lookup_snap);
>>>> -       if (ret) {
>>>> -               fprintf(stderr, "ERROR: can't perform the search - %s\n",
>>>> -                               strerror(errno));
>>>> -               return ret;
>>>> -       }
>>>> -
>>>>         ret = __list_subvol_search(fd, &root_lookup);
>>>>         if (ret) {
>>>>                 fprintf(stderr, "ERROR: can't perform the search - %s\n",
>>>> @@ -1041,86 +1302,11 @@ int list_snapshots(int fd, int print_parent, int order, int print_uuid)
>>>>         if (ret < 0)
>>>>                 return ret;
>>>>
>>>> -       /* now that we have all the subvol-relative paths filled in,
>>>> -        * we have to string the subvols together so that we can get
>>>> -        * a path all the way back to the FS root
>>>> -        */
>>>> -       if (!order)
>>>> -               n = rb_last(&root_lookup_snap.root);
>>>> -       else
>>>> -               n = rb_first(&root_lookup_snap.root);
>>>> -       while (n) {
>>>> -               struct root_info *entry_snap;
>>>> -               struct root_info *entry;
>>>> -               u64 level;
>>>> -               u64 parent_id;
>>>> -               char *path;
>>>> -               time_t t;
>>>> -               char tstr[256];
>>>> -               char uuidparse[37];
>>>> -
>>>> -               entry_snap = rb_entry(n, struct root_info, rb_node);
>>>> -               entry = tree_search(&root_lookup.root, entry_snap->root_id);
>>>> -
>>>> -               resolve_root(&root_lookup, entry, &parent_id, &level, &path);
>>>> -               t = entry->otime;
>>>> -               if(t)
>>>> -                       strftime(tstr,256,"%Y-%m-%d %X",localtime(&t));
>>>> -               else
>>>> -                       strcpy(tstr,"-");
>>>> -               if (print_parent) {
>>>> -                       if (print_uuid) {
>>>> -                               if (uuid_is_null(entry->uuid))
>>>> -                                       strcpy(uuidparse, "-");
>>>> -                               else
>>>> -                                       uuid_unparse(entry->uuid, uuidparse);
>>>> -                               printf("ID %llu gen %llu cgen %llu parent %llu"
>>>> -                                       " top level %llu otime %s uuid %s path %s\n",
>>>> -                                       (unsigned long long)entry->root_id,
>>>> -                                       (unsigned long long)entry->gen,
>>>> -                                       (unsigned long long)entry_snap->gen,
>>>> -                                       (unsigned long long)parent_id,
>>>> -                                       (unsigned long long)level,
>>>> -                                       tstr, uuidparse, path);
>>>> -                       } else {
>>>> -                               printf("ID %llu gen %llu cgen %llu parent %llu"
>>>> -                                       " top level %llu otime %s path %s\n",
>>>> -                                       (unsigned long long)entry->root_id,
>>>> -                                       (unsigned long long)entry->gen,
>>>> -                                       (unsigned long long)entry_snap->gen,
>>>> -                                       (unsigned long long)parent_id,
>>>> -                                       (unsigned long long)level, tstr, path);
>>>> -                       }
>>>> -               } else {
>>>> -                       if (print_uuid) {
>>>> -                               if (uuid_is_null(entry->uuid))
>>>> -                                       strcpy(uuidparse, "-");
>>>> -                               else
>>>> -                                       uuid_unparse(entry->uuid, uuidparse);
>>>> -                               printf("ID %llu gen %llu cgen %llu top level %llu "
>>>> -                                       "otime %s uuid %s path %s\n",
>>>> -                                       (unsigned long long)entry->root_id,
>>>> -                                       (unsigned long long)entry->gen,
>>>> -                                       (unsigned long long)entry_snap->gen,
>>>> -                                       (unsigned long long)level,
>>>> -                                       tstr, uuidparse, path);
>>>> -                       } else {
>>>> -                               printf("ID %llu gen %llu cgen %llu top level %llu "
>>>> -                                       "otime %s path %s\n",
>>>> -                                       (unsigned long long)entry->root_id,
>>>> -                                       (unsigned long long)entry->gen,
>>>> -                                       (unsigned long long)entry_snap->gen,
>>>> -                                       (unsigned long long)level, tstr, path);
>>>> -                       }
>>>> -               }
>>>> -
>>>> -               free(path);
>>>> -               if (!order)
>>>> -                       n = rb_prev(n);
>>>> -               else
>>>> -                       n = rb_next(n);
>>>> -       }
>>>> +       __filter_and_sort_subvol(&root_lookup, &root_sort, filter_set,
>>>> +                                comp_set);
>>>>
>>>> +       print_all_volume_info_default(&root_sort);
>>>> +       __free_all_subvolumn(&root_lookup);
>>>>         return ret;
>>>>  }
>>>>
>>>> @@ -1203,7 +1389,7 @@ static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh,
>>>>         return 0;
>>>>  }
>>>>
>>>> -int find_updated_files(int fd, u64 root_id, u64 oldest_gen)
>>>> +int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen)
>>>>  {
>>>>         int ret;
>>>>         struct btrfs_ioctl_search_args args;
>>>> @@ -1304,7 +1490,7 @@ int find_updated_files(int fd, u64 root_id, u64 oldest_gen)
>>>>         return ret;
>>>>  }
>>>>
>>>> -char *path_for_root(int fd, u64 root)
>>>> +char *btrfs_list_path_for_root(int fd, u64 root)
>>>>  {
>>>>         struct root_lookup root_lookup;
>>>>         struct rb_node *n;
>>>> @@ -1322,19 +1508,17 @@ char *path_for_root(int fd, u64 root)
>>>>         n = rb_last(&root_lookup.root);
>>>>         while (n) {
>>>>                 struct root_info *entry;
>>>> -               u64 parent_id;
>>>> -               u64 level;
>>>> -               char *path;
>>>>
>>>>                 entry = rb_entry(n, struct root_info, rb_node);
>>>> -               resolve_root(&root_lookup, entry, &parent_id, &level, &path);
>>>> -               if (entry->root_id == root)
>>>> -                       ret_path = path;
>>>> -               else
>>>> -                       free(path);
>>>> +               resolve_root(&root_lookup, entry);
>>>> +               if (entry->root_id == root) {
>>>> +                       ret_path = entry->full_path;
>>>> +                       entry->full_path = NULL;
>>>> +               }
>>>>
>>>>                 n = rb_prev(n);
>>>>         }
>>>> +       __free_all_subvolumn(&root_lookup);
>>>>
>>>>         return ret_path;
>>>>  }
>>>> diff --git a/btrfs-list.h b/btrfs-list.h
>>>> index b4a7f30..ca3eb7b 100644
>>>> --- a/btrfs-list.h
>>>> +++ b/btrfs-list.h
>>>> @@ -16,7 +16,72 @@
>>>>   * Boston, MA 021110-1307, USA.
>>>>   */
>>>>
>>>> -int list_subvols(int fd, int print_parent, int get_default, int print_uuid);
>>>> -int list_snapshots(int fd, int print_parent, int order, int print_uuid);
>>>> -int find_updated_files(int fd, u64 root_id, u64 oldest_gen);
>>>> -char *path_for_root(int fd, u64 root);
>>>> +struct root_info;
>>>> +
>>>> +typedef int (*btrfs_list_filter_func)(struct root_info *, void *);
>>>> +typedef int (*btrfs_list_comp_func)(struct root_info *, struct root_info *,
>>>> +                                   int);
>>>> +
>>>> +struct btrfs_list_filter {
>>>> +       btrfs_list_filter_func filter_func;
>>>> +       void *data;
>>>> +};
>>>> +
>>>> +struct btrfs_list_comparer {
>>>> +       btrfs_list_comp_func comp_func;
>>>> +       int is_descending;
>>>> +};
>>>> +
>>>> +struct btrfs_list_filter_set {
>>>> +       int total;
>>>> +       int nfilters;
>>>> +       struct btrfs_list_filter filters[0];
>>>> +};
>>>> +
>>>> +struct btrfs_list_comparer_set {
>>>> +       int total;
>>>> +       int ncomps;
>>>> +       struct btrfs_list_comparer comps[0];
>>>> +};
>>>> +
>>>> +enum btrfs_list_column_enum {
>>>> +       BTRFS_LIST_OBJECTID,
>>>> +       BTRFS_LIST_GENERATION,
>>>> +       BTRFS_LIST_OGENERATION,
>>>> +       BTRFS_LIST_PARENT,
>>>> +       BTRFS_LIST_TOP_LEVEL,
>>>> +       BTRFS_LIST_OTIME,
>>>> +       BTRFS_LIST_UUID,
>>>> +       BTRFS_LIST_PATH,
>>>> +       BTRFS_LIST_ALL,
>>>> +};
>>>> +
>>>> +enum btrfs_list_filter_enum {
>>>> +       BTRFS_LIST_FILTER_ROOTID,
>>>> +       BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
>>>> +       BTRFS_LIST_FILTER_MAX,
>>>> +};
>>>> +
>>>> +enum btrfs_list_comp_enum {
>>>> +       BTRFS_LIST_COMP_ROOTID,
>>>> +       BTRFS_LIST_COMP_OGEN,
>>>> +       BTRFS_LIST_COMP_GEN,
>>>> +       BTRFS_LIST_COMP_MAX,
>>>> +};
>>>> +
>>>> +void btrfs_list_setup_print_column(enum btrfs_list_column_enum column);
>>>> +struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void);
>>>> +void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set);
>>>> +int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
>>>> +                           enum btrfs_list_filter_enum filter, void *data);
>>>> +struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void);
>>>> +void btrfs_list_free_comparer_set(struct btrfs_list_comparer_set *comp_set);
>>>> +int btrfs_list_setup_comparer(struct btrfs_list_comparer_set **comp_set,
>>>> +                             enum btrfs_list_comp_enum comparer,
>>>> +                             int is_descending);
>>>> +
>>>> +int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
>>>> +                      struct btrfs_list_comparer_set *comp_set);
>>>> +int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen);
>>>> +int btrfs_list_get_default_subvolume(int fd, u64 *default_id);
>>>> +char *btrfs_list_path_for_root(int fd, u64 root);
>>>> diff --git a/cmds-inspect.c b/cmds-inspect.c
>>>> index f943ed9..376fab2 100644
>>>> --- a/cmds-inspect.c
>>>> +++ b/cmds-inspect.c
>>>> @@ -194,7 +194,7 @@ static int cmd_logical_resolve(int argc, char **argv)
>>>>                 char *name;
>>>>
>>>>                 if (getpath) {
>>>> -                       name = path_for_root(fd, root);
>>>> +                       name = btrfs_list_path_for_root(fd, root);
>>>>                         if (IS_ERR(name))
>>>>                                 return PTR_ERR(name);
>>>>                         if (!name) {
>>>> diff --git a/cmds-subvolume.c b/cmds-subvolume.c
>>>> index cd4b5a7..b1cf2bd 100644
>>>> --- a/cmds-subvolume.c
>>>> +++ b/cmds-subvolume.c
>>>> @@ -28,6 +28,7 @@
>>>>  #include "ioctl.h"
>>>>  #include "qgroup.h"
>>>>
>>>> +#include "ctree.h"
>>>>  #include "commands.h"
>>>>  #include "btrfs-list.h"
>>>>
>>>> @@ -270,13 +271,15 @@ static const char * const cmd_subvol_list_usage[] = {
>>>>
>>>>  static int cmd_subvol_list(int argc, char **argv)
>>>>  {
>>>> +       struct btrfs_list_filter_set *filter_set;
>>>> +       struct btrfs_list_comparer_set *comparer_set;
>>>>         int fd;
>>>>         int ret;
>>>> -       int print_parent = 0;
>>>> -       int print_snap_only = 0;
>>>> -       int order = 0;
>>>> +       int order;
>>>>         char *subvol;
>>>> -       int print_uuid = 0;
>>>> +
>>>> +       filter_set = btrfs_list_alloc_filter_set();
>>>> +       comparer_set = btrfs_list_alloc_comparer_set();
>>>>
>>>>         optind = 1;
>>>>         while(1) {
>>>> @@ -286,14 +289,21 @@ static int cmd_subvol_list(int argc, char **argv)
>>>>
>>>>                 switch(c) {
>>>>                 case 'p':
>>>> -                       print_parent = 1;
>>>> +                       btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
>>>>                         break;
>>>>                 case 's':
>>>> -                       print_snap_only = 1;
>>>>                         order = atoi(optarg);
>>>> +                       btrfs_list_setup_filter(&filter_set,
>>>> +                                               BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
>>>> +                                               NULL);
>>>> +                       btrfs_list_setup_comparer(&comparer_set,
>>>> +                                                 BTRFS_LIST_COMP_OGEN,
>>>> +                                                 !order);
>>>> +                       btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
>>>> +                       btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
>>>>                         break;
>>>>                 case 'u':
>>>> -                       print_uuid =1;
>>>> +                       btrfs_list_setup_print_column(BTRFS_LIST_UUID);
>>>>                         break;
>>>>                 default:
>>>>                         usage(cmd_subvol_list_usage);
>>>> @@ -320,10 +330,8 @@ static int cmd_subvol_list(int argc, char **argv)
>>>>                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
>>>>                 return 12;
>>>>         }
>>>> -       if (!print_snap_only)
>>>> -               ret = list_subvols(fd, print_parent, 0, print_uuid);
>>>> -       else
>>>> -               ret = list_snapshots(fd, print_parent, order, print_uuid);
>>>> +
>>>> +       ret = btrfs_list_subvols(fd, filter_set, comparer_set);
>>>>         if (ret)
>>>>                 return 19;
>>>>         return 0;
>>>> @@ -483,6 +491,8 @@ static int cmd_subvol_get_default(int argc, char **argv)
>>>>         int fd;
>>>>         int ret;
>>>>         char *subvol;
>>>> +       struct btrfs_list_filter_set *filter_set;
>>>> +       u64 default_id;
>>>>
>>>>         if (check_argc_exact(argc, 2))
>>>>                 usage(cmd_subvol_get_default_usage);
>>>> @@ -504,7 +514,30 @@ static int cmd_subvol_get_default(int argc, char **argv)
>>>>                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
>>>>                 return 12;
>>>>         }
>>>> -       ret = list_subvols(fd, 0, 1, 0);
>>>> +
>>>> +       ret = btrfs_list_get_default_subvolume(fd, &default_id);
>>>> +       if (ret) {
>>>> +               fprintf(stderr, "ERROR: can't perform the search - %s\n",
>>>> +                       strerror(errno));
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       if (default_id == 0) {
>>>> +               fprintf(stderr, "ERROR: 'default' dir item not found\n");
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       /* no need to resolve roots if FS_TREE is default */
>>>> +       if (default_id == BTRFS_FS_TREE_OBJECTID) {
>>>> +               printf("ID 5 (FS_TREE)\n");
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       filter_set = btrfs_list_alloc_filter_set();
>>>> +       btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
>>>> +                               (void *)&default_id);
>>>> +
>>>> +       ret = btrfs_list_subvols(fd, filter_set, NULL);
>>>>         if (ret)
>>>>                 return 19;
>>>>         return 0;
>>>> @@ -585,7 +618,7 @@ static int cmd_find_new(int argc, char **argv)
>>>>                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
>>>>                 return 12;
>>>>         }
>>>> -       ret = find_updated_files(fd, 0, last_gen);
>>>> +       ret = btrfs_list_find_updated_files(fd, 0, last_gen);
>>>>         if (ret)
>>>>                 return 19;
>>>>         return 0;
>>>> diff --git a/send-utils.c b/send-utils.c
>>>> index 096fa02..fcde5c2 100644
>>>> --- a/send-utils.c
>>>> +++ b/send-utils.c
>>>> @@ -244,7 +244,8 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
>>>>                                 if (!root_item_valid)
>>>>                                         goto skip;
>>>>
>>>> -                               path = path_for_root(mnt_fd, sh->objectid);
>>>> +                               path = btrfs_list_path_for_root(mnt_fd,
>>>> +                                                               sh->objectid);
>>>>                                 if (!path)
>>>>                                         path = strdup("");
>>>>                                 if (IS_ERR(path)) {
>>>> --
>>>> 1.7.6.5
>>>>
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
>>>> the body of a message to majordomo@vger.kernel.org
>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>
>>
>>
> 
> Alex.
> 



^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH V3] Btrfs-progs: add parent uuid for snapshots
  2012-10-09 15:44     ` David Sterba
@ 2012-10-16  7:00       ` Anand jain
  2012-10-16  7:00         ` [PATCH] Btrfs-progs: make use of column_name Anand jain
  2012-10-16  7:00         ` [PATCH] Btrfs-progs: update man page for -u and -q option in subvol list Anand jain
  2012-10-16  7:03       ` [PATCH V2] Btrfs-progs: add parent uuid for snapshots Anand Jain
  1 sibling, 2 replies; 31+ messages in thread
From: Anand jain @ 2012-10-16  7:00 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Anand Jain

From: Anand Jain <anand.jain@oracle.com>

Signed-off-by: Anand Jain <anand.jain@oracle.com>
---
 btrfs-list.c     |   32 +++++++++++++++++++++++++++-----
 btrfs-list.h     |    1 +
 cmds-subvolume.c |    6 +++++-
 3 files changed, 33 insertions(+), 6 deletions(-)

diff --git a/btrfs-list.c b/btrfs-list.c
index e5f0f96..5314ced 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -80,6 +80,7 @@ struct root_info {
 	time_t otime;
 
 	u8 uuid[BTRFS_UUID_SIZE];
+	u8 puuid[BTRFS_UUID_SIZE];
 
 	/* 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.
@@ -128,6 +129,11 @@ struct {
 		.need_print	= 0,
 	},
 	{
+		.name		= "parent UUID",
+		.column_name	= "Parent UUID",
+		.need_print	= 0,
+	},
+	{
 		.name		= "uuid",
 		.column_name	= "UUID",
 		.need_print	= 0,
@@ -435,7 +441,7 @@ static struct root_info *root_tree_search(struct root_lookup *root_tree,
 static int update_root(struct root_lookup *root_lookup,
 		       u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
 		       u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
-		       time_t ot, void *uuid)
+		       time_t ot, void *uuid, void *puuid)
 {
 	struct root_info *ri;
 
@@ -472,6 +478,8 @@ static int update_root(struct root_lookup *root_lookup,
 		ri->otime = ot;
 	if (uuid)
 		memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
+	if (puuid)
+		memcpy(&ri->puuid, puuid, BTRFS_UUID_SIZE);
 
 	return 0;
 }
@@ -489,17 +497,18 @@ static int update_root(struct root_lookup *root_lookup,
  * gen: the current generation of the root
  * ot: the original time(create time) of the root
  * uuid: uuid of the root
+ * puuid: uuid of the root parent if any
  */
 static int add_root(struct root_lookup *root_lookup,
 		    u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
 		    u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
-		    time_t ot, void *uuid)
+		    time_t ot, void *uuid, void *puuid)
 {
 	struct root_info *ri;
 	int ret;
 
 	ret = update_root(root_lookup, root_id, ref_tree, root_offset, flags,
-			  dir_id, name, name_len, ogen, gen, ot, uuid);
+			  dir_id, name, name_len, ogen, gen, ot, uuid, puuid);
 	if (!ret)
 		return 0;
 
@@ -540,6 +549,9 @@ static int add_root(struct root_lookup *root_lookup,
 	if (uuid) 
 		memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
 
+	if (puuid)
+		memcpy(&ri->puuid, puuid, BTRFS_UUID_SIZE);
+
 	ret = root_tree_insert(root_lookup, ri);
 	if (ret) {
 		printf("failed to insert tree %llu\n", (unsigned long long)root_id);
@@ -1022,6 +1034,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 	int i;
 	time_t t;
 	u8 uuid[BTRFS_UUID_SIZE];
+	u8 puuid[BTRFS_UUID_SIZE];
 
 	root_lookup_init(root_lookup);
 	memset(&args, 0, sizeof(args));
@@ -1075,7 +1088,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 
 				add_root(root_lookup, sh->objectid, sh->offset,
 					 0, 0, dir_id, name, name_len, 0, 0, 0,
-					 NULL);
+					 NULL, NULL);
 			} else if (sh->type == BTRFS_ROOT_ITEM_KEY) {
 				ri = (struct btrfs_root_item *)(args.buf + off);
 				gen = btrfs_root_generation(ri);
@@ -1085,15 +1098,17 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 					t = ri->otime.sec;
 					ogen = btrfs_root_otransid(ri);
 					memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
+					memcpy(puuid, ri->parent_uuid, BTRFS_UUID_SIZE);
 				} else {
 					t = 0;
 					ogen = 0;
 					memset(uuid, 0, BTRFS_UUID_SIZE);
+					memset(puuid, 0, BTRFS_UUID_SIZE);
 				}
 
 				add_root(root_lookup, sh->objectid, 0,
 					 sh->offset, flags, 0, NULL, 0, ogen,
-					 gen, t, uuid);
+					 gen, t, uuid, puuid);
 			}
 
 			off += sh->len;
@@ -1347,6 +1362,13 @@ static void print_subvolume_column(struct root_info *subv,
 			uuid_unparse(subv->uuid, uuidparse);
 		printf("%s", uuidparse);
 		break;
+	case BTRFS_LIST_PUUID:
+		if (uuid_is_null(subv->puuid))
+			strcpy(uuidparse, "-");
+		else
+			uuid_unparse(subv->puuid, uuidparse);
+		printf("%s", uuidparse);
+		break;
 	case BTRFS_LIST_PATH:
 		BUG_ON(!subv->full_path);
 		printf("%s", subv->full_path);
diff --git a/btrfs-list.h b/btrfs-list.h
index cde4b3c..a32545f 100644
--- a/btrfs-list.h
+++ b/btrfs-list.h
@@ -53,6 +53,7 @@ enum btrfs_list_column_enum {
 	BTRFS_LIST_PARENT,
 	BTRFS_LIST_TOP_LEVEL,
 	BTRFS_LIST_OTIME,
+	BTRFS_LIST_PUUID,
 	BTRFS_LIST_UUID,
 	BTRFS_LIST_PATH,
 	BTRFS_LIST_ALL,
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index ac39f7b..427263a 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -279,6 +279,7 @@ static const char * const cmd_subvol_list_usage[] = {
 	"-p           print parent ID",
 	"-a           print all the subvolumes in the filesystem.",
 	"-u           print the uuid of subvolumes (and snapshots)",
+	"-q           print the parent uuid of snapshots",
 	"-t           print the result as a table",
 	"-s           list snapshots only in the filesystem",
 	"-r           list readonly subvolumes (including snapshots)",
@@ -318,7 +319,7 @@ static int cmd_subvol_list(int argc, char **argv)
 	optind = 1;
 	while(1) {
 		c = getopt_long(argc, argv,
-				    "apsurg:c:t", long_options, NULL);
+				    "apsuqrg:c:t", long_options, NULL);
 		if (c < 0)
 			break;
 
@@ -342,6 +343,9 @@ static int cmd_subvol_list(int argc, char **argv)
 		case 'u':
 			btrfs_list_setup_print_column(BTRFS_LIST_UUID);
 			break;
+		case 'q':
+			btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
+			break;
 		case 'r':
 			flags |= BTRFS_ROOT_SUBVOL_RDONLY;
 			break;
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH] Btrfs-progs: make use of column_name
  2012-10-16  7:00       ` [PATCH V3] " Anand jain
@ 2012-10-16  7:00         ` Anand jain
  2012-10-16  7:00         ` [PATCH] Btrfs-progs: update man page for -u and -q option in subvol list Anand jain
  1 sibling, 0 replies; 31+ messages in thread
From: Anand jain @ 2012-10-16  7:00 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Anand Jain

From: Anand Jain <anand.jain@oracle.com>

Signed-off-by: Anand Jain <anand.jain@oracle.com>
---
 btrfs-list.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/btrfs-list.c b/btrfs-list.c
index 74782b8..9cfdb35 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -1432,7 +1432,7 @@ static void print_all_volume_info_tab_head()
 
 	for (i = 0; i < BTRFS_LIST_ALL; i++) {
 		if (btrfs_list_columns[i].need_print)
-			printf("%s\t", btrfs_list_columns[i].name);
+			printf("%s\t", btrfs_list_columns[i].column_name);
 
 		if (i == BTRFS_LIST_ALL-1)
 			printf("\n");
@@ -1442,7 +1442,7 @@ static void print_all_volume_info_tab_head()
 		memset(barrier, 0, sizeof(barrier));
 
 		if (btrfs_list_columns[i].need_print) {
-			len = strlen(btrfs_list_columns[i].name);
+			len = strlen(btrfs_list_columns[i].column_name);
 			while (len--)
 				strcat(barrier, "-");
 
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH] Btrfs-progs: update man page for -u and -q option in subvol list
  2012-10-16  7:00       ` [PATCH V3] " Anand jain
  2012-10-16  7:00         ` [PATCH] Btrfs-progs: make use of column_name Anand jain
@ 2012-10-16  7:00         ` Anand jain
  1 sibling, 0 replies; 31+ messages in thread
From: Anand jain @ 2012-10-16  7:00 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Anand Jain

From: Anand Jain <anand.jain@oracle.com>

Signed-off-by: Anand Jain <anand.jain@oracle.com>
---
 man/btrfs.8.in |    8 ++++++--
 1 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/man/btrfs.8.in b/man/btrfs.8.in
index 57c25b0..91f2af2 100644
--- a/man/btrfs.8.in
+++ b/man/btrfs.8.in
@@ -11,7 +11,7 @@ btrfs \- control a btrfs filesystem
 .PP
 \fBbtrfs\fP \fBsubvolume create\fP\fI [<dest>/]<name>\fP
 .PP
-\fBbtrfs\fP \fBsubvolume list\fP\fI [-aprts] [-g [+|-]value] [-c [+|-]value] [--rootid=rootid,gen,ogen,path] <path>\fP
+\fBbtrfs\fP \fBsubvolume list\fP\fI [-apruqts] [-g [+|-]value] [-c [+|-]value] [--rootid=rootid,gen,ogen,path] <path>\fP
 .PP
 \fBbtrfs\fP \fBsubvolume set-default\fP\fI <id> <path>\fP
 .PP
@@ -110,7 +110,7 @@ Create a subvolume in \fI<dest>\fR (or in the current directory if
 \fI<dest>\fR is omitted).
 .TP
 
-\fBsubvolume list\fR\fI [-aprts][-g [+|-]value] [-c [+|-]value] [--sort=gen,ogen,rootid,path] <path>\fR
+\fBsubvolume list\fR\fI [-apruqts][-g [+|-]value] [-c [+|-]value] [--sort=gen,ogen,rootid,path] <path>\fR
 .RS
 List the subvolumes present in the filesystem \fI<path>\fR. For every
 subvolume the following information is shown by default.
@@ -132,6 +132,10 @@ and top level. The parent's ID may be used at mount time via the
 
 \fB-s\fP only snapshot subvolumes in the filesystem will  be listed.
 
+\fB-u\fP print UUID of the subvolume.
+
+\fB-q\fP print parent UUID of the subvolume.
+
 \fB-g [+|-]value\fP
 list subvolumes in the filesystem that its generation is
 >=, <= or = value. '+' means >= value, '-' means <= value, If there is
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH V2] Btrfs-progs: add parent uuid for snapshots
  2012-10-09 15:44     ` David Sterba
  2012-10-16  7:00       ` [PATCH V3] " Anand jain
@ 2012-10-16  7:03       ` Anand Jain
  1 sibling, 0 replies; 31+ messages in thread
From: Anand Jain @ 2012-10-16  7:03 UTC (permalink / raw)
  To: linux-btrfs



I agree. Thanks for the comments.
New patch has been sent out.

-Anand


On 09/10/12 23:44, David Sterba wrote:
> On Fri, Oct 05, 2012 at 10:25:22AM +0800, Anand jain wrote:
>> @@ -128,6 +129,11 @@ struct {
>>   		.need_print	= 0,
>>   	},
>>   	{
>> +		.name		= "puuid",
>> +		.column_name	= "PUUID",
>
> the capitalized 'P' looks like it's part of the UUID abbreviation. The
> UUIDs are long, I think you can print 'parent UUID' in the header, the
> name for command line argument 'puuid' is understable.
>
>> +		.need_print	= 0,
>> +	},
>> +	{
>>   		.name		= "uuid",
>>   		.column_name	= "UUID",
>>   		.need_print	= 0,
>
>> --- a/cmds-subvolume.c
>> +++ b/cmds-subvolume.c
>> @@ -267,6 +267,7 @@ static const char * const cmd_subvol_list_usage[] = {
>>   	"-p           print parent ID",
>>   	"-a           print all the subvolumes in the filesystem.",
>>   	"-u           print the uuid of subvolumes (and snapshots)",
>> +	"-P           print the parent uuid of snapshots",
>
> This clashes with my efforts to make the options consistent so that we
> can have a lowercase for column selection and uppercase for filter. In
> case of the parent UUID,  it makes sense to filter by it, eg when we
> have a hierarchy of subvolumes that keep the same structure but is
> replicated several times.
>
> I suggest to pick a different letter than 'P', say 'q'. (-q is usually
> used for 'no verbose output' in utilities, but it does not make much
> sense in context of 'subvol list' so I hope it's ok from the UI POV).
>
>>   	"-t           print the result as a table",
>>   	"-s           list snapshots only in the filesystem",
>>   	"-r           list readonly subvolumes (including snapshots)",
>
> Thanks,
> david
>

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2012-10-16  6:59 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-09-18 10:35 [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
2012-09-18 10:50 ` [PATCH V4 1/7] Btrfs-progs: move the function declarations to a new head file Miao Xie
2012-09-18 10:52 ` [PATCH V4 2/7] Btrfs-progs: fix compile warning of implicit declaration of "list_snapshots" Miao Xie
2012-09-18 10:55 ` [PATCH V4 3/7] Btrfs-progs: fix wrong usage of btrfs subvolume list command Miao Xie
2012-09-18 10:59 ` [PATCH V4 4/7] Btrfs-progs: fix wrong way to check if the root item contains otime and uuid Miao Xie
2012-09-19  1:55   ` Anand Jain
2012-09-19  2:58     ` Miao Xie
2012-09-18 11:06 ` [PATCH V4 5/7] Btrfs-progs: restructure list_subvolumes Miao Xie
2012-09-20 12:09   ` David Sterba
2012-10-09 16:05   ` Alex Lyakas
2012-10-10  2:12     ` Miao Xie
2012-10-10 19:45       ` Alex Lyakas
2012-10-15  4:06         ` Miao Xie
2012-09-18 11:09 ` [PATCH V4 6/7] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Miao Xie
2012-09-18 15:11   ` Martin Steigerwald
2012-09-18 11:12 ` [PATCH V4 7/7] Btrfs-progs: update the manpage entries for the btrfs subvolume list Miao Xie
2012-09-28 12:55 ` [PATCH] Btrfs-progs: make btrfs_list_setup_filter to modify a set filter Anand jain
2012-10-03  0:03 ` [PATCH V4 0/7 ] Btrfs-progs: enhance btrfs subvol list only to show read-only snapshots Chris Mason
2012-10-09  6:01   ` Miao Xie
2012-10-09 15:57     ` David Sterba
2012-10-04 10:12 ` [PATCH] Btrfs-progs: add parent uuid for snapshots Anand jain
2012-10-04 12:00   ` Dong Robin
2012-10-05  2:27     ` Anand Jain
2012-10-05  2:25 ` [PATCH] Btrfs-progs: Corrections and additions to the btrfs man page Anand jain
2012-10-05  2:25   ` [PATCH] Btrfs-progs: Update btrfs man page for -P option Anand jain
2012-10-05  2:25   ` [PATCH V2] Btrfs-progs: add parent uuid for snapshots Anand jain
2012-10-09 15:44     ` David Sterba
2012-10-16  7:00       ` [PATCH V3] " Anand jain
2012-10-16  7:00         ` [PATCH] Btrfs-progs: make use of column_name Anand jain
2012-10-16  7:00         ` [PATCH] Btrfs-progs: update man page for -u and -q option in subvol list Anand jain
2012-10-16  7:03       ` [PATCH V2] Btrfs-progs: add parent uuid for snapshots Anand Jain

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.