All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library"
@ 2018-01-26 18:40 Omar Sandoval
  2018-01-26 18:40 ` [PATCH 01/26] btrfs-progs: get rid of undocumented qgroup inheritance options Omar Sandoval
                   ` (26 more replies)
  0 siblings, 27 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

Hello,

One of the features requests I get most often is a library to do the
sorts of operations that we do with btrfs-progs. We can shell out to
btrfs-progs, but the output format isn't always easily parsasble, and
shelling out isn't always ideal. There's libbtrfs, but it's very
incomplete, doesn't have a well thought out API, and is licensed under
the GPL, making it hard to use for many projects.

libbtrfsutil is a new library written from scratch to address these
issues. The implementation is completely independent of the existing
btrfs-progs code, including kerncompat.h, and has a clean API and naming
scheme. It is licensed under the LGPL. It also includes Python bindings
by default. I will maintain the library code.

Patch 1 is a preparation cleanup which can go in independently. Patch 2
adds the build system stuff for the library, and patch 3 does the same
for the Python bindings. Patches 4-14 implement the library helpers,
currently subvolume helpers and the sync ioctls. Patches 15-26 replace
the btrfs-progs and libbtrfs code to use libbtrfsutil instead. I took
care to preserve backwards-compatibility. `btrfs subvol list` in
particular had some buggy behaviors for -o and -a that I emulated in the
new code, see the comments in the code.

These patches are also available on my GitHub:
https://github.com/osandov/btrfs-progs/tree/libbtrfsutil. That branch
will rebase as I update this series.

Please share feedback regarding the API, implementation, or anything
else.

Thanks!

Omar Sandoval (26):
  btrfs-progs: get rid of undocumented qgroup inheritance options
  Add libbtrfsutil
  libbtrfsutil: add Python bindings
  libbtrfsutil: add btrfs_util_is_subvolume() and
    btrfs_util_subvolume_id()
  libbtrfsutil: add qgroup inheritance helpers
  libbtrfsutil: add btrfs_util_create_subvolume()
  libbtrfsutil: add btrfs_util_subvolume_info()
  libbtrfsutil: add btrfs_util_[gs]et_read_only()
  libbtrfsutil: add btrfs_util_[gs]et_default_subvolume()
  libbtrfsutil: add subvolume iterator helpers
  libbtrfsutil: add btrfs_util_create_snapshot()
  libbtrfsutil: add btrfs_util_delete_subvolume()
  libbtrfsutil: add btrfs_util_deleted_subvolumes()
  libbtrfsutil: add filesystem sync helpers
  btrfs-progs: use libbtrfsutil for read-only property
  btrfs-progs: use libbtrfsutil for sync ioctls
  btrfs-progs: use libbtrfsutil for set-default
  btrfs-progs: use libbtrfsutil for get-default
  btrfs-progs: use libbtrfsutil for subvol create and snapshot
  btrfs-progs: use libbtrfsutil for subvol delete
  btrfs-progs: use libbtrfsutil for subvol show
  btrfs-progs: use libbtrfsutil for subvol sync
  btrfs-progs: replace test_issubvolume() with btrfs_util_is_subvolume()
  btrfs-progs: add recursive snapshot/delete using libbtrfsutil
  btrfs-progs: deprecate libbtrfs helpers with libbtrfsutil equivalents
  btrfs-progs: use libbtrfsutil for subvolume list

 .gitignore                                   |    2 +
 Documentation/btrfs-subvolume.asciidoc       |   14 +-
 INSTALL                                      |    4 +
 Makefile                                     |   84 +-
 Makefile.inc.in                              |    2 +
 btrfs-list.c                                 | 1201 +++++++---------------
 btrfs-list.h                                 |   22 +-
 cmds-filesystem.c                            |   20 +-
 cmds-inspect.c                               |    9 +-
 cmds-qgroup.c                                |   20 +-
 cmds-receive.c                               |   12 +-
 cmds-subvolume.c                             |  807 +++++----------
 configure.ac                                 |   15 +
 libbtrfsutil/COPYING                         |  674 +++++++++++++
 libbtrfsutil/COPYING.LESSER                  |  165 +++
 libbtrfsutil/README.md                       |   35 +
 libbtrfsutil/btrfsutil.h                     |  621 ++++++++++++
 libbtrfsutil/errors.c                        |   55 +
 libbtrfsutil/filesystem.c                    |  103 ++
 libbtrfsutil/internal.h                      |   36 +
 libbtrfsutil/python/.gitignore               |    7 +
 libbtrfsutil/python/btrfsutilpy.h            |   84 ++
 libbtrfsutil/python/error.c                  |  202 ++++
 libbtrfsutil/python/filesystem.c             |   94 ++
 libbtrfsutil/python/module.c                 |  321 ++++++
 libbtrfsutil/python/qgroup.c                 |  141 +++
 libbtrfsutil/python/setup.py                 |  103 ++
 libbtrfsutil/python/subvolume.c              |  665 +++++++++++++
 libbtrfsutil/python/tests/__init__.py        |   66 ++
 libbtrfsutil/python/tests/test_filesystem.py |   73 ++
 libbtrfsutil/python/tests/test_qgroup.py     |   57 ++
 libbtrfsutil/python/tests/test_subvolume.py  |  383 +++++++
 libbtrfsutil/qgroup.c                        |   86 ++
 libbtrfsutil/subvolume.c                     | 1383 ++++++++++++++++++++++++++
 messages.h                                   |   14 +
 props.c                                      |   69 +-
 qgroup.c                                     |  106 --
 qgroup.h                                     |    4 -
 send-utils.c                                 |   25 +-
 utils.c                                      |  152 +--
 utils.h                                      |    6 -
 41 files changed, 6188 insertions(+), 1754 deletions(-)
 create mode 100644 libbtrfsutil/COPYING
 create mode 100644 libbtrfsutil/COPYING.LESSER
 create mode 100644 libbtrfsutil/README.md
 create mode 100644 libbtrfsutil/btrfsutil.h
 create mode 100644 libbtrfsutil/errors.c
 create mode 100644 libbtrfsutil/filesystem.c
 create mode 100644 libbtrfsutil/internal.h
 create mode 100644 libbtrfsutil/python/.gitignore
 create mode 100644 libbtrfsutil/python/btrfsutilpy.h
 create mode 100644 libbtrfsutil/python/error.c
 create mode 100644 libbtrfsutil/python/filesystem.c
 create mode 100644 libbtrfsutil/python/module.c
 create mode 100644 libbtrfsutil/python/qgroup.c
 create mode 100755 libbtrfsutil/python/setup.py
 create mode 100644 libbtrfsutil/python/subvolume.c
 create mode 100644 libbtrfsutil/python/tests/__init__.py
 create mode 100644 libbtrfsutil/python/tests/test_filesystem.py
 create mode 100644 libbtrfsutil/python/tests/test_qgroup.py
 create mode 100644 libbtrfsutil/python/tests/test_subvolume.py
 create mode 100644 libbtrfsutil/qgroup.c
 create mode 100644 libbtrfsutil/subvolume.c

-- 
2.16.1


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

* [PATCH 01/26] btrfs-progs: get rid of undocumented qgroup inheritance options
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
@ 2018-01-26 18:40 ` Omar Sandoval
  2018-01-27  4:35   ` Qu Wenruo
  2018-01-26 18:40 ` [PATCH 02/26] Add libbtrfsutil Omar Sandoval
                   ` (25 subsequent siblings)
  26 siblings, 1 reply; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

The -c option to subvol create and the -c and -x options to subvol
snapshot have never been documented (and -x doesn't actually work
because it's not in the getopt option string). The functionality is
dubious and the kernel interface is being removed, so get rid of these.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 cmds-subvolume.c | 25 ++-----------------------
 qgroup.c         | 42 ------------------------------------------
 qgroup.h         |  2 --
 3 files changed, 2 insertions(+), 67 deletions(-)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index dc626a64..f8e34bc8 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -129,18 +129,11 @@ static int cmd_subvol_create(int argc, char **argv)
 	DIR	*dirstream = NULL;
 
 	while (1) {
-		int c = getopt(argc, argv, "c:i:");
+		int c = getopt(argc, argv, "i:");
 		if (c < 0)
 			break;
 
 		switch (c) {
-		case 'c':
-			res = qgroup_inherit_add_copy(&inherit, optarg, 0);
-			if (res) {
-				retval = res;
-				goto out;
-			}
-			break;
 		case 'i':
 			res = qgroup_inherit_add_group(&inherit, optarg);
 			if (res) {
@@ -665,18 +658,11 @@ static int cmd_subvol_snapshot(int argc, char **argv)
 
 	memset(&args, 0, sizeof(args));
 	while (1) {
-		int c = getopt(argc, argv, "c:i:r");
+		int c = getopt(argc, argv, "i:r");
 		if (c < 0)
 			break;
 
 		switch (c) {
-		case 'c':
-			res = qgroup_inherit_add_copy(&inherit, optarg, 0);
-			if (res) {
-				retval = res;
-				goto out;
-			}
-			break;
 		case 'i':
 			res = qgroup_inherit_add_group(&inherit, optarg);
 			if (res) {
@@ -687,13 +673,6 @@ static int cmd_subvol_snapshot(int argc, char **argv)
 		case 'r':
 			readonly = 1;
 			break;
-		case 'x':
-			res = qgroup_inherit_add_copy(&inherit, optarg, 1);
-			if (res) {
-				retval = res;
-				goto out;
-			}
-			break;
 		default:
 			usage(cmd_subvol_snapshot_usage);
 		}
diff --git a/qgroup.c b/qgroup.c
index 156825fd..e9158a26 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -1310,45 +1310,3 @@ int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg)
 
 	return 0;
 }
-
-int qgroup_inherit_add_copy(struct btrfs_qgroup_inherit **inherit, char *arg,
-			    int type)
-{
-	int ret;
-	u64 qgroup_src;
-	u64 qgroup_dst;
-	char *p;
-	int pos = 0;
-
-	p = strchr(arg, ':');
-	if (!p) {
-bad:
-		error("invalid copy specification, missing separator :");
-		return -EINVAL;
-	}
-	*p = 0;
-	qgroup_src = parse_qgroupid(arg);
-	qgroup_dst = parse_qgroupid(p + 1);
-	*p = ':';
-
-	if (!qgroup_src || !qgroup_dst)
-		goto bad;
-
-	if (*inherit)
-		pos = (*inherit)->num_qgroups +
-		      (*inherit)->num_ref_copies * 2 * type;
-
-	ret = qgroup_inherit_realloc(inherit, 2, pos);
-	if (ret)
-		return ret;
-
-	(*inherit)->qgroups[pos++] = qgroup_src;
-	(*inherit)->qgroups[pos++] = qgroup_dst;
-
-	if (!type)
-		++(*inherit)->num_ref_copies;
-	else
-		++(*inherit)->num_excl_copies;
-
-	return 0;
-}
diff --git a/qgroup.h b/qgroup.h
index 875fbdf3..bb6610d7 100644
--- a/qgroup.h
+++ b/qgroup.h
@@ -92,7 +92,5 @@ int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set **comp_set,
 				int is_descending);
 int qgroup_inherit_size(struct btrfs_qgroup_inherit *p);
 int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg);
-int qgroup_inherit_add_copy(struct btrfs_qgroup_inherit **inherit, char *arg,
-			    int type);
 
 #endif
-- 
2.16.1


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

* [PATCH 02/26] Add libbtrfsutil
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
  2018-01-26 18:40 ` [PATCH 01/26] btrfs-progs: get rid of undocumented qgroup inheritance options Omar Sandoval
@ 2018-01-26 18:40 ` Omar Sandoval
  2018-01-29  2:16   ` Qu Wenruo
  2018-01-26 18:40 ` [PATCH 03/26] libbtrfsutil: add Python bindings Omar Sandoval
                   ` (24 subsequent siblings)
  26 siblings, 1 reply; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

Currently, users wishing to manage Btrfs filesystems programatically
have to shell out to btrfs-progs and parse the output. This isn't ideal.
The goal of libbtrfsutil is to provide a library version of as many of
the operations of btrfs-progs as possible and to migrate btrfs-progs to
use it.

Rather than simply refactoring the existing btrfs-progs code, the code
has to be written from scratch for a couple of reasons:

* A lot of the btrfs-progs code was not designed with a nice library API
  in mind in terms of reusability, naming, and error reporting.
* libbtrfsutil is licensed under the LGPL, whereas btrfs-progs is under
  the GPL, which makes it dubious to directly copy or move the code.

Eventually, most of the low-level btrfs-progs code should either live in
libbtrfsutil or the shared kernel/userspace filesystem code, and
btrfs-progs will just be the CLI wrapper.

This first commit just includes the build system changes, license,
README, and error reporting helper.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 .gitignore                  |   2 +
 Makefile                    |  47 +--
 libbtrfsutil/COPYING        | 674 ++++++++++++++++++++++++++++++++++++++++++++
 libbtrfsutil/COPYING.LESSER | 165 +++++++++++
 libbtrfsutil/README.md      |  32 +++
 libbtrfsutil/btrfsutil.h    |  72 +++++
 libbtrfsutil/errors.c       |  55 ++++
 7 files changed, 1031 insertions(+), 16 deletions(-)
 create mode 100644 libbtrfsutil/COPYING
 create mode 100644 libbtrfsutil/COPYING.LESSER
 create mode 100644 libbtrfsutil/README.md
 create mode 100644 libbtrfsutil/btrfsutil.h
 create mode 100644 libbtrfsutil/errors.c

diff --git a/.gitignore b/.gitignore
index 8e607f6e..272d53e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,6 +43,8 @@ libbtrfs.so.0.1
 library-test
 library-test-static
 /fssum
+/libbtrfsutil.so*
+/libbtrfsutil.a
 
 /tests/*-tests-results.txt
 /tests/test-console.txt
diff --git a/Makefile b/Makefile
index 6369e8f4..062f7f3c 100644
--- a/Makefile
+++ b/Makefile
@@ -73,6 +73,7 @@ CFLAGS = $(SUBST_CFLAGS) \
 	 -fPIC \
 	 -I$(TOPDIR) \
 	 -I$(TOPDIR)/kernel-lib \
+	 -I$(TOPDIR)/libbtrfsutil \
 	 $(EXTRAWARN_CFLAGS) \
 	 $(DEBUG_CFLAGS_INTERNAL) \
 	 $(EXTRA_CFLAGS)
@@ -121,12 +122,14 @@ libbtrfs_headers = send-stream.h send-utils.h send.h kernel-lib/rbtree.h btrfs-l
 	       kernel-lib/crc32c.h kernel-lib/list.h kerncompat.h \
 	       kernel-lib/radix-tree.h kernel-lib/sizes.h kernel-lib/raid56.h \
 	       extent-cache.h extent_io.h ioctl.h ctree.h btrfsck.h version.h
+libbtrfsutil_version := 0.1
+libbtrfsutil_objects = libbtrfsutil/errors.o
 convert_objects = convert/main.o convert/common.o convert/source-fs.o \
 		  convert/source-ext2.o convert/source-reiserfs.o
 mkfs_objects = mkfs/main.o mkfs/common.o
 image_objects = image/main.o image/sanitize.o
 all_objects = $(objects) $(cmds_objects) $(libbtrfs_objects) $(convert_objects) \
-	      $(mkfs_objects) $(image_objects)
+	      $(mkfs_objects) $(image_objects) $(libbtrfsutil_objects)
 
 TESTS = fsck-tests.sh convert-tests.sh
 
@@ -248,10 +251,10 @@ static_convert_objects = $(patsubst %.o, %.static.o, $(convert_objects))
 static_mkfs_objects = $(patsubst %.o, %.static.o, $(mkfs_objects))
 static_image_objects = $(patsubst %.o, %.static.o, $(image_objects))
 
-libs_shared = libbtrfs.so.0.1
-libs_static = libbtrfs.a
+libs_shared = libbtrfs.so.0.1 libbtrfsutil.so.$(libbtrfsutil_version)
+libs_static = libbtrfs.a libbtrfsutil.a
 libs = $(libs_shared) $(libs_static)
-lib_links = libbtrfs.so.0 libbtrfs.so
+lib_links = libbtrfs.so.0 libbtrfs.so libbtrfsutil.so.0 libbtrfsutil.so
 headers = $(libbtrfs_headers)
 
 # make C=1 to enable sparse
@@ -289,7 +292,7 @@ endif
 	$(Q)$(CC) $(STATIC_CFLAGS) -c $< -o $@ $($(subst -,_,$(@:%.static.o=%)-cflags)) \
 		$($(subst -,_,btrfs-$(@:%/$(notdir $@)=%)-cflags))
 
-all: $(progs) libbtrfs $(BUILDDIRS)
+all: $(progs) $(libs) $(lib_links) $(BUILDDIRS)
 $(SUBDIRS): $(BUILDDIRS)
 $(BUILDDIRS):
 	@echo "Making all in $(patsubst build-%,%,$@)"
@@ -353,20 +356,31 @@ kernel-lib/tables.c:
 	@echo "    [TABLE]  $@"
 	$(Q)./mktables > $@ || ($(RM) -f $@ && exit 1)
 
-libbtrfs: $(libs_shared) $(lib_links)
+libbtrfs.so.0.1: $(libbtrfs_objects)
+	@echo "    [LD]     $@"
+	$(Q)$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LIBBTRFS_LIBS) \
+		-shared -Wl,-soname,libbtrfs.so.0 -o $@
+
+libbtrfs.a: $(libbtrfs_objects)
+	@echo "    [AR]     $@"
+	$(Q)$(AR) cr $@ $^
+
+libbtrfs.so.0 libbtrfs.so: libbtrfs.so.0.1
+	@echo "    [LN]     $@"
+	$(Q)$(LN_S) -f $< $@
 
-$(libs_shared): $(libbtrfs_objects) $(lib_links) send.h
+libbtrfsutil.so.$(libbtrfsutil_version): $(libbtrfsutil_objects)
 	@echo "    [LD]     $@"
-	$(Q)$(CC) $(CFLAGS) $(libbtrfs_objects) $(LDFLAGS) $(LIBBTRFS_LIBS) \
-		-shared -Wl,-soname,libbtrfs.so.0 -o libbtrfs.so.0.1
+	$(Q)$(CC) $(CFLAGS) $(libbtrfsutil_objects) \
+		-shared -Wl,-soname,libbtrfsutil.so.0 -o $@
 
-$(libs_static): $(libbtrfs_objects)
+libbtrfsutil.a: $(libbtrfsutil_objects)
 	@echo "    [AR]     $@"
-	$(Q)$(AR) cr libbtrfs.a $(libbtrfs_objects)
+	$(Q)$(AR) cr $@ $^
 
-$(lib_links):
+libbtrfsutil.so.0 libbtrfsutil.so: libbtrfsutil.so.$(libbtrfsutil_version)
 	@echo "    [LN]     $@"
-	$(Q)$(LN_S) -f libbtrfs.so.0.1 $@
+	$(Q)$(LN_S) -f $< $@
 
 # keep intermediate files from the below implicit rules around
 .PRECIOUS: $(addsuffix .o,$(progs))
@@ -485,7 +499,7 @@ test-ioctl: ioctl-test ioctl-test-32 ioctl-test-64
 	$(Q)./ioctl-test-32 > ioctl-test-32.log
 	$(Q)./ioctl-test-64 > ioctl-test-64.log
 
-library-test: library-test.c $(libs_shared)
+library-test: library-test.c libbtrfs.so
 	@echo "    [TEST PREP]  $@"$(eval TMPD=$(shell mktemp -d))
 	$(Q)mkdir -p $(TMPD)/include/btrfs && \
 	cp $(libbtrfs_headers) $(TMPD)/include/btrfs && \
@@ -549,7 +563,8 @@ clean: $(CLEANDIRS)
               mktables btrfs.static mkfs.btrfs.static fssum \
 	      $(check_defs) \
 	      $(libs) $(lib_links) \
-	      $(progs_static) $(progs_extra)
+	      $(progs_static) $(progs_extra) \
+	      libbtrfsutil/*.o libbtrfsutil/*.o.d
 
 clean-doc:
 	@echo "Cleaning Documentation"
@@ -568,7 +583,7 @@ $(CLEANDIRS):
 	@echo "Cleaning $(patsubst clean-%,%,$@)"
 	$(Q)$(MAKE) $(MAKEOPTS) -C $(patsubst clean-%,%,$@) clean
 
-install: $(libs) $(progs_install) $(INSTALLDIRS)
+install: $(libs) $(libbtrfsutil_links) $(progs_install) $(INSTALLDIRS)
 	$(INSTALL) -m755 -d $(DESTDIR)$(bindir)
 	$(INSTALL) $(progs_install) $(DESTDIR)$(bindir)
 	$(INSTALL) fsck.btrfs $(DESTDIR)$(bindir)
diff --git a/libbtrfsutil/COPYING b/libbtrfsutil/COPYING
new file mode 100644
index 00000000..f288702d
--- /dev/null
+++ b/libbtrfsutil/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    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, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/libbtrfsutil/COPYING.LESSER b/libbtrfsutil/COPYING.LESSER
new file mode 100644
index 00000000..0a041280
--- /dev/null
+++ b/libbtrfsutil/COPYING.LESSER
@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/libbtrfsutil/README.md b/libbtrfsutil/README.md
new file mode 100644
index 00000000..2bc9a0a7
--- /dev/null
+++ b/libbtrfsutil/README.md
@@ -0,0 +1,32 @@
+libbtrfsutil
+============
+
+libbtrfsutil is a library for managing Btrfs filesystems. It is licensed under
+the LGPL. libbtrfsutil provides interfaces for a subset of the operations
+offered by the `btrfs` command line utility.
+
+Development
+-----------
+
+The [development process for btrfs-progs](../README.md#development) applies.
+
+libbtrfsutil only includes operations that are done through the filesystem and
+ioctl interface, not operations that modify the filesystem directly (e.g., mkfs
+or fsck). This is by design but also a legal necessity, as the filesystem
+implementation is GPL but libbtrfsutil is LGPL. That is also why the
+libbtrfsutil code is a reimplementation of the btrfs-progs code rather than a
+refactoring. Be wary of this when porting functionality.
+
+A few guidelines:
+
+* All interfaces must be documented in `btrfsutil.h` using the kernel-doc style
+* Error codes should be specific about what _exactly_ failed
+* Functions should have a path and an fd variant whenever possible
+* Spell out terms in function names, etc. rather than abbreviating whenever
+  possible
+* Don't require the Btrfs UAPI headers for any interfaces (e.g., instead of
+  directly exposing a type from `linux/btrfs_tree.h`, abstract it away in a
+  type specific to `libbtrfsutil`)
+* Preserve API and ABI compatability at all times
+* Bump the libbtrfsutil version once for every release where the library
+  changes (this is mostly on the maintainers)
diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
new file mode 100644
index 00000000..fe1091ca
--- /dev/null
+++ b/libbtrfsutil/btrfsutil.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BTRFS_UTIL_H
+#define BTRFS_UTIL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * enum btrfs_util_error - libbtrfsutil error codes.
+ *
+ * All functions in libbtrfsutil that can return an error return this type and
+ * set errno.
+ */
+enum btrfs_util_error {
+	BTRFS_UTIL_OK,
+	BTRFS_UTIL_ERROR_STOP_ITERATION,
+	BTRFS_UTIL_ERROR_NO_MEMORY,
+	BTRFS_UTIL_ERROR_INVALID_ARGUMENT,
+	BTRFS_UTIL_ERROR_NOT_BTRFS,
+	BTRFS_UTIL_ERROR_NOT_SUBVOLUME,
+	BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND,
+	BTRFS_UTIL_ERROR_OPEN_FAILED,
+	BTRFS_UTIL_ERROR_RMDIR_FAILED,
+	BTRFS_UTIL_ERROR_UNLINK_FAILED,
+	BTRFS_UTIL_ERROR_STAT_FAILED,
+	BTRFS_UTIL_ERROR_STATFS_FAILED,
+	BTRFS_UTIL_ERROR_SEARCH_FAILED,
+	BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED,
+	BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED,
+	BTRFS_UTIL_ERROR_SUBVOL_SETFLAGS_FAILED,
+	BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED,
+	BTRFS_UTIL_ERROR_SNAP_CREATE_FAILED,
+	BTRFS_UTIL_ERROR_SNAP_DESTROY_FAILED,
+	BTRFS_UTIL_ERROR_DEFAULT_SUBVOL_FAILED,
+	BTRFS_UTIL_ERROR_SYNC_FAILED,
+	BTRFS_UTIL_ERROR_START_SYNC_FAILED,
+	BTRFS_UTIL_ERROR_WAIT_SYNC_FAILED,
+};
+
+/**
+ * btrfs_util_strerror() - Convert a libtrfsutil error code to a string
+ * description.
+ * @err: The error to convert.
+ *
+ * Return: Error description.
+ */
+const char *btrfs_util_strerror(enum btrfs_util_error err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BTRFS_UTIL_H */
diff --git a/libbtrfsutil/errors.c b/libbtrfsutil/errors.c
new file mode 100644
index 00000000..f695c541
--- /dev/null
+++ b/libbtrfsutil/errors.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stddef.h>
+#include "btrfsutil.h"
+
+static const char * const error_messages[] = {
+	[BTRFS_UTIL_OK] = "Success",
+	[BTRFS_UTIL_ERROR_STOP_ITERATION] = "Stop iteration",
+	[BTRFS_UTIL_ERROR_NO_MEMORY] = "Cannot allocate memory",
+	[BTRFS_UTIL_ERROR_INVALID_ARGUMENT] = "Invalid argument",
+	[BTRFS_UTIL_ERROR_NOT_BTRFS] = "Not a Btrfs filesystem",
+	[BTRFS_UTIL_ERROR_NOT_SUBVOLUME] = "Not a Btrfs subvolume",
+	[BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND] = "Subvolume not found",
+	[BTRFS_UTIL_ERROR_OPEN_FAILED] = "Could not open",
+	[BTRFS_UTIL_ERROR_RMDIR_FAILED] = "Could not rmdir",
+	[BTRFS_UTIL_ERROR_UNLINK_FAILED] = "Could not unlink",
+	[BTRFS_UTIL_ERROR_STAT_FAILED] = "Could not stat",
+	[BTRFS_UTIL_ERROR_STATFS_FAILED] = "Could not statfs",
+	[BTRFS_UTIL_ERROR_SEARCH_FAILED] = "Could not search B-tree",
+	[BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED] = "Could not lookup inode",
+	[BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED] = "Could not get subvolume flags",
+	[BTRFS_UTIL_ERROR_SUBVOL_SETFLAGS_FAILED] = "Could not set subvolume flags",
+	[BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED] = "Could not create subvolume",
+	[BTRFS_UTIL_ERROR_SNAP_CREATE_FAILED] = "Could not create snapshot",
+	[BTRFS_UTIL_ERROR_SNAP_DESTROY_FAILED] = "Could not destroy subvolume/snapshot",
+	[BTRFS_UTIL_ERROR_DEFAULT_SUBVOL_FAILED] = "Could not set default subvolume",
+	[BTRFS_UTIL_ERROR_SYNC_FAILED] = "Could not sync filesystem",
+	[BTRFS_UTIL_ERROR_START_SYNC_FAILED] = "Could not start filesystem sync",
+	[BTRFS_UTIL_ERROR_WAIT_SYNC_FAILED] = "Could not wait for filesystem sync",
+};
+
+__attribute__((visibility("default")))
+const char *btrfs_util_strerror(enum btrfs_util_error err)
+{
+	if (err < 0 || err >= sizeof(error_messages) / sizeof(error_messages[0]))
+		return NULL;
+	return error_messages[err];
+}
-- 
2.16.1


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

* [PATCH 03/26] libbtrfsutil: add Python bindings
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
  2018-01-26 18:40 ` [PATCH 01/26] btrfs-progs: get rid of undocumented qgroup inheritance options Omar Sandoval
  2018-01-26 18:40 ` [PATCH 02/26] Add libbtrfsutil Omar Sandoval
@ 2018-01-26 18:40 ` Omar Sandoval
  2018-01-26 18:40 ` [PATCH 04/26] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id() Omar Sandoval
                   ` (23 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

The C libbtrfsutil library isn't very useful for scripting, so we also
want bindings for Python. Writing unit tests in Python is also much
easier than doing so in C. Only Python 3 is supported; if someone really
wants Python 2 support, they can write their own bindings. This commit
is just the scaffolding.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 INSTALL                               |   4 +
 Makefile                              |  36 ++++++
 Makefile.inc.in                       |   2 +
 configure.ac                          |  15 +++
 libbtrfsutil/README.md                |   5 +-
 libbtrfsutil/python/.gitignore        |   7 ++
 libbtrfsutil/python/btrfsutilpy.h     |  57 ++++++++++
 libbtrfsutil/python/error.c           | 202 ++++++++++++++++++++++++++++++++++
 libbtrfsutil/python/module.c          | 166 ++++++++++++++++++++++++++++
 libbtrfsutil/python/setup.py          | 100 +++++++++++++++++
 libbtrfsutil/python/tests/__init__.py |   0
 11 files changed, 593 insertions(+), 1 deletion(-)
 create mode 100644 libbtrfsutil/python/.gitignore
 create mode 100644 libbtrfsutil/python/btrfsutilpy.h
 create mode 100644 libbtrfsutil/python/error.c
 create mode 100644 libbtrfsutil/python/module.c
 create mode 100755 libbtrfsutil/python/setup.py
 create mode 100644 libbtrfsutil/python/tests/__init__.py

diff --git a/INSTALL b/INSTALL
index 819b92ea..24d6e24f 100644
--- a/INSTALL
+++ b/INSTALL
@@ -41,6 +41,10 @@ To build from the released tarballs:
     $ make
     $ make install
 
+To install the libbtrfsutil Python bindings:
+
+    $ make install_python
+
 You may disable building some parts like documentation, btrfs-convert or
 backtrace support. See ./configure --help for more.
 
diff --git a/Makefile b/Makefile
index 062f7f3c..02b03e81 100644
--- a/Makefile
+++ b/Makefile
@@ -144,8 +144,10 @@ endif
 
 ifeq ($(BUILD_VERBOSE),1)
   Q =
+  SETUP_PY_Q =
 else
   Q = @
+  SETUP_PY_Q = -q
 endif
 
 ifeq ("$(origin D)", "command line")
@@ -293,6 +295,9 @@ endif
 		$($(subst -,_,btrfs-$(@:%/$(notdir $@)=%)-cflags))
 
 all: $(progs) $(libs) $(lib_links) $(BUILDDIRS)
+ifeq ($(PYTHON_BINDINGS),1)
+all: libbtrfsutil_python
+endif
 $(SUBDIRS): $(BUILDDIRS)
 $(BUILDDIRS):
 	@echo "Making all in $(patsubst build-%,%,$@)"
@@ -336,6 +341,16 @@ test-inst: all
 
 test: test-fsck test-mkfs test-convert test-misc test-fuzz test-cli
 
+ifeq ($(PYTHON_BINDINGS),1)
+test-libbtrfsutil: libbtrfsutil_python
+	$(Q)cd libbtrfsutil/python; \
+		LD_LIBRARY_PATH=../.. $(PYTHON) -m unittest discover -v tests
+
+.PHONY: test-libbtrfsutil
+
+test: test-libbtrfsutil
+endif
+
 #
 # NOTE: For static compiles, you need to have all the required libs
 # 	static equivalent available
@@ -382,6 +397,15 @@ libbtrfsutil.so.0 libbtrfsutil.so: libbtrfsutil.so.$(libbtrfsutil_version)
 	@echo "    [LN]     $@"
 	$(Q)$(LN_S) -f $< $@
 
+ifeq ($(PYTHON_BINDINGS),1)
+libbtrfsutil_python: libbtrfsutil.so
+	@echo "    [PY]     libbtrfsutil"
+	$(Q)cd libbtrfsutil/python; \
+		CFLAGS= LDFLAGS= $(PYTHON) setup.py $(SETUP_PY_Q) build_ext -i build
+
+.PHONY: libbtrfsutil_python
+endif
+
 # keep intermediate files from the below implicit rules around
 .PRECIOUS: $(addsuffix .o,$(progs))
 
@@ -565,6 +589,10 @@ clean: $(CLEANDIRS)
 	      $(libs) $(lib_links) \
 	      $(progs_static) $(progs_extra) \
 	      libbtrfsutil/*.o libbtrfsutil/*.o.d
+ifeq ($(PYTHON_BINDINGS),1)
+	$(Q)cd libbtrfsutil/python; \
+		$(PYTHON) setup.py $(SETUP_PY_Q) clean -a
+endif
 
 clean-doc:
 	@echo "Cleaning Documentation"
@@ -599,6 +627,14 @@ ifneq ($(udevdir),)
 	$(INSTALL) -m644 $(udev_rules) $(DESTDIR)$(udevruledir)
 endif
 
+ifeq ($(PYTHON_BINDINGS),1)
+install_python: libbtrfsutil_python
+	$(Q)cd libbtrfsutil/python; \
+		$(PYTHON) setup.py install --skip-build $(if $(DESTDIR),--root $(DESTDIR)) --prefix $(prefix)
+
+.PHONY: install_python
+endif
+
 install-static: $(progs_static) $(INSTALLDIRS)
 	$(INSTALL) -m755 -d $(DESTDIR)$(bindir)
 	$(INSTALL) $(progs_static) $(DESTDIR)$(bindir)
diff --git a/Makefile.inc.in b/Makefile.inc.in
index 56271903..408e00d2 100644
--- a/Makefile.inc.in
+++ b/Makefile.inc.in
@@ -14,6 +14,8 @@ DISABLE_BTRFSCONVERT = @DISABLE_BTRFSCONVERT@
 BTRFSCONVERT_EXT2 = @BTRFSCONVERT_EXT2@
 BTRFSCONVERT_REISERFS = @BTRFSCONVERT_REISERFS@
 BTRFSRESTORE_ZSTD = @BTRFSRESTORE_ZSTD@
+PYTHON_BINDINGS = @PYTHON_BINDINGS@
+PYTHON = @PYTHON@
 
 SUBST_CFLAGS = @CFLAGS@
 SUBST_LDFLAGS = @LDFLAGS@
diff --git a/configure.ac b/configure.ac
index 290dc1d5..5e2905d5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -199,6 +199,19 @@ fi
 AS_IF([test "x$enable_zstd" = xyes], [BTRFSRESTORE_ZSTD=1], [BTRFSRESTORE_ZSTD=0])
 AC_SUBST(BTRFSRESTORE_ZSTD)
 
+AC_ARG_ENABLE([python],
+	AS_HELP_STRING([--disable-python], [do not build libbtrfsutil Python bindings]),
+	[], [enable_python=yes]
+)
+
+if test "x$enable_python" = xyes; then
+	AM_PATH_PYTHON([3.4])
+fi
+
+AS_IF([test "x$enable_python" = xyes], [PYTHON_BINDINGS=1], [PYTHON_BINDINGS=0])
+AC_SUBST(PYTHON_BINDINGS)
+AC_SUBST(PYTHON)
+
 # udev v190 introduced the btrfs builtin and a udev rule to use it.
 # Our udev rule gives us the friendly dm names but isn't required (or valid)
 # on earlier releases.
@@ -253,6 +266,8 @@ AC_MSG_RESULT([
 	backtrace support:  ${enable_backtrace}
 	btrfs-convert:      ${enable_convert} ${convertfs:+($convertfs)}
 	btrfs-restore zstd: ${enable_zstd}
+	Python bindings:    ${enable_python}
+	Python interpreter: ${PYTHON}
 
 	Type 'make' to compile.
 ])
diff --git a/libbtrfsutil/README.md b/libbtrfsutil/README.md
index 2bc9a0a7..eed8102d 100644
--- a/libbtrfsutil/README.md
+++ b/libbtrfsutil/README.md
@@ -3,7 +3,8 @@ libbtrfsutil
 
 libbtrfsutil is a library for managing Btrfs filesystems. It is licensed under
 the LGPL. libbtrfsutil provides interfaces for a subset of the operations
-offered by the `btrfs` command line utility.
+offered by the `btrfs` command line utility. It also includes official Python
+bindings (Python 3 only).
 
 Development
 -----------
@@ -27,6 +28,8 @@ A few guidelines:
 * Don't require the Btrfs UAPI headers for any interfaces (e.g., instead of
   directly exposing a type from `linux/btrfs_tree.h`, abstract it away in a
   type specific to `libbtrfsutil`)
+* Include Python bindings for all interfaces
+* Write tests for all interfaces
 * Preserve API and ABI compatability at all times
 * Bump the libbtrfsutil version once for every release where the library
   changes (this is mostly on the maintainers)
diff --git a/libbtrfsutil/python/.gitignore b/libbtrfsutil/python/.gitignore
new file mode 100644
index 00000000..d050ff7c
--- /dev/null
+++ b/libbtrfsutil/python/.gitignore
@@ -0,0 +1,7 @@
+__pycache__
+*.pyc
+/btrfsutil.egg-info
+/btrfsutil*.so
+/build
+/constants.c
+/dist
diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
new file mode 100644
index 00000000..6d82f7e1
--- /dev/null
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BTRFSUTILPY_H
+#define BTRFSUTILPY_H
+
+#define PY_SSIZE_T_CLEAN
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <Python.h>
+#include "structmember.h"
+
+#include <btrfsutil.h>
+
+extern PyTypeObject BtrfsUtilError_type;
+
+/*
+ * Helpers for path arguments based on posixmodule.c in CPython.
+ */
+struct path_arg {
+	bool allow_fd;
+	char *path;
+	int fd;
+	Py_ssize_t length;
+	PyObject *object;
+	PyObject *cleanup;
+};
+int path_converter(PyObject *o, void *p);
+void path_cleanup(struct path_arg *path);
+
+void SetFromBtrfsUtilError(enum btrfs_util_error err);
+void SetFromBtrfsUtilErrorWithPath(enum btrfs_util_error err,
+				   struct path_arg *path);
+void SetFromBtrfsUtilErrorWithPaths(enum btrfs_util_error err,
+				    struct path_arg *path1,
+				    struct path_arg *path2);
+
+void add_module_constants(PyObject *m);
+
+#endif /* BTRFSUTILPY_H */
diff --git a/libbtrfsutil/python/error.c b/libbtrfsutil/python/error.c
new file mode 100644
index 00000000..0876c9b4
--- /dev/null
+++ b/libbtrfsutil/python/error.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "btrfsutilpy.h"
+
+typedef struct {
+	PyOSErrorObject os_error;
+	PyObject *btrfsutilerror;
+} BtrfsUtilError;
+
+void SetFromBtrfsUtilError(enum btrfs_util_error err)
+{
+	SetFromBtrfsUtilErrorWithPaths(err, NULL, NULL);
+}
+
+void SetFromBtrfsUtilErrorWithPath(enum btrfs_util_error err,
+				   struct path_arg *path1)
+{
+	SetFromBtrfsUtilErrorWithPaths(err, path1, NULL);
+}
+
+void SetFromBtrfsUtilErrorWithPaths(enum btrfs_util_error err,
+				    struct path_arg *path1,
+				    struct path_arg *path2)
+{
+	PyObject *strobj, *args, *exc;
+	int i = errno;
+	const char *str1 = btrfs_util_strerror(err), *str2 = strerror(i);
+
+	if (str1 && str2 && strcmp(str1, str2) != 0) {
+		strobj = PyUnicode_FromFormat("%s: %s", str1, str2);
+	} else if (str1) {
+		strobj = PyUnicode_FromString(str1);
+	} else if (str2) {
+		strobj = PyUnicode_FromString(str2);
+	} else {
+		Py_INCREF(Py_None);
+		strobj = Py_None;
+	}
+	if (strobj == NULL)
+		return;
+
+	args = Py_BuildValue("iOOOOi", i, strobj,
+			     path1 ? path1->object : Py_None, Py_None,
+			     path2 ? path2->object : Py_None, (int)err);
+	Py_DECREF(strobj);
+	if (args == NULL)
+		return;
+
+	exc = PyObject_CallObject((PyObject *)&BtrfsUtilError_type, args);
+	Py_DECREF(args);
+	if (exc == NULL)
+		return;
+
+	PyErr_SetObject((PyObject *)&BtrfsUtilError_type, exc);
+	Py_DECREF(exc);
+}
+
+static int BtrfsUtilError_clear(BtrfsUtilError *self)
+{
+	Py_CLEAR(self->btrfsutilerror);
+	return Py_TYPE(self)->tp_base->tp_clear((PyObject *)self);
+}
+
+static void BtrfsUtilError_dealloc(BtrfsUtilError *self)
+{
+	PyObject_GC_UnTrack(self);
+	BtrfsUtilError_clear(self);
+	Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static int BtrfsUtilError_traverse(BtrfsUtilError *self, visitproc visit,
+				   void *arg)
+{
+	Py_VISIT(self->btrfsutilerror);
+	return Py_TYPE(self)->tp_base->tp_traverse((PyObject *)self, visit, arg);
+}
+
+static PyObject *BtrfsUtilError_new(PyTypeObject *type, PyObject *args,
+				    PyObject *kwds)
+{
+	BtrfsUtilError *self;
+	PyObject *oserror_args = args;
+
+	if (PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 6) {
+		oserror_args = PyTuple_GetSlice(args, 0, 5);
+		if (oserror_args == NULL)
+			return NULL;
+	}
+
+	self = (BtrfsUtilError *)type->tp_base->tp_new(type, oserror_args,
+						       kwds);
+	if (oserror_args != args)
+		Py_DECREF(oserror_args);
+	if (self == NULL)
+		return NULL;
+
+	if (PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 6) {
+		self->btrfsutilerror = PyTuple_GET_ITEM(args, 5);
+		Py_INCREF(self->btrfsutilerror);
+	}
+
+	return (PyObject *)self;
+}
+
+static PyObject *BtrfsUtilError_str(BtrfsUtilError *self)
+{
+#define OR_NONE(x) ((x) ? (x) : Py_None)
+	if (self->btrfsutilerror) {
+		if (self->os_error.filename) {
+			if (self->os_error.filename2) {
+				return PyUnicode_FromFormat("[BtrfsUtilError %S Errno %S] %S: %R -> %R",
+							    OR_NONE(self->btrfsutilerror),
+							    OR_NONE(self->os_error.myerrno),
+							    OR_NONE(self->os_error.strerror),
+							    self->os_error.filename,
+							    self->os_error.filename2);
+			} else {
+				return PyUnicode_FromFormat("[BtrfsUtilError %S Errno %S] %S: %R",
+							    OR_NONE(self->btrfsutilerror),
+							    OR_NONE(self->os_error.myerrno),
+							    OR_NONE(self->os_error.strerror),
+							    self->os_error.filename);
+			}
+		}
+		if (self->os_error.myerrno && self->os_error.strerror) {
+			return PyUnicode_FromFormat("[BtrfsUtilError %S Errno %S] %S",
+						    self->btrfsutilerror,
+						    self->os_error.myerrno,
+						    self->os_error.strerror);
+		}
+	}
+	return Py_TYPE(self)->tp_base->tp_str((PyObject *)self);
+#undef OR_NONE
+}
+
+static PyMemberDef BtrfsUtilError_members[] = {
+	{"btrfsutilerror", T_OBJECT,
+	 offsetof(BtrfsUtilError, btrfsutilerror), 0,
+	 "btrfsutil error code"},
+	{},
+};
+
+#define BtrfsUtilError_DOC	\
+	"Btrfs operation error."
+
+PyTypeObject BtrfsUtilError_type = {
+	PyVarObject_HEAD_INIT(NULL, 0)
+	"btrfsutil.BtrfsUtilError",			/* tp_name */
+	sizeof(BtrfsUtilError),				/* tp_basicsize */
+	0,						/* tp_itemsize */
+	(destructor)BtrfsUtilError_dealloc,		/* tp_dealloc */
+	NULL,						/* tp_print */
+	NULL,						/* tp_getattr */
+	NULL,						/* tp_setattr */
+	NULL,						/* tp_as_async */
+	NULL,						/* tp_repr */
+	NULL,						/* tp_as_number */
+	NULL,						/* tp_as_sequence */
+	NULL,						/* tp_as_mapping */
+	NULL,						/* tp_hash  */
+	NULL,						/* tp_call */
+	(reprfunc)BtrfsUtilError_str,			/* tp_str */
+	NULL,						/* tp_getattro */
+	NULL,						/* tp_setattro */
+	NULL,						/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,	/* tp_flags */
+	BtrfsUtilError_DOC,				/* tp_doc */
+	(traverseproc)BtrfsUtilError_traverse,		/* tp_traverse */
+	(inquiry)BtrfsUtilError_clear,			/* tp_clear */
+	NULL,						/* tp_richcompare */
+	0,						/* tp_weaklistoffset */
+	NULL,						/* tp_iter */
+	NULL,						/* tp_iternext */
+	NULL,						/* tp_methods */
+	BtrfsUtilError_members,				/* tp_members */
+	NULL,						/* tp_getset */
+	NULL,						/* tp_base */
+	NULL,						/* tp_dict */
+	NULL,						/* tp_descr_get */
+	NULL,						/* tp_descr_set */
+	offsetof(BtrfsUtilError, os_error.dict),	/* tp_dictoffset */
+	NULL,						/* tp_init */
+	NULL,						/* tp_alloc */
+	BtrfsUtilError_new,				/* tp_new */
+};
diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
new file mode 100644
index 00000000..d7398808
--- /dev/null
+++ b/libbtrfsutil/python/module.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "btrfsutilpy.h"
+
+static int fd_converter(PyObject *o, void *p)
+{
+	int *fd = p;
+	long tmp;
+	int overflow;
+
+	tmp = PyLong_AsLongAndOverflow(o, &overflow);
+	if (tmp == -1 && PyErr_Occurred())
+		return 0;
+	if (overflow > 0 || tmp > INT_MAX) {
+		PyErr_SetString(PyExc_OverflowError,
+				"fd is greater than maximum");
+		return 0;
+	}
+	if (overflow < 0 || tmp < 0) {
+		PyErr_SetString(PyExc_ValueError, "fd is negative");
+		return 0;
+	}
+	*fd = tmp;
+	return 1;
+}
+
+int path_converter(PyObject *o, void *p)
+{
+	struct path_arg *path = p;
+	int is_index, is_bytes, is_unicode;
+	PyObject *bytes = NULL;
+	Py_ssize_t length = 0;
+	char *tmp;
+
+	if (o == NULL) {
+		path_cleanup(p);
+		return 1;
+	}
+
+	path->object = path->cleanup = NULL;
+	Py_INCREF(o);
+
+	path->fd = -1;
+
+	is_index = path->allow_fd && PyIndex_Check(o);
+	is_bytes = PyBytes_Check(o);
+	is_unicode = PyUnicode_Check(o);
+
+	if (!is_index && !is_bytes && !is_unicode) {
+		_Py_IDENTIFIER(__fspath__);
+		PyObject *func;
+
+		func = _PyObject_LookupSpecial(o, &PyId___fspath__);
+		if (func == NULL)
+			goto err_format;
+		Py_DECREF(o);
+		o = PyObject_CallFunctionObjArgs(func, NULL);
+		Py_DECREF(func);
+		if (o == NULL)
+			return 0;
+		is_bytes = PyBytes_Check(o);
+		is_unicode = PyUnicode_Check(o);
+	}
+
+	if (is_unicode) {
+		if (!PyUnicode_FSConverter(o, &bytes))
+			goto err;
+	} else if (is_bytes) {
+		bytes = o;
+		Py_INCREF(bytes);
+	} else if (is_index) {
+		if (!fd_converter(o, &path->fd))
+			goto err;
+		path->path = NULL;
+		goto out;
+	} else {
+err_format:
+		PyErr_Format(PyExc_TypeError, "expected %s, not %s",
+			     path->allow_fd ? "string, bytes, os.PathLike, or integer" :
+			     "string, bytes, or os.PathLike",
+			     Py_TYPE(o)->tp_name);
+		goto err;
+	}
+
+	length = PyBytes_GET_SIZE(bytes);
+	tmp = PyBytes_AS_STRING(bytes);
+	if ((size_t)length != strlen(tmp)) {
+		PyErr_SetString(PyExc_TypeError,
+				"path has embedded nul character");
+		goto err;
+	}
+
+	path->path = tmp;
+	if (bytes == o)
+		Py_DECREF(bytes);
+	else
+		path->cleanup = bytes;
+	path->fd = -1;
+
+out:
+	path->length = length;
+	path->object = o;
+	return Py_CLEANUP_SUPPORTED;
+
+err:
+	Py_XDECREF(o);
+	Py_XDECREF(bytes);
+	return 0;
+}
+
+void path_cleanup(struct path_arg *path)
+{
+	Py_CLEAR(path->object);
+	Py_CLEAR(path->cleanup);
+}
+
+static PyMethodDef btrfsutil_methods[] = {
+	{},
+};
+
+static struct PyModuleDef btrfsutilmodule = {
+	PyModuleDef_HEAD_INIT,
+	"btrfsutil",
+	"Library for managing Btrfs filesystems",
+	-1,
+	btrfsutil_methods,
+};
+
+PyMODINIT_FUNC
+PyInit_btrfsutil(void)
+{
+	PyObject *m;
+
+	BtrfsUtilError_type.tp_base = (PyTypeObject *)PyExc_OSError;
+	if (PyType_Ready(&BtrfsUtilError_type) < 0)
+		return NULL;
+
+	m = PyModule_Create(&btrfsutilmodule);
+	if (!m)
+		return NULL;
+
+	Py_INCREF(&BtrfsUtilError_type);
+	PyModule_AddObject(m, "BtrfsUtilError",
+			   (PyObject *)&BtrfsUtilError_type);
+
+	add_module_constants(m);
+
+	return m;
+}
diff --git a/libbtrfsutil/python/setup.py b/libbtrfsutil/python/setup.py
new file mode 100755
index 00000000..3dc778ab
--- /dev/null
+++ b/libbtrfsutil/python/setup.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2018 Facebook
+#
+# This file is part of libbtrfsutil.
+#
+# libbtrfsutil is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# libbtrfsutil 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import os
+import os.path
+from setuptools import setup, Extension
+from setuptools.command.build_ext import build_ext
+import subprocess
+
+
+def out_of_date(dependencies, target):
+    dependency_mtimes = [os.path.getmtime(dependency) for dependency in dependencies]
+    try:
+        target_mtime = os.path.getmtime(target)
+    except OSError:
+        return True
+    return any(dependency_mtime >= target_mtime for dependency_mtime in dependency_mtimes)
+
+
+def gen_constants():
+    with open('../btrfsutil.h', 'r') as f:
+        btrfsutil_h = f.read()
+
+    constants = re.findall(
+        r'^\s*(BTRFS_UTIL_ERROR_[a-zA-Z0-9_]+)',
+        btrfsutil_h, flags=re.MULTILINE)
+
+    with open('constants.c', 'w') as f:
+        f.write("""\
+#include <btrfsutil.h>
+#include "btrfsutilpy.h"
+
+void add_module_constants(PyObject *m)
+{
+""")
+        for constant in constants:
+            assert constant.startswith('BTRFS_UTIL_')
+            name = constant[len('BTRFS_UTIL_'):]
+            f.write('\tPyModule_AddIntConstant(m, "{}", {});\n'.format(name, constant))
+        f.write("""\
+}
+""")
+
+
+class my_build_ext(build_ext):
+    def run(self):
+        if out_of_date(['../btrfsutil.h'], 'constants.c'):
+            try:
+                gen_constants()
+            except Exception as e:
+                try:
+                    os.remove('constants.c')
+                except OSError:
+                    pass
+                raise e
+        super().run()
+
+
+module = Extension(
+    name='btrfsutil',
+    sources=[
+        'constants.c',
+        'error.c',
+        'module.c',
+    ],
+    include_dirs=['..'],
+    library_dirs=['../..'],
+    libraries=['btrfsutil'],
+)
+
+
+version = subprocess.check_output(
+    ['./version.sh', '--configure'], cwd='../..').decode('utf-8')
+version = re.match(r'v(\d+(?:\.\d+)*)', version).group(1)
+
+setup(
+    name='btrfsutil',
+    version=version,
+    description='Library for managing Btrfs filesystems',
+    url='https://github.com/kdave/btrfs-progs',
+    cmdclass={'build_ext': my_build_ext},
+    ext_modules=[module],
+)
diff --git a/libbtrfsutil/python/tests/__init__.py b/libbtrfsutil/python/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
-- 
2.16.1


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

* [PATCH 04/26] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id()
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (2 preceding siblings ...)
  2018-01-26 18:40 ` [PATCH 03/26] libbtrfsutil: add Python bindings Omar Sandoval
@ 2018-01-26 18:40 ` Omar Sandoval
  2018-01-29 10:24   ` Nikolay Borisov
  2018-01-26 18:40 ` [PATCH 05/26] libbtrfsutil: add qgroup inheritance helpers Omar Sandoval
                   ` (22 subsequent siblings)
  26 siblings, 1 reply; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

These are the most trivial helpers in the library and will be used to
implement several of the more involved functions.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 Makefile                                    |   2 +-
 libbtrfsutil/btrfsutil.h                    |  33 +++++++
 libbtrfsutil/python/btrfsutilpy.h           |   3 +
 libbtrfsutil/python/module.c                |  12 +++
 libbtrfsutil/python/setup.py                |   1 +
 libbtrfsutil/python/subvolume.c             |  73 +++++++++++++++
 libbtrfsutil/python/tests/__init__.py       |  66 ++++++++++++++
 libbtrfsutil/python/tests/test_subvolume.py |  57 ++++++++++++
 libbtrfsutil/subvolume.c                    | 137 ++++++++++++++++++++++++++++
 9 files changed, 383 insertions(+), 1 deletion(-)
 create mode 100644 libbtrfsutil/python/subvolume.c
 create mode 100644 libbtrfsutil/python/tests/test_subvolume.py
 create mode 100644 libbtrfsutil/subvolume.c

diff --git a/Makefile b/Makefile
index 02b03e81..48a558a9 100644
--- a/Makefile
+++ b/Makefile
@@ -123,7 +123,7 @@ libbtrfs_headers = send-stream.h send-utils.h send.h kernel-lib/rbtree.h btrfs-l
 	       kernel-lib/radix-tree.h kernel-lib/sizes.h kernel-lib/raid56.h \
 	       extent-cache.h extent_io.h ioctl.h ctree.h btrfsck.h version.h
 libbtrfsutil_version := 0.1
-libbtrfsutil_objects = libbtrfsutil/errors.o
+libbtrfsutil_objects = libbtrfsutil/errors.o libbtrfsutil/subvolume.o
 convert_objects = convert/main.o convert/common.o convert/source-fs.o \
 		  convert/source-ext2.o convert/source-reiserfs.o
 mkfs_objects = mkfs/main.o mkfs/common.o
diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index fe1091ca..dff6599d 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -20,6 +20,8 @@
 #ifndef BTRFS_UTIL_H
 #define BTRFS_UTIL_H
 
+#include <stdint.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -65,6 +67,37 @@ enum btrfs_util_error {
  */
 const char *btrfs_util_strerror(enum btrfs_util_error err);
 
+/**
+ * btrfs_util_is_subvolume() - Return whether a given path is a Btrfs subvolume.
+ * @path: Path to check.
+ *
+ * Return: %BTRFS_UTIL_OK if @path is a Btrfs subvolume,
+ * %BTRFS_UTIL_ERROR_NOT_BTRFS if @path is not on a Btrfs filesystem,
+ * %BTRFS_UTIL_ERROR_NOT_SUBVOLUME if @path is not a subvolume, non-zero error
+ * code on any other failure.
+ */
+enum btrfs_util_error btrfs_util_is_subvolume(const char *path);
+
+/**
+ * btrfs_util_f_is_subvolume() - See btrfs_util_is_subvolume().
+ */
+enum btrfs_util_error btrfs_util_f_is_subvolume(int fd);
+
+/**
+ * btrfs_util_subvolume_id() - Get the ID of the subvolume containing a path.
+ * @path: Path on a Btrfs filesystem.
+ * @id_ret: Returned subvolume ID.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_subvolume_id(const char *path,
+					      uint64_t *id_ret);
+
+/**
+ * btrfs_util_f_subvolume_id() - See btrfs_util_subvolume_id().
+ */
+enum btrfs_util_error btrfs_util_f_subvolume_id(int fd, uint64_t *id_ret);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
index 6d82f7e1..9a04fda7 100644
--- a/libbtrfsutil/python/btrfsutilpy.h
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -52,6 +52,9 @@ void SetFromBtrfsUtilErrorWithPaths(enum btrfs_util_error err,
 				    struct path_arg *path1,
 				    struct path_arg *path2);
 
+PyObject *is_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds);
+
 void add_module_constants(PyObject *m);
 
 #endif /* BTRFSUTILPY_H */
diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
index d7398808..d492cbc7 100644
--- a/libbtrfsutil/python/module.c
+++ b/libbtrfsutil/python/module.c
@@ -132,6 +132,18 @@ void path_cleanup(struct path_arg *path)
 }
 
 static PyMethodDef btrfsutil_methods[] = {
+	{"is_subvolume", (PyCFunction)is_subvolume,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "is_subvolume(path) -> bool\n\n"
+	 "Get whether a file is a subvolume.\n\n"
+	 "Arguments:\n"
+	 "path -- string, bytes, path-like object, or open file descriptor"},
+	{"subvolume_id", (PyCFunction)subvolume_id,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "subvolume_id(path) -> int\n\n"
+	 "Get the ID of the subvolume containing a file.\n\n"
+	 "Arguments:\n"
+	 "path -- string, bytes, path-like object, or open file descriptor"},
 	{},
 };
 
diff --git a/libbtrfsutil/python/setup.py b/libbtrfsutil/python/setup.py
index 3dc778ab..be973a34 100755
--- a/libbtrfsutil/python/setup.py
+++ b/libbtrfsutil/python/setup.py
@@ -79,6 +79,7 @@ module = Extension(
         'constants.c',
         'error.c',
         'module.c',
+        'subvolume.c',
     ],
     include_dirs=['..'],
     library_dirs=['../..'],
diff --git a/libbtrfsutil/python/subvolume.c b/libbtrfsutil/python/subvolume.c
new file mode 100644
index 00000000..538bf324
--- /dev/null
+++ b/libbtrfsutil/python/subvolume.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "btrfsutilpy.h"
+
+PyObject *is_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *keywords[] = {"path", NULL};
+	struct path_arg path = {.allow_fd = true};
+	enum btrfs_util_error err;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:is_subvolume",
+					 keywords, &path_converter, &path))
+		return NULL;
+
+	if (path.path)
+		err = btrfs_util_is_subvolume(path.path);
+	else
+		err = btrfs_util_f_is_subvolume(path.fd);
+	if (err == BTRFS_UTIL_OK) {
+		path_cleanup(&path);
+		Py_RETURN_TRUE;
+	} else if (err == BTRFS_UTIL_ERROR_NOT_BTRFS ||
+		   err == BTRFS_UTIL_ERROR_NOT_SUBVOLUME) {
+		path_cleanup(&path);
+		Py_RETURN_FALSE;
+	} else {
+		SetFromBtrfsUtilErrorWithPath(err, &path);
+		path_cleanup(&path);
+		return NULL;
+	}
+}
+
+PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *keywords[] = {"path", NULL};
+	struct path_arg path = {.allow_fd = true};
+	enum btrfs_util_error err;
+	uint64_t id;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:subvolume_id",
+					 keywords, &path_converter, &path))
+		return NULL;
+
+	if (path.path)
+		err = btrfs_util_subvolume_id(path.path, &id);
+	else
+		err = btrfs_util_f_subvolume_id(path.fd, &id);
+	if (err) {
+		SetFromBtrfsUtilErrorWithPath(err, &path);
+		path_cleanup(&path);
+		return NULL;
+	}
+
+	path_cleanup(&path);
+	return PyLong_FromUnsignedLongLong(id);
+}
diff --git a/libbtrfsutil/python/tests/__init__.py b/libbtrfsutil/python/tests/__init__.py
index e69de29b..d2c6ff28 100644
--- a/libbtrfsutil/python/tests/__init__.py
+++ b/libbtrfsutil/python/tests/__init__.py
@@ -0,0 +1,66 @@
+# Copyright (C) 2018 Facebook
+#
+# This file is part of libbtrfsutil.
+#
+# libbtrfsutil is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# libbtrfsutil 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+from pathlib import PurePath
+import subprocess
+import tempfile
+import unittest
+
+
+HAVE_PATH_LIKE = hasattr(PurePath, '__fspath__')
+
+
+@unittest.skipIf(os.geteuid() != 0, 'must be run as root')
+class BtrfsTestCase(unittest.TestCase):
+    def setUp(self):
+        self.mountpoint = tempfile.mkdtemp()
+        try:
+            with tempfile.NamedTemporaryFile(delete=False) as f:
+                os.truncate(f.fileno(), 1024 * 1024 * 1024)
+                self.image = f.name
+        except Exception as e:
+            os.rmdir(self.mountpoint)
+            raise e
+
+        try:
+            subprocess.check_call(['mkfs.btrfs', '-q', self.image])
+            subprocess.check_call(['mount', '-o', 'loop', '--', self.image, self.mountpoint])
+        except Exception as e:
+            os.remove(self.image)
+            os.rmdir(self.mountpoint)
+            raise e
+
+    def tearDown(self):
+        try:
+            subprocess.check_call(['umount', self.mountpoint])
+        finally:
+            os.remove(self.image)
+            os.rmdir(self.mountpoint)
+
+    @staticmethod
+    def path_or_fd(path, open_flags=os.O_RDONLY):
+        yield path
+        yield path.encode()
+        if HAVE_PATH_LIKE:
+            yield PurePath(path)
+        fd = os.open(path, open_flags)
+        try:
+            yield fd
+        finally:
+            os.close(fd)
+
diff --git a/libbtrfsutil/python/tests/test_subvolume.py b/libbtrfsutil/python/tests/test_subvolume.py
new file mode 100644
index 00000000..44b1d7f0
--- /dev/null
+++ b/libbtrfsutil/python/tests/test_subvolume.py
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 Facebook
+#
+# This file is part of libbtrfsutil.
+#
+# libbtrfsutil is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# libbtrfsutil 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+
+import fcntl
+import errno
+import os
+import os.path
+from pathlib import PurePath
+import traceback
+
+import btrfsutil
+from tests import BtrfsTestCase
+
+
+class TestSubvolume(BtrfsTestCase):
+    def test_is_subvolume(self):
+        dir = os.path.join(self.mountpoint, 'foo')
+        os.mkdir(dir)
+
+        for arg in self.path_or_fd(self.mountpoint):
+            with self.subTest(type=type(arg)):
+                self.assertTrue(btrfsutil.is_subvolume(arg))
+        for arg in self.path_or_fd(dir):
+            with self.subTest(type=type(arg)):
+                self.assertFalse(btrfsutil.is_subvolume(arg))
+
+        with self.assertRaises(btrfsutil.BtrfsUtilError) as e:
+            btrfsutil.is_subvolume(os.path.join(self.mountpoint, 'bar'))
+        # This is a bit of an implementation detail, but really this is testing
+        # that the exception is initialized correctly.
+        self.assertEqual(e.exception.btrfsutilerror, btrfsutil.ERROR_STATFS_FAILED)
+        self.assertEqual(e.exception.errno, errno.ENOENT)
+
+    def test_subvolume_id(self):
+        dir = os.path.join(self.mountpoint, 'foo')
+        os.mkdir(dir)
+
+        for arg in self.path_or_fd(self.mountpoint):
+            with self.subTest(type=type(arg)):
+                self.assertEqual(btrfsutil.subvolume_id(arg), 5)
+        for arg in self.path_or_fd(dir):
+            with self.subTest(type=type(arg)):
+                self.assertEqual(btrfsutil.subvolume_id(arg), 5)
diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c
new file mode 100644
index 00000000..37d5d388
--- /dev/null
+++ b/libbtrfsutil/subvolume.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <linux/btrfs.h>
+#include <linux/btrfs_tree.h>
+#include <linux/magic.h>
+
+#include "btrfsutil.h"
+
+#define SAVE_ERRNO_AND_CLOSE(fd) {	\
+	int saved_errno = errno;	\
+					\
+	close(fd);			\
+	errno = saved_errno;		\
+}
+
+/*
+ * This intentionally duplicates btrfs_util_f_is_subvolume() instead of opening
+ * a file descriptor and calling it, because fstat() and fstatfs() don't accept
+ * file descriptors opened with O_PATH on old kernels (before v3.6 and before
+ * v3.12, respectively), but stat() and statfs() can be called on a path that
+ * the user doesn't have read or write permissions to.
+ */
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_is_subvolume(const char *path)
+{
+	struct statfs sfs;
+	struct stat st;
+	int ret;
+
+	ret = statfs(path, &sfs);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_STATFS_FAILED;
+
+	if (sfs.f_type != BTRFS_SUPER_MAGIC) {
+		errno = EINVAL;
+		return BTRFS_UTIL_ERROR_NOT_BTRFS;
+	}
+
+	ret = stat(path, &st);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_STAT_FAILED;
+
+	if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) {
+		errno = EINVAL;
+		return BTRFS_UTIL_ERROR_NOT_SUBVOLUME;
+	}
+
+	return BTRFS_UTIL_OK;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_f_is_subvolume(int fd)
+{
+	struct statfs sfs;
+	struct stat st;
+	int ret;
+
+	ret = fstatfs(fd, &sfs);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_STATFS_FAILED;
+
+	if (sfs.f_type != BTRFS_SUPER_MAGIC) {
+		errno = EINVAL;
+		return BTRFS_UTIL_ERROR_NOT_BTRFS;
+	}
+
+	ret = fstat(fd, &st);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_STAT_FAILED;
+
+	if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) {
+		errno = EINVAL;
+		return BTRFS_UTIL_ERROR_NOT_SUBVOLUME;
+	}
+
+	return BTRFS_UTIL_OK;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_subvolume_id(const char *path,
+					      uint64_t *id_ret)
+{
+	enum btrfs_util_error err;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+	err = btrfs_util_f_subvolume_id(fd, id_ret);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_f_subvolume_id(int fd, uint64_t *id_ret)
+{
+	struct btrfs_ioctl_ino_lookup_args args = {
+		.treeid = 0,
+		.objectid = BTRFS_FIRST_FREE_OBJECTID,
+	};
+	int ret;
+
+	ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
+	if (ret == -1) {
+		close(fd);
+		return BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED;
+	}
+
+	*id_ret = args.treeid;
+
+	return BTRFS_UTIL_OK;
+}
-- 
2.16.1


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

* [PATCH 05/26] libbtrfsutil: add qgroup inheritance helpers
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (3 preceding siblings ...)
  2018-01-26 18:40 ` [PATCH 04/26] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id() Omar Sandoval
@ 2018-01-26 18:40 ` Omar Sandoval
  2018-01-26 18:40 ` [PATCH 06/26] libbtrfsutil: add btrfs_util_create_subvolume() Omar Sandoval
                   ` (21 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

We want to hide struct btrfs_qgroup_inherit from the user because that
comes from the Btrfs UAPI headers. Instead, wrap it in a struct
btrfs_util_qgroup_inherit and provide helpers to manipulate it. This
will be used for subvolume and snapshot creation.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 Makefile                                 |   3 +-
 libbtrfsutil/btrfsutil.h                 |  45 +++++++++
 libbtrfsutil/python/btrfsutilpy.h        |   6 ++
 libbtrfsutil/python/module.c             |   8 ++
 libbtrfsutil/python/qgroup.c             | 154 +++++++++++++++++++++++++++++++
 libbtrfsutil/python/setup.py             |   1 +
 libbtrfsutil/python/tests/test_qgroup.py |  36 ++++++++
 libbtrfsutil/qgroup.c                    |  86 +++++++++++++++++
 8 files changed, 338 insertions(+), 1 deletion(-)
 create mode 100644 libbtrfsutil/python/qgroup.c
 create mode 100644 libbtrfsutil/python/tests/test_qgroup.py
 create mode 100644 libbtrfsutil/qgroup.c

diff --git a/Makefile b/Makefile
index 48a558a9..2f798f11 100644
--- a/Makefile
+++ b/Makefile
@@ -123,7 +123,8 @@ libbtrfs_headers = send-stream.h send-utils.h send.h kernel-lib/rbtree.h btrfs-l
 	       kernel-lib/radix-tree.h kernel-lib/sizes.h kernel-lib/raid56.h \
 	       extent-cache.h extent_io.h ioctl.h ctree.h btrfsck.h version.h
 libbtrfsutil_version := 0.1
-libbtrfsutil_objects = libbtrfsutil/errors.o libbtrfsutil/subvolume.o
+libbtrfsutil_objects = libbtrfsutil/errors.o libbtrfsutil/qgroup.o \
+		       libbtrfsutil/subvolume.o
 convert_objects = convert/main.o convert/common.o convert/source-fs.o \
 		  convert/source-ext2.o convert/source-reiserfs.o
 mkfs_objects = mkfs/main.o mkfs/common.o
diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index dff6599d..d401f4d7 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -20,6 +20,7 @@
 #ifndef BTRFS_UTIL_H
 #define BTRFS_UTIL_H
 
+#include <stddef.h>
 #include <stdint.h>
 
 #ifdef __cplusplus
@@ -98,6 +99,50 @@ enum btrfs_util_error btrfs_util_subvolume_id(const char *path,
  */
 enum btrfs_util_error btrfs_util_f_subvolume_id(int fd, uint64_t *id_ret);
 
+struct btrfs_util_qgroup_inherit;
+
+/**
+ * btrfs_util_create_qgroup_inherit() - Create a qgroup inheritance specifier
+ * for btrfs_util_create_subvolume() or btrfs_util_create_snapshot().
+ * @flags: Must be zero.
+ * @ret: Returned qgroup inheritance specifier.
+ *
+ * The returned structure must be freed with
+ * btrfs_util_destroy_qgroup_inherit().
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_create_qgroup_inherit(int flags,
+						       struct btrfs_util_qgroup_inherit **ret);
+
+/**
+ * btrfs_util_destroy_qgroup_inherit() - Destroy a qgroup inheritance specifier
+ * previously created with btrfs_util_create_qgroup_inherit().
+ * @inherit: Specifier to destroy.
+ */
+void btrfs_util_destroy_qgroup_inherit(struct btrfs_util_qgroup_inherit *inherit);
+
+/**
+ * btrfs_util_qgroup_inherit_add_group() - Add inheritance from a qgroup to a
+ * qgroup inheritance specifier.
+ * @inherit: Specifier to modify. May be reallocated.
+ * @qgroupid: ID of qgroup to inherit from.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_qgroup_inherit_add_group(struct btrfs_util_qgroup_inherit **inherit,
+							  uint64_t qgroupid);
+
+/**
+ * btrfs_util_qgroup_inherit_get_groups() - Get the qgroups a qgroup inheritance
+ * specifier contains.
+ * @inherit: Qgroup inheritance specifier.
+ * @groups: Returned array of qgroup IDs.
+ * @n: Returned number of entries in the @groups array.
+ */
+void btrfs_util_qgroup_inherit_get_groups(const struct btrfs_util_qgroup_inherit *inherit,
+					  const uint64_t **groups, size_t *n);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
index 9a04fda7..a36f2671 100644
--- a/libbtrfsutil/python/btrfsutilpy.h
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -29,7 +29,13 @@
 
 #include <btrfsutil.h>
 
+typedef struct {
+	PyObject_HEAD
+	struct btrfs_util_qgroup_inherit *inherit;
+} QgroupInherit;
+
 extern PyTypeObject BtrfsUtilError_type;
+extern PyTypeObject QgroupInherit_type;
 
 /*
  * Helpers for path arguments based on posixmodule.c in CPython.
diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
index d492cbc7..de7d17a1 100644
--- a/libbtrfsutil/python/module.c
+++ b/libbtrfsutil/python/module.c
@@ -164,6 +164,10 @@ PyInit_btrfsutil(void)
 	if (PyType_Ready(&BtrfsUtilError_type) < 0)
 		return NULL;
 
+	QgroupInherit_type.tp_new = PyType_GenericNew;
+	if (PyType_Ready(&QgroupInherit_type) < 0)
+		return NULL;
+
 	m = PyModule_Create(&btrfsutilmodule);
 	if (!m)
 		return NULL;
@@ -172,6 +176,10 @@ PyInit_btrfsutil(void)
 	PyModule_AddObject(m, "BtrfsUtilError",
 			   (PyObject *)&BtrfsUtilError_type);
 
+	Py_INCREF(&QgroupInherit_type);
+	PyModule_AddObject(m, "QgroupInherit",
+			   (PyObject *)&QgroupInherit_type);
+
 	add_module_constants(m);
 
 	return m;
diff --git a/libbtrfsutil/python/qgroup.c b/libbtrfsutil/python/qgroup.c
new file mode 100644
index 00000000..69716d92
--- /dev/null
+++ b/libbtrfsutil/python/qgroup.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "btrfsutilpy.h"
+
+static void QgroupInherit_dealloc(QgroupInherit *self)
+{
+	btrfs_util_destroy_qgroup_inherit(self->inherit);
+	Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static int QgroupInherit_init(QgroupInherit *self, PyObject *args,
+			      PyObject *kwds)
+{
+	static char *keywords[] = {NULL};
+	enum btrfs_util_error err;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, ":QgroupInherit",
+					 keywords))
+		return -1;
+
+	err = btrfs_util_create_qgroup_inherit(0, &self->inherit);
+	if (err) {
+		SetFromBtrfsUtilError(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+static PyObject *QgroupInherit_getattro(QgroupInherit *self, PyObject *nameobj)
+{
+    const char *name = "";
+
+    if (PyUnicode_Check(nameobj)) {
+	    name = PyUnicode_AsUTF8(nameobj);
+	    if (!name)
+		    return NULL;
+    }
+
+    if (strcmp(name, "groups") == 0) {
+	    PyObject *ret, *tmp;
+	    const uint64_t *arr;
+	    size_t n, i;
+
+	    btrfs_util_qgroup_inherit_get_groups(self->inherit, &arr, &n);
+	    ret = PyList_New(n);
+	    if (!ret)
+		    return NULL;
+
+	    for (i = 0; i < n; i++) {
+		    tmp = PyLong_FromUnsignedLongLong(arr[i]);
+		    if (!tmp) {
+			    Py_DECREF(ret);
+			    return NULL;
+		    }
+		    PyList_SET_ITEM(ret, i, tmp);
+	    }
+
+	    return ret;
+    } else {
+	    return PyObject_GenericGetAttr((PyObject *)self, nameobj);
+    }
+}
+
+static PyObject *QgroupInherit_add_group(QgroupInherit *self, PyObject *args,
+					 PyObject *kwds)
+{
+	static char *keywords[] = {"qgroupid", NULL};
+	enum btrfs_util_error err;
+	uint64_t qgroupid;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "K:add_group", keywords,
+					 &qgroupid))
+		return NULL;
+
+	err = btrfs_util_qgroup_inherit_add_group(&self->inherit, qgroupid);
+	if (err) {
+		SetFromBtrfsUtilError(err);
+		return NULL;
+	}
+
+	Py_RETURN_NONE;
+}
+
+static PyMethodDef QgroupInherit_methods[] = {
+	{"add_group", (PyCFunction)QgroupInherit_add_group,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "add_group(qgroupid)\n\n"
+	 "Add a qgroup to inherit from.\n\n"
+	 "Arguments:\n"
+	 "qgroupid -- ID of qgroup to add"},
+	{},
+};
+
+#define QgroupInherit_DOC	\
+	"QgroupInherit() -> new qgroup inheritance specifier\n\n"	\
+	"Create a new object which specifies what qgroups to inherit\n"	\
+	"from for create_subvolume() and create_snapshot()"
+
+PyTypeObject QgroupInherit_type = {
+	PyVarObject_HEAD_INIT(NULL, 0)
+	"btrfsutil.QgroupInherit",		/* tp_name */
+	sizeof(QgroupInherit),			/* tp_basicsize */
+	0,					/* tp_itemsize */
+	(destructor)QgroupInherit_dealloc,	/* tp_dealloc */
+	NULL,					/* tp_print */
+	NULL,					/* tp_getattr */
+	NULL,					/* tp_setattr */
+	NULL,					/* tp_as_async */
+	NULL,					/* tp_repr */
+	NULL,					/* tp_as_number */
+	NULL,					/* tp_as_sequence */
+	NULL,					/* tp_as_mapping */
+	NULL,					/* tp_hash  */
+	NULL,					/* tp_call */
+	NULL,					/* tp_str */
+	(getattrofunc)QgroupInherit_getattro,	/* tp_getattro */
+	NULL,					/* tp_setattro */
+	NULL,					/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT,			/* tp_flags */
+	QgroupInherit_DOC,			/* tp_doc */
+	NULL,					/* tp_traverse */
+	NULL,					/* tp_clear */
+	NULL,					/* tp_richcompare */
+	0,					/* tp_weaklistoffset */
+	NULL,					/* tp_iter */
+	NULL,					/* tp_iternext */
+	QgroupInherit_methods,			/* tp_methods */
+	NULL,					/* tp_members */
+	NULL,					/* tp_getset */
+	NULL,					/* tp_base */
+	NULL,					/* tp_dict */
+	NULL,					/* tp_descr_get */
+	NULL,					/* tp_descr_set */
+	0,					/* tp_dictoffset */
+	(initproc)QgroupInherit_init,		/* tp_init */
+};
diff --git a/libbtrfsutil/python/setup.py b/libbtrfsutil/python/setup.py
index be973a34..a2a43f80 100755
--- a/libbtrfsutil/python/setup.py
+++ b/libbtrfsutil/python/setup.py
@@ -79,6 +79,7 @@ module = Extension(
         'constants.c',
         'error.c',
         'module.c',
+        'qgroup.c',
         'subvolume.c',
     ],
     include_dirs=['..'],
diff --git a/libbtrfsutil/python/tests/test_qgroup.py b/libbtrfsutil/python/tests/test_qgroup.py
new file mode 100644
index 00000000..a590464b
--- /dev/null
+++ b/libbtrfsutil/python/tests/test_qgroup.py
@@ -0,0 +1,36 @@
+# Copyright (C) 2018 Facebook
+#
+# This file is part of libbtrfsutil.
+#
+# libbtrfsutil is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# libbtrfsutil 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import unittest
+
+import btrfsutil
+
+
+class TestQgroupInherit(unittest.TestCase):
+    def test_new(self):
+        inherit = btrfsutil.QgroupInherit()
+        self.assertEqual(inherit.groups, [])
+
+    def test_add_group(self):
+        inherit = btrfsutil.QgroupInherit()
+        inherit.add_group(1)
+        self.assertEqual(inherit.groups, [1])
+        inherit.add_group(2)
+        self.assertEqual(inherit.groups, [1, 2])
+        inherit.add_group(3)
+        self.assertEqual(inherit.groups, [1, 2, 3])
diff --git a/libbtrfsutil/qgroup.c b/libbtrfsutil/qgroup.c
new file mode 100644
index 00000000..033b1f5e
--- /dev/null
+++ b/libbtrfsutil/qgroup.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <linux/btrfs.h>
+
+#include "btrfsutil.h"
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_create_qgroup_inherit(int flags,
+						       struct btrfs_util_qgroup_inherit **ret)
+{
+	struct btrfs_qgroup_inherit *inherit;
+
+	if (flags) {
+		errno = EINVAL;
+		return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+	}
+
+	inherit = calloc(1, sizeof(*inherit));
+	if (!inherit)
+		return BTRFS_UTIL_ERROR_NO_MEMORY;
+
+	/*
+	 * struct btrfs_util_qgroup_inherit is a lie; it's actually struct
+	 * btrfs_qgroup_inherit, but we abstract it away so that users don't
+	 * need to depend on the Btrfs UAPI headers.
+	 */
+	*ret = (struct btrfs_util_qgroup_inherit *)inherit;
+
+	return BTRFS_UTIL_OK;
+}
+
+__attribute__((visibility("default")))
+void btrfs_util_destroy_qgroup_inherit(struct btrfs_util_qgroup_inherit *inherit)
+{
+	free(inherit);
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_qgroup_inherit_add_group(struct btrfs_util_qgroup_inherit **inherit,
+							  uint64_t qgroupid)
+{
+	struct btrfs_qgroup_inherit *tmp = (struct btrfs_qgroup_inherit *)*inherit;
+
+	tmp = realloc(tmp, sizeof(*tmp) +
+		      (tmp->num_qgroups + 1) * sizeof(tmp->qgroups[0]));
+	if (!tmp)
+		return BTRFS_UTIL_ERROR_NO_MEMORY;
+
+	tmp->qgroups[tmp->num_qgroups++] = qgroupid;
+
+	*inherit = (struct btrfs_util_qgroup_inherit *)tmp;
+
+	return BTRFS_UTIL_OK;
+}
+
+__attribute__((visibility("default")))
+void btrfs_util_qgroup_inherit_get_groups(const struct btrfs_util_qgroup_inherit *inherit,
+					  const uint64_t **groups, size_t *n)
+{
+	struct btrfs_qgroup_inherit *tmp = (struct btrfs_qgroup_inherit *)inherit;
+
+	/* Need to cast because __u64 != uint64_t. */
+	*groups = (const uint64_t *)&tmp->qgroups[0];
+	*n = tmp->num_qgroups;
+}
-- 
2.16.1


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

* [PATCH 06/26] libbtrfsutil: add btrfs_util_create_subvolume()
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (4 preceding siblings ...)
  2018-01-26 18:40 ` [PATCH 05/26] libbtrfsutil: add qgroup inheritance helpers Omar Sandoval
@ 2018-01-26 18:40 ` Omar Sandoval
  2018-01-26 18:40 ` [PATCH 07/26] libbtrfsutil: add btrfs_util_subvolume_info() Omar Sandoval
                   ` (20 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

Doing the ioctl() directly isn't too bad, but passing in a full path is
more convenient than opening the parent and passing the path component.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 libbtrfsutil/btrfsutil.h                    |  52 ++++++
 libbtrfsutil/internal.h                     |  29 ++++
 libbtrfsutil/python/btrfsutilpy.h           |   2 +
 libbtrfsutil/python/module.c                |  16 ++
 libbtrfsutil/python/subvolume.c             |  59 +++++++
 libbtrfsutil/python/tests/test_qgroup.py    |  11 ++
 libbtrfsutil/python/tests/test_subvolume.py |  80 ++++++++-
 libbtrfsutil/subvolume.c                    | 241 ++++++++++++++++++++++++++++
 8 files changed, 489 insertions(+), 1 deletion(-)
 create mode 100644 libbtrfsutil/internal.h

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index d401f4d7..20029f18 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -99,8 +99,60 @@ enum btrfs_util_error btrfs_util_subvolume_id(const char *path,
  */
 enum btrfs_util_error btrfs_util_f_subvolume_id(int fd, uint64_t *id_ret);
 
+/**
+ * btrfs_util_subvolume_path() - Get the path of the subvolume with a given ID
+ * relative to the filesystem root.
+ * @path: Path on a Btrfs filesystem.
+ * @id: ID of subvolume to set as the default. If zero is given, the subvolume
+ * ID of @path is used.
+ * @path_ret: Returned path.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_subvolume_path(const char *path, uint64_t id,
+						char **path_ret);
+
+/**
+ * btrfs_util_f_subvolume_path() - See btrfs_util_subvolume_path().
+ */
+enum btrfs_util_error btrfs_util_f_subvolume_path(int fd, uint64_t id,
+						  char **path_ret);
+
 struct btrfs_util_qgroup_inherit;
 
+/**
+ * btrfs_util_create_subvolume() - Create a new subvolume.
+ * @path: Where to create the subvolume.
+ * @flags: Must be zero.
+ * @async_transid: If not NULL, create the subvolume asynchronously (i.e.,
+ * without waiting for it to commit it to disk) and return the transaction ID
+ * that it was created in. This transaction ID can be waited on with
+ * btrfs_util_wait_sync().
+ * @qgroup_inherit: Qgroups to inherit from, or NULL.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_create_subvolume(const char *path, int flags,
+						  uint64_t *async_transid,
+						  struct btrfs_util_qgroup_inherit *qgroup_inherit);
+
+/**
+ * btrfs_util_f_create_subvolume() - Create a new subvolume given its parent and
+ * name.
+ * @parent_fd: File descriptor of the parent directory where the subvolume
+ * should be created.
+ * @name: Name of the subvolume to create.
+ * @flags: See btrfs_util_create_subvolume().
+ * @async_transid: See btrfs_util_create_subvolume().
+ * @qgroup_inherit: See btrfs_util_create_subvolume().
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_f_create_subvolume(int parent_fd,
+						    const char *name, int flags,
+						    uint64_t *async_transid,
+						    struct btrfs_util_qgroup_inherit *qgroup_inherit);
+
 /**
  * btrfs_util_create_qgroup_inherit() - Create a qgroup inheritance specifier
  * for btrfs_util_create_subvolume() or btrfs_util_create_snapshot().
diff --git a/libbtrfsutil/internal.h b/libbtrfsutil/internal.h
new file mode 100644
index 00000000..bc91ad88
--- /dev/null
+++ b/libbtrfsutil/internal.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BTRFS_UTIL_INTERNAL_H
+#define BTRFS_UTIL_INTERNAL_H
+
+#include <asm/byteorder.h>
+
+#define le16_to_cpu __le16_to_cpu
+#define le32_to_cpu __le32_to_cpu
+#define le64_to_cpu __le64_to_cpu
+
+#endif /* BTRFS_UTIL_INTERNAL_H */
diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
index a36f2671..32e83fe5 100644
--- a/libbtrfsutil/python/btrfsutilpy.h
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -60,6 +60,8 @@ void SetFromBtrfsUtilErrorWithPaths(enum btrfs_util_error err,
 
 PyObject *is_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *subvolume_path(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 
 void add_module_constants(PyObject *m);
 
diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
index de7d17a1..444516b1 100644
--- a/libbtrfsutil/python/module.c
+++ b/libbtrfsutil/python/module.c
@@ -144,6 +144,22 @@ static PyMethodDef btrfsutil_methods[] = {
 	 "Get the ID of the subvolume containing a file.\n\n"
 	 "Arguments:\n"
 	 "path -- string, bytes, path-like object, or open file descriptor"},
+	{"subvolume_path", (PyCFunction)subvolume_path,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "subvolume_path(path, id=0) -> int\n\n"
+	 "Get the path of a subvolume relative to the filesystem root.\n\n"
+	 "Arguments:\n"
+	 "path -- string, bytes, path-like object, or open file descriptor\n"
+	 "id -- if not zero, instead of returning the subvolume path of the\n"
+	 "given path, return the path of the subvolume with this ID"},
+	{"create_subvolume", (PyCFunction)create_subvolume,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "create_subvolume(path, async=False)\n\n"
+	 "Create a new subvolume.\n\n"
+	 "Arguments:\n"
+	 "path -- string, bytes, or path-like object\n"
+	 "async -- create the subvolume without waiting for it to commit to\n"
+	 "disk and return the transaction ID"},
 	{},
 };
 
diff --git a/libbtrfsutil/python/subvolume.c b/libbtrfsutil/python/subvolume.c
index 538bf324..bef9fab5 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -71,3 +71,62 @@ PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds)
 	path_cleanup(&path);
 	return PyLong_FromUnsignedLongLong(id);
 }
+
+PyObject *subvolume_path(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *keywords[] = {"path", "id", NULL};
+	struct path_arg path = {.allow_fd = true};
+	enum btrfs_util_error err;
+	uint64_t id = 0;
+	char *subvol_path;
+	PyObject *ret;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:subvolume_path",
+					 keywords, &path_converter, &path, &id))
+		return NULL;
+
+	if (path.path)
+		err = btrfs_util_subvolume_path(path.path, id, &subvol_path);
+	else
+		err = btrfs_util_f_subvolume_path(path.fd, id, &subvol_path);
+	if (err) {
+		SetFromBtrfsUtilErrorWithPath(err, &path);
+		path_cleanup(&path);
+		return NULL;
+	}
+
+	path_cleanup(&path);
+
+	ret = PyUnicode_DecodeFSDefault(subvol_path);
+	free(subvol_path);
+	return ret;
+}
+
+PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *keywords[] = {"path", "async", "qgroup_inherit", NULL};
+	struct path_arg path = {.allow_fd = false};
+	enum btrfs_util_error err;
+	int async = 0;
+	QgroupInherit *inherit = NULL;
+	uint64_t transid;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|pO!:create_subvolume",
+					 keywords, &path_converter, &path,
+					 &async, &QgroupInherit_type, &inherit))
+		return NULL;
+
+	err = btrfs_util_create_subvolume(path.path, 0, async ? &transid : NULL,
+					  inherit ? inherit->inherit : NULL);
+	if (err) {
+		SetFromBtrfsUtilErrorWithPath(err, &path);
+		path_cleanup(&path);
+		return NULL;
+	}
+
+	path_cleanup(&path);
+	if (async)
+		return PyLong_FromUnsignedLongLong(transid);
+	else
+		Py_RETURN_NONE;
+}
diff --git a/libbtrfsutil/python/tests/test_qgroup.py b/libbtrfsutil/python/tests/test_qgroup.py
index a590464b..19e6b05a 100644
--- a/libbtrfsutil/python/tests/test_qgroup.py
+++ b/libbtrfsutil/python/tests/test_qgroup.py
@@ -19,6 +19,17 @@ import os
 import unittest
 
 import btrfsutil
+from tests import BtrfsTestCase
+
+
+class TestQgroup(BtrfsTestCase):
+    def test_subvolume_inherit(self):
+        subvol = os.path.join(self.mountpoint, 'subvol')
+
+        inherit = btrfsutil.QgroupInherit()
+        inherit.add_group(5)
+
+        btrfsutil.create_subvolume(subvol, qgroup_inherit=inherit)
 
 
 class TestQgroupInherit(unittest.TestCase):
diff --git a/libbtrfsutil/python/tests/test_subvolume.py b/libbtrfsutil/python/tests/test_subvolume.py
index 44b1d7f0..57ba27bf 100644
--- a/libbtrfsutil/python/tests/test_subvolume.py
+++ b/libbtrfsutil/python/tests/test_subvolume.py
@@ -23,7 +23,7 @@ from pathlib import PurePath
 import traceback
 
 import btrfsutil
-from tests import BtrfsTestCase
+from tests import BtrfsTestCase, HAVE_PATH_LIKE
 
 
 class TestSubvolume(BtrfsTestCase):
@@ -55,3 +55,81 @@ class TestSubvolume(BtrfsTestCase):
         for arg in self.path_or_fd(dir):
             with self.subTest(type=type(arg)):
                 self.assertEqual(btrfsutil.subvolume_id(arg), 5)
+
+    def test_subvolume_path(self):
+        btrfsutil.create_subvolume(os.path.join(self.mountpoint, 'subvol1'))
+        os.mkdir(os.path.join(self.mountpoint, 'dir1'))
+        os.mkdir(os.path.join(self.mountpoint, 'dir1/dir2'))
+        btrfsutil.create_subvolume(os.path.join(self.mountpoint, 'dir1/dir2/subvol2'))
+        btrfsutil.create_subvolume(os.path.join(self.mountpoint, 'dir1/dir2/subvol2/subvol3'))
+        os.mkdir(os.path.join(self.mountpoint, 'subvol1/dir3'))
+        btrfsutil.create_subvolume(os.path.join(self.mountpoint, 'subvol1/dir3/subvol4'))
+
+        for arg in self.path_or_fd(self.mountpoint):
+            with self.subTest(type=type(arg)):
+                self.assertEqual(btrfsutil.subvolume_path(arg), '')
+                self.assertEqual(btrfsutil.subvolume_path(arg, 5), '')
+                self.assertEqual(btrfsutil.subvolume_path(arg, 256), 'subvol1')
+                self.assertEqual(btrfsutil.subvolume_path(arg, 257), 'dir1/dir2/subvol2')
+                self.assertEqual(btrfsutil.subvolume_path(arg, 258), 'dir1/dir2/subvol2/subvol3')
+                self.assertEqual(btrfsutil.subvolume_path(arg, 259), 'subvol1/dir3/subvol4')
+
+        pwd = os.getcwd()
+        try:
+            os.chdir(self.mountpoint)
+            path = ''
+            for i in range(26):
+                name = chr(ord('a') + i) * 255
+                path = os.path.join(path, name)
+                btrfsutil.create_subvolume(name)
+                os.chdir(name)
+            self.assertEqual(btrfsutil.subvolume_path('.'), path)
+        finally:
+            os.chdir(pwd)
+
+    def test_create_subvolume(self):
+        subvol = os.path.join(self.mountpoint, 'subvol')
+
+        btrfsutil.create_subvolume(subvol + '1')
+        self.assertTrue(btrfsutil.is_subvolume(subvol + '1'))
+        btrfsutil.create_subvolume((subvol + '2').encode())
+        self.assertTrue(btrfsutil.is_subvolume(subvol + '2'))
+        if HAVE_PATH_LIKE:
+            btrfsutil.create_subvolume(PurePath(subvol + '3'))
+            self.assertTrue(btrfsutil.is_subvolume(subvol + '3'))
+
+        pwd = os.getcwd()
+        try:
+            os.chdir(self.mountpoint)
+            btrfsutil.create_subvolume('subvol4')
+            self.assertTrue(btrfsutil.is_subvolume('subvol4'))
+        finally:
+            os.chdir(pwd)
+
+        btrfsutil.create_subvolume(subvol + '5/')
+        self.assertTrue(btrfsutil.is_subvolume(subvol + '5'))
+
+        btrfsutil.create_subvolume(subvol + '6//')
+        self.assertTrue(btrfsutil.is_subvolume(subvol + '6'))
+
+        transid = btrfsutil.create_subvolume(subvol + '7', async=True)
+        self.assertTrue(btrfsutil.is_subvolume(subvol + '7'))
+        self.assertGreater(transid, 0)
+
+        # Test creating subvolumes under '/' in a chroot.
+        pid = os.fork()
+        if pid == 0:
+            try:
+                os.chroot(self.mountpoint)
+                os.chdir('/')
+                btrfsutil.create_subvolume('/subvol8')
+                self.assertTrue(btrfsutil.is_subvolume('/subvol8'))
+                with self.assertRaises(btrfsutil.BtrfsUtilError):
+                    btrfsutil.create_subvolume('/')
+                os._exit(0)
+            except Exception:
+                traceback.print_exc()
+                os._exit(1)
+        wstatus = os.waitpid(pid, 0)[1]
+        self.assertTrue(os.WIFEXITED(wstatus))
+        self.assertEqual(os.WEXITSTATUS(wstatus), 0)
diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c
index 37d5d388..864b8663 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -19,6 +19,8 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
@@ -29,6 +31,7 @@
 #include <linux/magic.h>
 
 #include "btrfsutil.h"
+#include "internal.h"
 
 #define SAVE_ERRNO_AND_CLOSE(fd) {	\
 	int saved_errno = errno;	\
@@ -135,3 +138,241 @@ enum btrfs_util_error btrfs_util_f_subvolume_id(int fd, uint64_t *id_ret)
 
 	return BTRFS_UTIL_OK;
 }
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_subvolume_path(const char *path, uint64_t id,
+						char **path_ret)
+{
+	enum btrfs_util_error err;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+	err = btrfs_util_f_subvolume_path(fd, id, path_ret);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_f_subvolume_path(int fd, uint64_t id,
+						  char **path_ret)
+{
+	char *path, *p;
+	size_t capacity = 4096;
+
+	path = malloc(capacity);
+	if (!path)
+		return BTRFS_UTIL_ERROR_NO_MEMORY;
+	p = path + capacity - 1;
+	p[0] = '\0';
+
+	if (id == 0) {
+		enum btrfs_util_error err;
+
+		err = btrfs_util_f_is_subvolume(fd);
+		if (err)
+			return err;
+
+		err = btrfs_util_f_subvolume_id(fd, &id);
+		if (err)
+			return err;
+	}
+
+	while (id != BTRFS_FS_TREE_OBJECTID) {
+		struct btrfs_ioctl_search_args search = {
+			.key = {
+				.tree_id = BTRFS_ROOT_TREE_OBJECTID,
+				.min_objectid = id,
+				.max_objectid = id,
+				.min_type = BTRFS_ROOT_BACKREF_KEY,
+				.max_type = BTRFS_ROOT_BACKREF_KEY,
+				.min_offset = 0,
+				.max_offset = UINT64_MAX,
+				.min_transid = 0,
+				.max_transid = UINT64_MAX,
+				.nr_items = 1,
+			},
+		};
+		struct btrfs_ioctl_ino_lookup_args lookup;
+		const struct btrfs_ioctl_search_header *header;
+		const struct btrfs_root_ref *ref;
+		const char *name;
+		uint16_t name_len;
+		size_t lookup_len;
+		size_t total_len;
+		int ret;
+
+		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search);
+		if (ret == -1) {
+			free(path);
+			return BTRFS_UTIL_ERROR_SEARCH_FAILED;
+		}
+
+		if (search.key.nr_items == 0) {
+			free(path);
+			errno = ENOENT;
+			return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
+		}
+
+		header = (struct btrfs_ioctl_search_header *)search.buf;
+		ref = (struct btrfs_root_ref *)(header + 1);
+		name = (char *)(ref + 1);
+		name_len = le16_to_cpu(ref->name_len);
+
+		id = header->offset;
+
+		lookup.treeid = id;
+		lookup.objectid = le64_to_cpu(ref->dirid);
+		ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &lookup);
+		if (ret == -1) {
+			free(path);
+			return BTRFS_UTIL_ERROR_SEARCH_FAILED;
+		}
+		lookup_len = strlen(lookup.name);
+
+		total_len = name_len + lookup_len + (id != BTRFS_FS_TREE_OBJECTID);
+		if (p - total_len < path) {
+			char *new_path, *new_p;
+			size_t new_capacity = capacity * 2;
+
+			new_path = malloc(new_capacity);
+			if (!new_path) {
+				free(path);
+				return BTRFS_UTIL_ERROR_NO_MEMORY;
+			}
+			new_p = new_path + new_capacity - (path + capacity - p);
+			memcpy(new_p, p, path + capacity - p);
+			free(path);
+			path = new_path;
+			p = new_p;
+			capacity = new_capacity;
+		}
+		p -= name_len;
+		memcpy(p, name, name_len);
+		p -= lookup_len;
+		memcpy(p, lookup.name, lookup_len);
+		if (id != BTRFS_FS_TREE_OBJECTID)
+			*--p = '/';
+	}
+
+	if (p != path)
+		memmove(path, p, path + capacity - p);
+
+	*path_ret = path;
+
+	return BTRFS_UTIL_OK;
+}
+
+static enum btrfs_util_error openat_parent_and_name(int dirfd, const char *path,
+						    char *name, size_t name_len,
+						    int *fd)
+{
+	char *tmp_path, *slash, *dirname, *basename;
+	size_t len;
+
+	/* Ignore trailing slashes. */
+	len = strlen(path);
+	while (len > 1 && path[len - 1] == '/')
+		len--;
+
+	tmp_path = malloc(len + 1);
+	if (!tmp_path)
+		return BTRFS_UTIL_ERROR_NO_MEMORY;
+	memcpy(tmp_path, path, len);
+	tmp_path[len] = '\0';
+
+	slash = memrchr(tmp_path, '/', len);
+	if (slash == tmp_path) {
+		dirname = "/";
+		basename = tmp_path + 1;
+	} else if (slash) {
+		*slash = '\0';
+		dirname = tmp_path;
+		basename = slash + 1;
+	} else {
+		dirname = ".";
+		basename = tmp_path;
+	}
+
+	len = strlen(basename);
+	if (len >= name_len) {
+		errno = ENAMETOOLONG;
+		return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+	}
+	memcpy(name, basename, len);
+	name[len] = '\0';
+
+	*fd = openat(dirfd, dirname, O_RDONLY | O_DIRECTORY);
+	if (*fd == -1) {
+		free(tmp_path);
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+	}
+
+	free(tmp_path);
+	return BTRFS_UTIL_OK;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_create_subvolume(const char *path, int flags,
+						  uint64_t *async_transid,
+						  struct btrfs_util_qgroup_inherit *qgroup_inherit)
+{
+	char name[BTRFS_SUBVOL_NAME_MAX + 1];
+	enum btrfs_util_error err;
+	int parent_fd;
+
+	err = openat_parent_and_name(AT_FDCWD, path, name, sizeof(name),
+				     &parent_fd);
+	if (err)
+		return err;
+
+	err = btrfs_util_f_create_subvolume(parent_fd, name, flags,
+					    async_transid, qgroup_inherit);
+	SAVE_ERRNO_AND_CLOSE(parent_fd);
+	return err;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_f_create_subvolume(int parent_fd,
+						    const char *name, int flags,
+						    uint64_t *async_transid,
+						    struct btrfs_util_qgroup_inherit *qgroup_inherit)
+{
+	struct btrfs_ioctl_vol_args_v2 args = {};
+	size_t len;
+	int ret;
+
+	if (flags) {
+		errno = EINVAL;
+		return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+	}
+
+	if (async_transid)
+		args.flags |= BTRFS_SUBVOL_CREATE_ASYNC;
+	if (qgroup_inherit) {
+		args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
+		args.qgroup_inherit = (struct btrfs_qgroup_inherit *)qgroup_inherit;
+		args.size = (sizeof(*args.qgroup_inherit) +
+			     args.qgroup_inherit->num_qgroups *
+			     sizeof(args.qgroup_inherit->qgroups[0]));
+	}
+
+	len = strlen(name);
+	if (len >= sizeof(args.name)) {
+		errno = ENAMETOOLONG;
+		return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+	}
+	memcpy(args.name, name, len);
+	args.name[len] = '\0';
+
+	ret = ioctl(parent_fd, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED;
+
+	if (async_transid)
+		*async_transid = args.transid;
+
+	return BTRFS_UTIL_OK;
+}
-- 
2.16.1


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

* [PATCH 07/26] libbtrfsutil: add btrfs_util_subvolume_info()
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (5 preceding siblings ...)
  2018-01-26 18:40 ` [PATCH 06/26] libbtrfsutil: add btrfs_util_create_subvolume() Omar Sandoval
@ 2018-01-26 18:40 ` Omar Sandoval
  2018-01-26 18:40 ` [PATCH 08/26] libbtrfsutil: add btrfs_util_[gs]et_read_only() Omar Sandoval
                   ` (19 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

This gets the the information in `btrfs subvolume show` from the root
item.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 libbtrfsutil/btrfsutil.h                    | 109 ++++++++++++++++++++
 libbtrfsutil/python/btrfsutilpy.h           |   3 +
 libbtrfsutil/python/module.c                |  14 +++
 libbtrfsutil/python/subvolume.c             | 113 +++++++++++++++++++++
 libbtrfsutil/python/tests/test_subvolume.py |  50 ++++++++++
 libbtrfsutil/subvolume.c                    | 149 ++++++++++++++++++++++++++++
 6 files changed, 438 insertions(+)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 20029f18..41d5c1e7 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -20,8 +20,10 @@
 #ifndef BTRFS_UTIL_H
 #define BTRFS_UTIL_H
 
+#include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
+#include <sys/time.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -118,6 +120,113 @@ enum btrfs_util_error btrfs_util_subvolume_path(const char *path, uint64_t id,
 enum btrfs_util_error btrfs_util_f_subvolume_path(int fd, uint64_t id,
 						  char **path_ret);
 
+/**
+ * struct btrfs_util_subvolume_info - Information about a Btrfs subvolume.
+ */
+struct btrfs_util_subvolume_info {
+	/** @id: ID of this subvolume, unique across the filesystem. */
+	uint64_t id;
+
+	/**
+	 * @parent_id: ID of the subvolume which contains this subvolume, or
+	 * zero for the root subvolume (BTRFS_FS_TREE_OBJECTID) or orphaned
+	 * subvolumes (i.e., subvolumes which have been deleted but not yet
+	 * cleaned up).
+	 */
+	uint64_t parent_id;
+
+	/**
+	 * @dir_id: Inode number of the directory containing this subvolume in
+	 * the parent subvolume, or zero for the root subvolume
+	 * (BTRFS_FS_TREE_OBJECTID) or orphaned subvolumes.
+	 */
+	uint64_t dir_id;
+
+	/** @flags: On-disk root item flags. */
+	uint64_t flags;
+
+	/** @uuid: UUID of this subvolume. */
+	uint8_t uuid[16];
+
+	/**
+	 * @parent_uuid: UUID of the subvolume this subvolume is a snapshot of,
+	 * or all zeroes if this subvolume is not a snapshot.
+	 */
+	uint8_t parent_uuid[16];
+
+	/**
+	 * @received_uuid: UUID of the subvolume this subvolume was received
+	 * from, or all zeroes if this subvolume was not received. Note that
+	 * this field, @stransid, @rtransid, @stime, and @rtime are set manually
+	 * by userspace after a subvolume is received.
+	 */
+	uint8_t received_uuid[16];
+
+	/** @generation: Transaction ID of the subvolume root. */
+	uint64_t generation;
+
+	/**
+	 * @ctransid: Transaction ID when an inode in this subvolume was last
+	 * changed.
+	 */
+	uint64_t ctransid;
+
+	/** @otransid: Transaction ID when this subvolume was created. */
+	uint64_t otransid;
+
+	/**
+	 * @stransid: Transaction ID of the sent subvolume this subvolume was
+	 * received from, or zero if this subvolume was not received. See the
+	 * note on @received_uuid.
+	 */
+	uint64_t stransid;
+
+	/**
+	 * @rtransid: Transaction ID when this subvolume was received, or zero
+	 * if this subvolume was not received. See the note on @received_uuid.
+	 */
+	uint64_t rtransid;
+
+	/** @ctime: Time when an inode in this subvolume was last changed. */
+	struct timespec ctime;
+
+	/** @otime: Time when this subvolume was created. */
+	struct timespec otime;
+
+	/**
+	 * @stime: Not well-defined, usually zero unless it was set otherwise.
+	 * See the note on @received_uuid.
+	 */
+	struct timespec stime;
+
+	/**
+	 * @rtime: Time when this subvolume was received, or zero if this
+	 * subvolume was not received. See the note on @received_uuid.
+	 */
+	struct timespec rtime;
+};
+
+/**
+ * btrfs_util_subvolume_info() - Get information about a subvolume.
+ * @path: Path in a Btrfs filesystem. This may be any path in the filesystem; it
+ * does not have to refer to a subvolume unless @id is zero.
+ * @id: ID of subvolume to get information about. If zero is given, the
+ * subvolume ID of @path is used.
+ * @subvol: Returned subvolume information. This can be %NULL if you just want
+ * to check whether the subvolume exists; %BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND
+ * will be returned if it does not.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_subvolume_info(const char *path, uint64_t id,
+						struct btrfs_util_subvolume_info *subvol);
+
+/**
+ * btrfs_util_f_subvolume_info() - See btrfs_util_subvolume_info().
+ */
+enum btrfs_util_error btrfs_util_f_subvolume_info(int fd, uint64_t id,
+						  struct btrfs_util_subvolume_info *subvol);
+
 struct btrfs_util_qgroup_inherit;
 
 /**
diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
index 32e83fe5..e601cb8b 100644
--- a/libbtrfsutil/python/btrfsutilpy.h
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -35,6 +35,8 @@ typedef struct {
 } QgroupInherit;
 
 extern PyTypeObject BtrfsUtilError_type;
+extern PyStructSequence_Desc SubvolumeInfo_desc;
+extern PyTypeObject SubvolumeInfo_type;
 extern PyTypeObject QgroupInherit_type;
 
 /*
@@ -61,6 +63,7 @@ void SetFromBtrfsUtilErrorWithPaths(enum btrfs_util_error err,
 PyObject *is_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *subvolume_path(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *subvolume_info(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 
 void add_module_constants(PyObject *m);
diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
index 444516b1..b1469fc9 100644
--- a/libbtrfsutil/python/module.c
+++ b/libbtrfsutil/python/module.c
@@ -152,6 +152,14 @@ static PyMethodDef btrfsutil_methods[] = {
 	 "path -- string, bytes, path-like object, or open file descriptor\n"
 	 "id -- if not zero, instead of returning the subvolume path of the\n"
 	 "given path, return the path of the subvolume with this ID"},
+	{"subvolume_info", (PyCFunction)subvolume_info,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "subvolume_info(path, id=0) -> SubvolumeInfo\n\n"
+	 "Get information about a subvolume.\n\n"
+	 "Arguments:\n"
+	 "path -- string, bytes, path-like object, or open file descriptor\n"
+	 "id -- if not zero, instead of returning information about the\n"
+	 "given path, return information about the subvolume with this ID"},
 	{"create_subvolume", (PyCFunction)create_subvolume,
 	 METH_VARARGS | METH_KEYWORDS,
 	 "create_subvolume(path, async=False)\n\n"
@@ -180,6 +188,9 @@ PyInit_btrfsutil(void)
 	if (PyType_Ready(&BtrfsUtilError_type) < 0)
 		return NULL;
 
+	if (PyStructSequence_InitType2(&SubvolumeInfo_type, &SubvolumeInfo_desc) < 0)
+		return NULL;
+
 	QgroupInherit_type.tp_new = PyType_GenericNew;
 	if (PyType_Ready(&QgroupInherit_type) < 0)
 		return NULL;
@@ -192,6 +203,9 @@ PyInit_btrfsutil(void)
 	PyModule_AddObject(m, "BtrfsUtilError",
 			   (PyObject *)&BtrfsUtilError_type);
 
+	Py_INCREF(&SubvolumeInfo_type);
+	PyModule_AddObject(m, "SubvolumeInfo", (PyObject *)&SubvolumeInfo_type);
+
 	Py_INCREF(&QgroupInherit_type);
 	PyModule_AddObject(m, "QgroupInherit",
 			   (PyObject *)&QgroupInherit_type);
diff --git a/libbtrfsutil/python/subvolume.c b/libbtrfsutil/python/subvolume.c
index bef9fab5..9c323995 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -102,6 +102,119 @@ PyObject *subvolume_path(PyObject *self, PyObject *args, PyObject *kwds)
 	return ret;
 }
 
+static PyObject *subvolume_info_to_object(const struct btrfs_util_subvolume_info *subvol)
+{
+	PyObject *ret, *tmp;
+
+	ret = PyStructSequence_New(&SubvolumeInfo_type);
+	if (ret == NULL)
+		return NULL;
+
+#define SET_UINT64(i, field)					\
+	tmp = PyLong_FromUnsignedLongLong(subvol->field);	\
+	if (tmp == NULL) {					\
+		Py_DECREF(ret);					\
+		return ret;					\
+	}							\
+	PyStructSequence_SET_ITEM(ret, i, tmp);
+
+#define SET_UUID(i, field)						\
+	tmp = PyBytes_FromStringAndSize((char *)subvol->field, 16);	\
+	if (tmp == NULL) {						\
+		Py_DECREF(ret);						\
+		return ret;						\
+	}								\
+	PyStructSequence_SET_ITEM(ret, i, tmp);
+
+#define SET_TIME(i, field)						\
+	tmp = PyFloat_FromDouble(subvol->field.tv_sec +			\
+				 subvol->field.tv_nsec / 1000000000);	\
+	if (tmp == NULL) {						\
+		Py_DECREF(ret);						\
+		return ret;						\
+	}								\
+	PyStructSequence_SET_ITEM(ret, i, tmp);
+
+	SET_UINT64(0, id);
+	SET_UINT64(1, parent_id);
+	SET_UINT64(2, dir_id);
+	SET_UINT64(3, flags);
+	SET_UUID(4, uuid);
+	SET_UUID(5, parent_uuid);
+	SET_UUID(6, received_uuid);
+	SET_UINT64(7, generation);
+	SET_UINT64(8, ctransid);
+	SET_UINT64(9, otransid);
+	SET_UINT64(10, stransid);
+	SET_UINT64(11, rtransid);
+	SET_TIME(12, ctime);
+	SET_TIME(13, otime);
+	SET_TIME(14, stime);
+	SET_TIME(15, rtime);
+
+#undef SET_TIME
+#undef SET_UUID
+#undef SET_UINT64
+
+	return ret;
+}
+
+PyObject *subvolume_info(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *keywords[] = {"path", "id", NULL};
+	struct path_arg path = {.allow_fd = true};
+	struct btrfs_util_subvolume_info subvol;
+	enum btrfs_util_error err;
+	uint64_t id = 0;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:subvolume_info",
+					 keywords, &path_converter, &path, &id))
+		return NULL;
+
+	if (path.path)
+		err = btrfs_util_subvolume_info(path.path, id, &subvol);
+	else
+		err = btrfs_util_f_subvolume_info(path.fd, id, &subvol);
+	if (err) {
+		SetFromBtrfsUtilErrorWithPath(err, &path);
+		path_cleanup(&path);
+		return NULL;
+	}
+
+	path_cleanup(&path);
+
+	return subvolume_info_to_object(&subvol);
+}
+
+static PyStructSequence_Field SubvolumeInfo_fields[] = {
+	{"id", "int ID of this subvolume"},
+	{"parent_id", "int ID of the subvolume containing this subvolume"},
+	{"dir_id", "int inode number of the directory containing this subvolume"},
+	{"flags", "int root item flags"},
+	{"uuid", "bytes UUID of this subvolume"},
+	{"parent_uuid", "bytes UUID of the subvolume this is a snapshot of"},
+	{"received_uuid", "bytes UUID of the subvolume this was received from"},
+	{"generation", "int transaction ID of the subvolume root"},
+	{"ctransid", "int transaction ID when an inode was last changed"},
+	{"otransid", "int transaction ID when this subvolume was created"},
+	{"stransid", "int transaction ID of the sent subvolume this subvolume was received from"},
+	{"rtransid", "int transaction ID when this subvolume was received"},
+	{"ctime", "float time when an inode was last changed"},
+	{"otime", "float time when this subvolume was created"},
+	{"stime", "float time, usually zero"},
+	{"rtime", "float time when this subvolume was received"},
+	{},
+};
+
+PyStructSequence_Desc SubvolumeInfo_desc = {
+	"btrfsutil.SubvolumeInfo",
+	"Information about a Btrfs subvolume.",
+	SubvolumeInfo_fields,
+	14,
+};
+
+PyTypeObject SubvolumeInfo_type;
+
 PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
 {
 	static char *keywords[] = {"path", "async", "qgroup_inherit", NULL};
diff --git a/libbtrfsutil/python/tests/test_subvolume.py b/libbtrfsutil/python/tests/test_subvolume.py
index 57ba27bf..ecb0d7ae 100644
--- a/libbtrfsutil/python/tests/test_subvolume.py
+++ b/libbtrfsutil/python/tests/test_subvolume.py
@@ -87,6 +87,56 @@ class TestSubvolume(BtrfsTestCase):
         finally:
             os.chdir(pwd)
 
+    def test_subvolume_info(self):
+        for arg in self.path_or_fd(self.mountpoint):
+            with self.subTest(type=type(arg)):
+                info = btrfsutil.subvolume_info(arg)
+                self.assertEqual(info.id, 5)
+                self.assertEqual(info.parent_id, 0)
+                self.assertEqual(info.dir_id, 0)
+                self.assertEqual(info.flags, 0)
+                self.assertEqual(info.uuid, bytes(16))
+                self.assertEqual(info.parent_uuid, bytes(16))
+                self.assertEqual(info.received_uuid, bytes(16))
+                self.assertNotEqual(info.generation, 0)
+                self.assertEqual(info.ctransid, 0)
+                self.assertEqual(info.otransid, 0)
+                self.assertEqual(info.stransid, 0)
+                self.assertEqual(info.rtransid, 0)
+                self.assertEqual(info.ctime, 0)
+                self.assertEqual(info.otime, 0)
+                self.assertEqual(info.stime, 0)
+                self.assertEqual(info.rtime, 0)
+
+        subvol = os.path.join(self.mountpoint, 'subvol')
+        btrfsutil.create_subvolume(subvol)
+
+        info = btrfsutil.subvolume_info(subvol)
+        self.assertEqual(info.id, 256)
+        self.assertEqual(info.parent_id, 5)
+        self.assertEqual(info.dir_id, 256)
+        self.assertEqual(info.flags, 0)
+        self.assertIsInstance(info.uuid, bytes)
+        self.assertEqual(info.parent_uuid, bytes(16))
+        self.assertEqual(info.received_uuid, bytes(16))
+        self.assertNotEqual(info.generation, 0)
+        self.assertNotEqual(info.ctransid, 0)
+        self.assertNotEqual(info.otransid, 0)
+        self.assertEqual(info.stransid, 0)
+        self.assertEqual(info.rtransid, 0)
+        self.assertNotEqual(info.ctime, 0)
+        self.assertNotEqual(info.otime, 0)
+        self.assertEqual(info.stime, 0)
+        self.assertEqual(info.rtime, 0)
+
+        # TODO: test received_uuid, stransid, rtransid, stime, and rtime
+
+        for arg in self.path_or_fd(self.mountpoint):
+            with self.subTest(type=type(arg)):
+                with self.assertRaises(btrfsutil.BtrfsUtilError) as e:
+                    # BTRFS_EXTENT_TREE_OBJECTID
+                    btrfsutil.subvolume_info(arg, 2)
+
     def test_create_subvolume(self):
         subvol = os.path.join(self.mountpoint, 'subvol')
 
diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c
index 864b8663..3e94c050 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -265,6 +265,155 @@ enum btrfs_util_error btrfs_util_f_subvolume_path(int fd, uint64_t id,
 	return BTRFS_UTIL_OK;
 }
 
+static void copy_timespec(struct timespec *timespec,
+			  const struct btrfs_timespec *btrfs_timespec)
+{
+	timespec->tv_sec = le64_to_cpu(btrfs_timespec->sec);
+	timespec->tv_nsec = le32_to_cpu(btrfs_timespec->nsec);
+}
+
+static void copy_root_item(struct btrfs_util_subvolume_info *subvol,
+			   const struct btrfs_root_item *root)
+{
+	subvol->flags = le64_to_cpu(root->flags);
+	memcpy(subvol->uuid, root->uuid, sizeof(subvol->uuid));
+	memcpy(subvol->parent_uuid, root->parent_uuid,
+	       sizeof(subvol->parent_uuid));
+	memcpy(subvol->received_uuid, root->received_uuid,
+	       sizeof(subvol->received_uuid));
+	subvol->generation = le64_to_cpu(root->generation);
+	subvol->ctransid = le64_to_cpu(root->ctransid);
+	subvol->otransid = le64_to_cpu(root->otransid);
+	subvol->stransid = le64_to_cpu(root->stransid);
+	subvol->rtransid = le64_to_cpu(root->rtransid);
+	copy_timespec(&subvol->ctime, &root->ctime);
+	copy_timespec(&subvol->otime, &root->otime);
+	copy_timespec(&subvol->stime, &root->stime);
+	copy_timespec(&subvol->rtime, &root->rtime);
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_subvolume_info(const char *path, uint64_t id,
+						struct btrfs_util_subvolume_info *subvol)
+{
+	enum btrfs_util_error err;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+	err = btrfs_util_f_subvolume_info(fd, id, subvol);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_f_subvolume_info(int fd, uint64_t id,
+						  struct btrfs_util_subvolume_info *subvol)
+{
+	struct btrfs_ioctl_search_args search = {
+		.key = {
+			.tree_id = BTRFS_ROOT_TREE_OBJECTID,
+			.min_type = BTRFS_ROOT_ITEM_KEY,
+			.max_type = BTRFS_ROOT_BACKREF_KEY,
+			.min_offset = 0,
+			.max_offset = UINT64_MAX,
+			.min_transid = 0,
+			.max_transid = UINT64_MAX,
+			.nr_items = 0,
+		},
+	};
+	enum btrfs_util_error err;
+	size_t items_pos = 0, buf_off = 0;
+	bool need_root_item = true, need_root_backref = true;
+	int ret;
+
+	if (id == 0) {
+		err = btrfs_util_f_is_subvolume(fd);
+		if (err)
+			return err;
+
+		err = btrfs_util_f_subvolume_id(fd, &id);
+		if (err)
+			return err;
+	}
+
+	if ((id < BTRFS_FIRST_FREE_OBJECTID && id != BTRFS_FS_TREE_OBJECTID) ||
+	    id > BTRFS_LAST_FREE_OBJECTID) {
+		errno = ENOENT;
+		return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
+	}
+
+	search.key.min_objectid = search.key.max_objectid = id;
+
+	if (subvol) {
+		subvol->id = id;
+		subvol->parent_id = 0;
+		subvol->dir_id = 0;
+		if (id == BTRFS_FS_TREE_OBJECTID)
+			need_root_backref = false;
+	} else {
+		/*
+		 * We only need the backref for filling in the subvolume info.
+		 */
+		need_root_backref = false;
+	}
+
+	/* Don't bother searching for the backref if we don't need it. */
+	if (!need_root_backref)
+		search.key.max_type = BTRFS_ROOT_ITEM_KEY;
+
+	while (need_root_item || need_root_backref) {
+		const struct btrfs_ioctl_search_header *header;
+
+		if (items_pos >= search.key.nr_items) {
+			search.key.nr_items = 4096;
+			ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search);
+			if (ret == -1)
+				return BTRFS_UTIL_ERROR_SEARCH_FAILED;
+			items_pos = 0;
+			buf_off = 0;
+
+			if (search.key.nr_items == 0) {
+				if (need_root_item) {
+					errno = ENOENT;
+					return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
+				} else {
+					break;
+				}
+			}
+		}
+
+		header = (struct btrfs_ioctl_search_header *)(search.buf + buf_off);
+		if (header->type == BTRFS_ROOT_ITEM_KEY) {
+			if (subvol) {
+				const struct btrfs_root_item *root;
+
+				root = (const struct btrfs_root_item *)(header + 1);
+				copy_root_item(subvol, root);
+			}
+			need_root_item = false;
+			search.key.min_type = BTRFS_ROOT_BACKREF_KEY;
+		} else if (header->type == BTRFS_ROOT_BACKREF_KEY) {
+			if (subvol) {
+				const struct btrfs_root_ref *ref;
+
+				ref = (const struct btrfs_root_ref *)(header + 1);
+				subvol->parent_id = header->offset;
+				subvol->dir_id = le64_to_cpu(ref->dirid);
+			}
+			need_root_backref = false;
+			search.key.min_type = UINT32_MAX;
+		}
+
+		items_pos++;
+		buf_off += sizeof(*header) + header->len;
+	}
+
+	return BTRFS_UTIL_OK;
+}
+
 static enum btrfs_util_error openat_parent_and_name(int dirfd, const char *path,
 						    char *name, size_t name_len,
 						    int *fd)
-- 
2.16.1


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

* [PATCH 08/26] libbtrfsutil: add btrfs_util_[gs]et_read_only()
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (6 preceding siblings ...)
  2018-01-26 18:40 ` [PATCH 07/26] libbtrfsutil: add btrfs_util_subvolume_info() Omar Sandoval
@ 2018-01-26 18:40 ` Omar Sandoval
  2018-01-26 18:40 ` [PATCH 09/26] libbtrfsutil: add btrfs_util_[gs]et_default_subvolume() Omar Sandoval
                   ` (18 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

In the future, btrfs_util_[gs]et_subvolume_flags() might be useful, but
since these are the only subvolume flags we've defined in all this time,
this will do for now.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 libbtrfsutil/btrfsutil.h                    | 33 ++++++++++++++
 libbtrfsutil/python/btrfsutilpy.h           |  2 +
 libbtrfsutil/python/module.c                | 13 ++++++
 libbtrfsutil/python/subvolume.c             | 53 ++++++++++++++++++++++
 libbtrfsutil/python/tests/test_subvolume.py | 17 +++++++
 libbtrfsutil/subvolume.c                    | 69 +++++++++++++++++++++++++++++
 6 files changed, 187 insertions(+)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 41d5c1e7..d187d3a7 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -227,6 +227,39 @@ enum btrfs_util_error btrfs_util_subvolume_info(const char *path, uint64_t id,
 enum btrfs_util_error btrfs_util_f_subvolume_info(int fd, uint64_t id,
 						  struct btrfs_util_subvolume_info *subvol);
 
+/**
+ * btrfs_util_get_subvolume_read_only() - Get whether a subvolume is read-only.
+ * @path: Subvolume path.
+ * @ret: Returned read-only flag.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_get_subvolume_read_only(const char *path,
+							 bool *ret);
+
+/**
+ * btrfs_util_f_get_subvolume_read_only() - See
+ * btrfs_util_get_subvolume_read_only().
+ */
+enum btrfs_util_error btrfs_util_f_get_subvolume_read_only(int fd, bool *ret);
+
+/**
+ * btrfs_util_set_subvolume_read_only() - Set whether a subvolume is read-only.
+ * @path: Subvolume path.
+ * @read_only: New value of read-only flag.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_set_subvolume_read_only(const char *path,
+							 bool read_only);
+
+/**
+ * btrfs_util_f_set_subvolume_read_only() - See
+ * btrfs_util_set_subvolume_read_only().
+ */
+enum btrfs_util_error btrfs_util_f_set_subvolume_read_only(int fd,
+							   bool read_only);
+
 struct btrfs_util_qgroup_inherit;
 
 /**
diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
index e601cb8b..21253e51 100644
--- a/libbtrfsutil/python/btrfsutilpy.h
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -64,6 +64,8 @@ PyObject *is_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *subvolume_path(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *subvolume_info(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *get_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *set_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 
 void add_module_constants(PyObject *m);
diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
index b1469fc9..3395fb14 100644
--- a/libbtrfsutil/python/module.c
+++ b/libbtrfsutil/python/module.c
@@ -160,6 +160,19 @@ static PyMethodDef btrfsutil_methods[] = {
 	 "path -- string, bytes, path-like object, or open file descriptor\n"
 	 "id -- if not zero, instead of returning information about the\n"
 	 "given path, return information about the subvolume with this ID"},
+	{"get_subvolume_read_only", (PyCFunction)get_subvolume_read_only,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "get_subvolume_read_only(path) -> bool\n\n"
+	 "Get whether a subvolume is read-only.\n\n"
+	 "Arguments:\n"
+	 "path -- string, bytes, path-like object, or open file descriptor"},
+	{"set_subvolume_read_only", (PyCFunction)set_subvolume_read_only,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "set_subvolume_read_only(path, read_only=True)\n\n"
+	 "Set whether a subvolume is read-only.\n\n"
+	 "Arguments:\n"
+	 "path -- string, bytes, path-like object, or open file descriptor\n"
+	 "read_only -- bool flag value"},
 	{"create_subvolume", (PyCFunction)create_subvolume,
 	 METH_VARARGS | METH_KEYWORDS,
 	 "create_subvolume(path, async=False)\n\n"
diff --git a/libbtrfsutil/python/subvolume.c b/libbtrfsutil/python/subvolume.c
index 9c323995..9ae7729f 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -215,6 +215,59 @@ PyStructSequence_Desc SubvolumeInfo_desc = {
 
 PyTypeObject SubvolumeInfo_type;
 
+PyObject *get_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *keywords[] = {"path", NULL};
+	struct path_arg path = {.allow_fd = true};
+	enum btrfs_util_error err;
+	bool read_only;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds,
+					 "O&:get_subvolume_read_only",
+					 keywords, &path_converter, &path))
+		return NULL;
+
+	if (path.path)
+		err = btrfs_util_get_subvolume_read_only(path.path, &read_only);
+	else
+		err = btrfs_util_f_get_subvolume_read_only(path.fd, &read_only);
+	if (err) {
+		SetFromBtrfsUtilErrorWithPath(err, &path);
+		path_cleanup(&path);
+		return NULL;
+	}
+
+	path_cleanup(&path);
+	return PyBool_FromLong(read_only);
+}
+
+PyObject *set_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *keywords[] = {"path", "read_only", NULL};
+	struct path_arg path = {.allow_fd = true};
+	enum btrfs_util_error err;
+	int read_only = 1;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds,
+					 "O&|p:set_subvolume_read_only",
+					 keywords, &path_converter, &path,
+					 &read_only))
+		return NULL;
+
+	if (path.path)
+		err = btrfs_util_set_subvolume_read_only(path.path, read_only);
+	else
+		err = btrfs_util_f_set_subvolume_read_only(path.fd, read_only);
+	if (err) {
+		SetFromBtrfsUtilErrorWithPath(err, &path);
+		path_cleanup(&path);
+		return NULL;
+	}
+
+	path_cleanup(&path);
+	Py_RETURN_NONE;
+}
+
 PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
 {
 	static char *keywords[] = {"path", "async", "qgroup_inherit", NULL};
diff --git a/libbtrfsutil/python/tests/test_subvolume.py b/libbtrfsutil/python/tests/test_subvolume.py
index ecb0d7ae..23871de9 100644
--- a/libbtrfsutil/python/tests/test_subvolume.py
+++ b/libbtrfsutil/python/tests/test_subvolume.py
@@ -137,6 +137,23 @@ class TestSubvolume(BtrfsTestCase):
                     # BTRFS_EXTENT_TREE_OBJECTID
                     btrfsutil.subvolume_info(arg, 2)
 
+    def test_read_only(self):
+        for arg in self.path_or_fd(self.mountpoint):
+            with self.subTest(type=type(arg)):
+                btrfsutil.set_subvolume_read_only(arg)
+                self.assertTrue(btrfsutil.get_subvolume_read_only(arg))
+                self.assertTrue(btrfsutil.subvolume_info(arg).flags & 1)
+
+                btrfsutil.set_subvolume_read_only(arg, False)
+                self.assertFalse(btrfsutil.get_subvolume_read_only(arg))
+                self.assertFalse(btrfsutil.subvolume_info(arg).flags & 1)
+
+                btrfsutil.set_subvolume_read_only(arg, True)
+                self.assertTrue(btrfsutil.get_subvolume_read_only(arg))
+                self.assertTrue(btrfsutil.subvolume_info(arg).flags & 1)
+
+                btrfsutil.set_subvolume_read_only(arg, False)
+
     def test_create_subvolume(self):
         subvol = os.path.join(self.mountpoint, 'subvol')
 
diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c
index 3e94c050..394c7cbd 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -414,6 +414,75 @@ enum btrfs_util_error btrfs_util_f_subvolume_info(int fd, uint64_t id,
 	return BTRFS_UTIL_OK;
 }
 
+enum btrfs_util_error btrfs_util_f_get_subvolume_read_only(int fd,
+							   bool *read_only_ret)
+{
+	uint64_t flags;
+	int ret;
+
+	ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED;
+
+	*read_only_ret = flags & BTRFS_SUBVOL_RDONLY;
+	return BTRFS_UTIL_OK;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_get_subvolume_read_only(const char *path,
+							 bool *ret)
+{
+	enum btrfs_util_error err;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+	err = btrfs_util_f_get_subvolume_read_only(fd, ret);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_set_subvolume_read_only(const char *path,
+							 bool read_only)
+{
+	enum btrfs_util_error err;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+	err = btrfs_util_f_set_subvolume_read_only(fd, read_only);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_f_set_subvolume_read_only(int fd,
+							   bool read_only)
+{
+	uint64_t flags;
+	int ret;
+
+	ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED;
+
+	if (read_only)
+		flags |= BTRFS_SUBVOL_RDONLY;
+	else
+		flags &= ~BTRFS_SUBVOL_RDONLY;
+
+	ret = ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &flags);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_SUBVOL_SETFLAGS_FAILED;
+
+	return BTRFS_UTIL_OK;
+}
+
 static enum btrfs_util_error openat_parent_and_name(int dirfd, const char *path,
 						    char *name, size_t name_len,
 						    int *fd)
-- 
2.16.1


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

* [PATCH 09/26] libbtrfsutil: add btrfs_util_[gs]et_default_subvolume()
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (7 preceding siblings ...)
  2018-01-26 18:40 ` [PATCH 08/26] libbtrfsutil: add btrfs_util_[gs]et_read_only() Omar Sandoval
@ 2018-01-26 18:40 ` Omar Sandoval
  2018-01-26 18:40 ` [PATCH 10/26] libbtrfsutil: add subvolume iterator helpers Omar Sandoval
                   ` (17 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

set_default_subvolume() is a trivial ioctl(), but there's no ioctl() for
get_default_subvolume(), so we need to search the root tree.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 libbtrfsutil/btrfsutil.h                    |  37 +++++++++
 libbtrfsutil/python/btrfsutilpy.h           |   2 +
 libbtrfsutil/python/module.c                |  14 ++++
 libbtrfsutil/python/subvolume.c             |  50 ++++++++++++
 libbtrfsutil/python/tests/test_subvolume.py |  14 ++++
 libbtrfsutil/subvolume.c                    | 116 ++++++++++++++++++++++++++++
 6 files changed, 233 insertions(+)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index d187d3a7..1643ea7b 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -260,6 +260,43 @@ enum btrfs_util_error btrfs_util_set_subvolume_read_only(const char *path,
 enum btrfs_util_error btrfs_util_f_set_subvolume_read_only(int fd,
 							   bool read_only);
 
+/**
+ * btrfs_util_get_default_subvolume() - Get the default subvolume for a
+ * filesystem.
+ * @path: Path on a Btrfs filesystem.
+ * @id_ret: Returned subvolume ID.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_get_default_subvolume(const char *path,
+						       uint64_t *id_ret);
+
+/**
+ * btrfs_util_f_get_default_subvolume() - See
+ * btrfs_util_get_default_subvolume().
+ */
+enum btrfs_util_error btrfs_util_f_get_default_subvolume(int fd,
+							 uint64_t *id_ret);
+
+/**
+ * btrfs_util_set_default_subvolume() - Set the default subvolume for a
+ * filesystem.
+ * @path: Path in a Btrfs filesystem. This may be any path in the filesystem; it
+ * does not have to refer to a subvolume unless @id is zero.
+ * @id: ID of subvolume to set as the default. If zero is given, the subvolume
+ * ID of @path is used.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_set_default_subvolume(const char *path,
+						       uint64_t id);
+
+/**
+ * btrfs_util_f_set_default_subvolume() - See
+ * btrfs_util_set_default_subvolume().
+ */
+enum btrfs_util_error btrfs_util_f_set_default_subvolume(int fd, uint64_t id);
+
 struct btrfs_util_qgroup_inherit;
 
 /**
diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
index 21253e51..41314d4a 100644
--- a/libbtrfsutil/python/btrfsutilpy.h
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -66,6 +66,8 @@ PyObject *subvolume_path(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *subvolume_info(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *get_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *set_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *get_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *set_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 
 void add_module_constants(PyObject *m);
diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
index 3395fb14..0ac4d63a 100644
--- a/libbtrfsutil/python/module.c
+++ b/libbtrfsutil/python/module.c
@@ -173,6 +173,20 @@ static PyMethodDef btrfsutil_methods[] = {
 	 "Arguments:\n"
 	 "path -- string, bytes, path-like object, or open file descriptor\n"
 	 "read_only -- bool flag value"},
+	{"get_default_subvolume", (PyCFunction)get_default_subvolume,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "get_default_subvolume(path) -> int\n\n"
+	 "Get the ID of the default subvolume of a filesystem.\n\n"
+	 "Arguments:\n"
+	 "path -- string, bytes, path-like object, or open file descriptor"},
+	{"set_default_subvolume", (PyCFunction)set_default_subvolume,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "set_default_subvolume(path, id=0)\n\n"
+	 "Set the default subvolume of a filesystem.\n\n"
+	 "Arguments:\n"
+	 "path -- string, bytes, path-like object, or open file descriptor\n"
+	 "id -- if not zero, set the default subvolume to the subvolume with\n"
+	 "this ID instead of the given path"},
 	{"create_subvolume", (PyCFunction)create_subvolume,
 	 METH_VARARGS | METH_KEYWORDS,
 	 "create_subvolume(path, async=False)\n\n"
diff --git a/libbtrfsutil/python/subvolume.c b/libbtrfsutil/python/subvolume.c
index 9ae7729f..7b30e03c 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -268,6 +268,56 @@ PyObject *set_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds
 	Py_RETURN_NONE;
 }
 
+PyObject *get_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *keywords[] = {"path", NULL};
+	struct path_arg path = {.allow_fd = true};
+	enum btrfs_util_error err;
+	uint64_t id;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:get_default_subvolume",
+					 keywords, &path_converter, &path))
+		return NULL;
+
+	if (path.path)
+		err = btrfs_util_get_default_subvolume(path.path, &id);
+	else
+		err = btrfs_util_f_get_default_subvolume(path.fd, &id);
+	if (err) {
+		SetFromBtrfsUtilErrorWithPath(err, &path);
+		path_cleanup(&path);
+		return NULL;
+	}
+
+	path_cleanup(&path);
+	return PyLong_FromUnsignedLongLong(id);
+}
+
+PyObject *set_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *keywords[] = {"path", "id", NULL};
+	struct path_arg path = {.allow_fd = true};
+	enum btrfs_util_error err;
+	uint64_t id = 0;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:set_default_subvolume",
+					 keywords, &path_converter, &path, &id))
+		return NULL;
+
+	if (path.path)
+		err = btrfs_util_set_default_subvolume(path.path, id);
+	else
+		err = btrfs_util_f_set_default_subvolume(path.fd, id);
+	if (err) {
+		SetFromBtrfsUtilErrorWithPath(err, &path);
+		path_cleanup(&path);
+		return NULL;
+	}
+
+	path_cleanup(&path);
+	Py_RETURN_NONE;
+}
+
 PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
 {
 	static char *keywords[] = {"path", "async", "qgroup_inherit", NULL};
diff --git a/libbtrfsutil/python/tests/test_subvolume.py b/libbtrfsutil/python/tests/test_subvolume.py
index 23871de9..937a4397 100644
--- a/libbtrfsutil/python/tests/test_subvolume.py
+++ b/libbtrfsutil/python/tests/test_subvolume.py
@@ -154,6 +154,20 @@ class TestSubvolume(BtrfsTestCase):
 
                 btrfsutil.set_subvolume_read_only(arg, False)
 
+    def test_default_subvolume(self):
+        for arg in self.path_or_fd(self.mountpoint):
+            with self.subTest(type=type(arg)):
+                self.assertEqual(btrfsutil.get_default_subvolume(arg), 5)
+
+        subvol = os.path.join(self.mountpoint, 'subvol')
+        btrfsutil.create_subvolume(subvol)
+        for arg in self.path_or_fd(subvol):
+            with self.subTest(type=type(arg)):
+                btrfsutil.set_default_subvolume(arg)
+                self.assertEqual(btrfsutil.get_default_subvolume(arg), 256)
+                btrfsutil.set_default_subvolume(arg, 5)
+                self.assertEqual(btrfsutil.get_default_subvolume(arg), 5)
+
     def test_create_subvolume(self):
         subvol = os.path.join(self.mountpoint, 'subvol')
 
diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c
index 394c7cbd..eb57b4c4 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -483,6 +483,122 @@ enum btrfs_util_error btrfs_util_f_set_subvolume_read_only(int fd,
 	return BTRFS_UTIL_OK;
 }
 
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_get_default_subvolume(const char *path,
+						       uint64_t *id_ret)
+{
+	enum btrfs_util_error err;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+	err = btrfs_util_f_get_default_subvolume(fd, id_ret);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_f_get_default_subvolume(int fd,
+							 uint64_t *id_ret)
+{
+	struct btrfs_ioctl_search_args search = {
+		.key = {
+			.tree_id = BTRFS_ROOT_TREE_OBJECTID,
+			.min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID,
+			.max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID,
+			.min_type = BTRFS_DIR_ITEM_KEY,
+			.max_type = BTRFS_DIR_ITEM_KEY,
+			.min_offset = 0,
+			.max_offset = UINT64_MAX,
+			.min_transid = 0,
+			.max_transid = UINT64_MAX,
+			.nr_items = 0,
+		},
+	};
+	size_t items_pos = 0, buf_off = 0;
+	int ret;
+
+	for (;;) {
+		const struct btrfs_ioctl_search_header *header;
+
+		if (items_pos >= search.key.nr_items) {
+			search.key.nr_items = 4096;
+			ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search);
+			if (ret == -1)
+				return BTRFS_UTIL_ERROR_SEARCH_FAILED;
+			items_pos = 0;
+			buf_off = 0;
+
+			if (search.key.nr_items == 0) {
+				errno = ENOENT;
+				return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
+			}
+		}
+
+		header = (struct btrfs_ioctl_search_header *)(search.buf + buf_off);
+		if (header->type == BTRFS_DIR_ITEM_KEY) {
+			const struct btrfs_dir_item *dir;
+			const char *name;
+			uint16_t name_len;
+
+			dir = (struct btrfs_dir_item *)(header + 1);
+			name = (const char *)(dir + 1);
+			name_len = le16_to_cpu(dir->name_len);
+			if (strncmp(name, "default", name_len) == 0) {
+				*id_ret = le64_to_cpu(dir->location.objectid);
+				break;
+			}
+		}
+
+		items_pos++;
+		buf_off += sizeof(*header) + header->len;
+		search.key.min_offset = header->offset + 1;
+	}
+
+	return BTRFS_UTIL_OK;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_set_default_subvolume(const char *path,
+						       uint64_t id)
+{
+	enum btrfs_util_error err;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+	err = btrfs_util_f_set_default_subvolume(fd, id);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_f_set_default_subvolume(int fd, uint64_t id)
+{
+	enum btrfs_util_error err;
+	int ret;
+
+	if (id == 0) {
+		err = btrfs_util_f_is_subvolume(fd);
+		if (err)
+			return err;
+
+		err = btrfs_util_f_subvolume_id(fd, &id);
+		if (err)
+			return err;
+	}
+
+	ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &id);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_DEFAULT_SUBVOL_FAILED;
+
+	return BTRFS_UTIL_OK;
+}
+
 static enum btrfs_util_error openat_parent_and_name(int dirfd, const char *path,
 						    char *name, size_t name_len,
 						    int *fd)
-- 
2.16.1


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

* [PATCH 10/26] libbtrfsutil: add subvolume iterator helpers
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (8 preceding siblings ...)
  2018-01-26 18:40 ` [PATCH 09/26] libbtrfsutil: add btrfs_util_[gs]et_default_subvolume() Omar Sandoval
@ 2018-01-26 18:40 ` Omar Sandoval
  2018-01-26 18:40 ` [PATCH 11/26] libbtrfsutil: add btrfs_util_create_snapshot() Omar Sandoval
                   ` (16 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

This is how we can implement stuff like `btrfs subvol list`. Rather than
producing the entire list upfront, the iterator approach uses less
memory in the common case where the whole list is not stored (O(max
subvolume path length)). It supports both pre-order traversal (useful
for, e.g, recursive snapshot) and post-order traversal (useful for
recursive delete).

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 libbtrfsutil/btrfsutil.h                    |  92 ++++++++
 libbtrfsutil/python/btrfsutilpy.h           |   1 +
 libbtrfsutil/python/module.c                |   8 +
 libbtrfsutil/python/subvolume.c             | 211 ++++++++++++++++++
 libbtrfsutil/python/tests/test_subvolume.py |  56 +++++
 libbtrfsutil/subvolume.c                    | 322 ++++++++++++++++++++++++++++
 6 files changed, 690 insertions(+)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 1643ea7b..3f78a34d 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -332,6 +332,98 @@ enum btrfs_util_error btrfs_util_f_create_subvolume(int parent_fd,
 						    uint64_t *async_transid,
 						    struct btrfs_util_qgroup_inherit *qgroup_inherit);
 
+struct btrfs_util_subvolume_iterator;
+
+/**
+ * BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER - Iterate post-order. The default
+ * behavior is pre-order, e.g., foo will be yielded before foo/bar. If this flag
+ * is specified, foo/bar will be yielded before foo.
+ */
+#define BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER (1 << 0)
+#define BTRFS_UTIL_SUBVOLUME_ITERATOR_MASK ((1 << 1) - 1)
+
+/**
+ * btrfs_util_create_subvolume_iterator() - Create an iterator over subvolumes
+ * in a Btrfs filesystem.
+ * @path: Path in a Btrfs filesystem. This may be any path in the filesystem; it
+ * does not have to refer to a subvolume unless @top is zero.
+ * @top: List subvolumes beneath (but not including) the subvolume with this ID.
+ * If zero is given, the subvolume ID of @path is used. To list all subvolumes,
+ * pass %BTRFS_FS_TREE_OBJECTID (i.e., 5). The returned paths are relative to
+ * the subvolume with this ID.
+ * @flags: Bitmask of BTRFS_UTIL_SUBVOLUME_ITERATOR_* flags.
+ * @ret: Returned iterator.
+ *
+ * The returned iterator must be freed with
+ * btrfs_util_destroy_subvolume_iterator().
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_create_subvolume_iterator(const char *path,
+							   uint64_t top,
+							   int flags,
+							   struct btrfs_util_subvolume_iterator **ret);
+
+/**
+ * btrfs_util_f_create_subvolume_iterator() - See
+ * btrfs_util_create_subvolume_iterator().
+ */
+enum btrfs_util_error btrfs_util_f_create_subvolume_iterator(int fd, uint64_t top,
+							     int flags,
+							     struct btrfs_util_subvolume_iterator **ret);
+
+/**
+ * btrfs_util_destroy_subvolume_iterator() - Destroy a subvolume iterator
+ * previously created by btrfs_util_create_subvolume_iterator().
+ * @iter: Iterator to destroy.
+ */
+void btrfs_util_destroy_subvolume_iterator(struct btrfs_util_subvolume_iterator *iter);
+
+/**
+ * btrfs_util_subvolume_iterator_fd() - Get the file descriptor associated with
+ * a subvolume iterator.
+ * @iter: Iterator to get.
+ *
+ * This can be used to get the file descriptor opened by
+ * btrfs_util_create_subvolume_iterator() in order to use it for other
+ * functions.
+ *
+ * Return: File descriptor.
+ */
+int btrfs_util_subvolume_iterator_fd(const struct btrfs_util_subvolume_iterator *iter);
+
+/**
+ * btrfs_util_subvolume_iterator_next() - Get the next subvolume from a
+ * subvolume iterator.
+ * @iter: Subvolume iterator.
+ * @path_ret: Returned subvolume path, relative to the subvolume ID used to
+ * create the iterator. May be %NULL.
+ * Must be freed with free().
+ * @id_ret: Returned subvolume ID. May be %NULL.
+ *
+ * Return: %BTRFS_UTIL_OK on success, %BTRFS_UTIL_ERROR_STOP_ITERATION if there
+ * are no more subvolumes, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_subvolume_iterator_next(struct btrfs_util_subvolume_iterator *iter,
+							 char **path_ret,
+							 uint64_t *id_ret);
+
+/**
+ * btrfs_util_subvolume_iterator_next_info() - Get information about the next
+ * subvolume for a subvolume iterator.
+ * @iter: Subvolume iterator.
+ * @path_ret: See btrfs_util_subvolume_iterator_next().
+ * @subvol: Returned subvolume information.
+ *
+ * This convenience function basically combines
+ * btrfs_util_subvolume_iterator_next() and btrfs_util_subvolume_info().
+ *
+ * Return: See btrfs_util_subvolume_iterator_next().
+ */
+enum btrfs_util_error btrfs_util_subvolume_iterator_next_info(struct btrfs_util_subvolume_iterator *iter,
+							      char **path_ret,
+							      struct btrfs_util_subvolume_info *subvol);
+
 /**
  * btrfs_util_create_qgroup_inherit() - Create a qgroup inheritance specifier
  * for btrfs_util_create_subvolume() or btrfs_util_create_snapshot().
diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
index 41314d4a..a9c15219 100644
--- a/libbtrfsutil/python/btrfsutilpy.h
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -37,6 +37,7 @@ typedef struct {
 extern PyTypeObject BtrfsUtilError_type;
 extern PyStructSequence_Desc SubvolumeInfo_desc;
 extern PyTypeObject SubvolumeInfo_type;
+extern PyTypeObject SubvolumeIterator_type;
 extern PyTypeObject QgroupInherit_type;
 
 /*
diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
index 0ac4d63a..daf0747f 100644
--- a/libbtrfsutil/python/module.c
+++ b/libbtrfsutil/python/module.c
@@ -218,6 +218,10 @@ PyInit_btrfsutil(void)
 	if (PyStructSequence_InitType2(&SubvolumeInfo_type, &SubvolumeInfo_desc) < 0)
 		return NULL;
 
+	SubvolumeIterator_type.tp_new = PyType_GenericNew;
+	if (PyType_Ready(&SubvolumeIterator_type) < 0)
+		return NULL;
+
 	QgroupInherit_type.tp_new = PyType_GenericNew;
 	if (PyType_Ready(&QgroupInherit_type) < 0)
 		return NULL;
@@ -233,6 +237,10 @@ PyInit_btrfsutil(void)
 	Py_INCREF(&SubvolumeInfo_type);
 	PyModule_AddObject(m, "SubvolumeInfo", (PyObject *)&SubvolumeInfo_type);
 
+	Py_INCREF(&SubvolumeIterator_type);
+	PyModule_AddObject(m, "SubvolumeIterator",
+			   (PyObject *)&SubvolumeIterator_type);
+
 	Py_INCREF(&QgroupInherit_type);
 	PyModule_AddObject(m, "QgroupInherit",
 			   (PyObject *)&QgroupInherit_type);
diff --git a/libbtrfsutil/python/subvolume.c b/libbtrfsutil/python/subvolume.c
index 7b30e03c..23163176 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -346,3 +346,214 @@ PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
 	else
 		Py_RETURN_NONE;
 }
+
+typedef struct {
+	PyObject_HEAD
+	struct btrfs_util_subvolume_iterator *iter;
+	bool info;
+} SubvolumeIterator;
+
+static void SubvolumeIterator_dealloc(SubvolumeIterator *self)
+{
+	btrfs_util_destroy_subvolume_iterator(self->iter);
+	Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static PyObject *SubvolumeIterator_next(SubvolumeIterator *self)
+{
+	enum btrfs_util_error err;
+	PyObject *ret, *tmp;
+	char *path;
+
+	if (!self->iter) {
+		PyErr_SetString(PyExc_ValueError,
+				"operation on closed iterator");
+		return NULL;
+	}
+
+	if (self->info) {
+		struct btrfs_util_subvolume_info subvol;
+
+		err = btrfs_util_subvolume_iterator_next_info(self->iter, &path,
+							      &subvol);
+		if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
+			PyErr_SetNone(PyExc_StopIteration);
+			return NULL;
+		} else if (err) {
+			SetFromBtrfsUtilError(err);
+			return NULL;
+		}
+
+		tmp = subvolume_info_to_object(&subvol);
+	} else {
+		uint64_t id;
+
+		err = btrfs_util_subvolume_iterator_next(self->iter, &path, &id);
+		if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
+			PyErr_SetNone(PyExc_StopIteration);
+			return NULL;
+		} else if (err) {
+			SetFromBtrfsUtilError(err);
+			return NULL;
+		}
+
+		tmp = PyLong_FromUnsignedLongLong(id);
+
+	}
+	if (tmp) {
+		ret = Py_BuildValue("O&O", PyUnicode_DecodeFSDefault, path,
+				    tmp);
+		Py_DECREF(tmp);
+		free(path);
+	} else {
+		ret = NULL;
+	}
+	return ret;
+}
+
+static int SubvolumeIterator_init(SubvolumeIterator *self, PyObject *args,
+				  PyObject *kwds)
+{
+	static char *keywords[] = {"path", "top", "info", "post_order", NULL};
+	struct path_arg path = {.allow_fd = true};
+	enum btrfs_util_error err;
+	unsigned long long top = 5;
+	int info = 0;
+	int post_order = 0;
+	int flags = 0;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|Kpp:SubvolumeIterator",
+					 keywords, &path_converter, &path, &top,
+					 &info, &post_order))
+		return -1;
+
+	if (post_order)
+		flags |= BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER;
+
+	if (path.path) {
+		err = btrfs_util_create_subvolume_iterator(path.path, top,
+							   flags, &self->iter);
+	} else {
+		err = btrfs_util_f_create_subvolume_iterator(path.fd, top,
+							     flags,
+							     &self->iter);
+	}
+	if (err) {
+		SetFromBtrfsUtilErrorWithPath(err, &path);
+		path_cleanup(&path);
+		return -1;
+	}
+
+	self->info = info;
+
+	return 0;
+}
+
+static PyObject *SubvolumeIterator_close(SubvolumeIterator *self)
+{
+	if (self->iter) {
+		btrfs_util_destroy_subvolume_iterator(self->iter);
+		self->iter = NULL;
+	}
+	Py_RETURN_NONE;
+}
+
+static PyObject *SubvolumeIterator_fileno(SubvolumeIterator *self)
+{
+	if (!self->iter) {
+		PyErr_SetString(PyExc_ValueError,
+				"operation on closed iterator");
+		return NULL;
+	}
+	return PyLong_FromLong(btrfs_util_subvolume_iterator_fd(self->iter));
+}
+
+static PyObject *SubvolumeIterator_enter(SubvolumeIterator *self)
+{
+	Py_INCREF((PyObject *)self);
+	return (PyObject *)self;
+}
+
+static PyObject *SubvolumeIterator_exit(SubvolumeIterator *self, PyObject *args,
+				       PyObject *kwds)
+{
+	static char *keywords[] = {"exc_type", "exc_value", "traceback", NULL};
+	PyObject *exc_type, *exc_value, *traceback;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOO:__exit__", keywords,
+					 &exc_type, &exc_value, &traceback))
+		return NULL;
+
+	return SubvolumeIterator_close(self);
+}
+
+#define SubvolumeIterator_DOC	\
+	 "SubvolumeIterator(path, top=0, info=False, post_order=False) -> new subvolume iterator\n\n"	\
+	 "Create a new iterator that produces tuples of (path, ID) representing\n"	\
+	 "subvolumes on a filesystem.\n\n"						\
+	 "Arguments:\n"									\
+	 "path -- string, bytes, path-like object, or open file descriptor in\n"	\
+	 "filesystem to list\n"								\
+	 "top -- if not zero, instead of only listing subvolumes beneath the\n"		\
+	 "given path, list subvolumes beneath the subvolume with this ID; passing\n"	\
+	 "BTRFS_FS_TREE_OBJECTID (5) here lists all subvolumes. The subvolumes\n"	\
+	 "are listed relative to the subvolume with this ID.\n"				\
+	 "info -- bool indicating the iterator should yield SubvolumeInfo instead of\n"	\
+	 "the subvolume ID\n"								\
+	 "post_order -- bool indicating whether to yield parent subvolumes before\n"	\
+	 "child subvolumes (e.g., 'foo/bar' before 'foo')"
+
+static PyMethodDef SubvolumeIterator_methods[] = {
+	{"close", (PyCFunction)SubvolumeIterator_close,
+	 METH_NOARGS,
+	 "close()\n\n"
+	 "Close this iterator."},
+	{"fileno", (PyCFunction)SubvolumeIterator_fileno,
+	 METH_NOARGS,
+	 "fileno() -> int\n\n"
+	 "Get the file descriptor associated with this iterator."},
+	{"__enter__", (PyCFunction)SubvolumeIterator_enter,
+	 METH_NOARGS, ""},
+	{"__exit__", (PyCFunction)SubvolumeIterator_exit,
+	 METH_VARARGS | METH_KEYWORDS, ""},
+	{},
+};
+
+PyTypeObject SubvolumeIterator_type = {
+	PyVarObject_HEAD_INIT(NULL, 0)
+	"btrfsutil.SubvolumeIterator",		/* tp_name */
+	sizeof(SubvolumeIterator),		/* tp_basicsize */
+	0,					/* tp_itemsize */
+	(destructor)SubvolumeIterator_dealloc,	/* tp_dealloc */
+	NULL,					/* tp_print */
+	NULL,					/* tp_getattr */
+	NULL,					/* tp_setattr */
+	NULL,					/* tp_as_async */
+	NULL,					/* tp_repr */
+	NULL,					/* tp_as_number */
+	NULL,					/* tp_as_sequence */
+	NULL,					/* tp_as_mapping */
+	NULL,					/* tp_hash  */
+	NULL,					/* tp_call */
+	NULL,					/* tp_str */
+	NULL,					/* tp_getattro */
+	NULL,					/* tp_setattro */
+	NULL,					/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT,			/* tp_flags */
+	SubvolumeIterator_DOC,			/* tp_doc */
+	NULL,					/* tp_traverse */
+	NULL,					/* tp_clear */
+	NULL,					/* tp_richcompare */
+	0,					/* tp_weaklistoffset */
+	PyObject_SelfIter,			/* tp_iter */
+	(iternextfunc)SubvolumeIterator_next,	/* tp_iternext */
+	SubvolumeIterator_methods,		/* tp_methods */
+	NULL,					/* tp_members */
+	NULL,					/* tp_getset */
+	NULL,					/* tp_base */
+	NULL,					/* tp_dict */
+	NULL,					/* tp_descr_get */
+	NULL,					/* tp_descr_set */
+	0,					/* tp_dictoffset */
+	(initproc)SubvolumeIterator_init,	/* tp_init */
+};
diff --git a/libbtrfsutil/python/tests/test_subvolume.py b/libbtrfsutil/python/tests/test_subvolume.py
index 937a4397..b43beca7 100644
--- a/libbtrfsutil/python/tests/test_subvolume.py
+++ b/libbtrfsutil/python/tests/test_subvolume.py
@@ -214,3 +214,59 @@ class TestSubvolume(BtrfsTestCase):
         wstatus = os.waitpid(pid, 0)[1]
         self.assertTrue(os.WIFEXITED(wstatus))
         self.assertEqual(os.WEXITSTATUS(wstatus), 0)
+
+    def test_subvolume_iterator(self):
+        pwd = os.getcwd()
+        try:
+            os.chdir(self.mountpoint)
+            btrfsutil.create_subvolume('foo')
+
+            path, subvol = next(btrfsutil.SubvolumeIterator('.', info=True))
+            self.assertEqual(path, 'foo')
+            self.assertIsInstance(subvol, btrfsutil.SubvolumeInfo)
+            self.assertEqual(subvol.id, 256)
+            self.assertEqual(subvol.parent_id, 5)
+
+            btrfsutil.create_subvolume('foo/bar')
+            btrfsutil.create_subvolume('foo/bar/baz')
+
+            subvols = [
+                ('foo', 256),
+                ('foo/bar', 257),
+                ('foo/bar/baz', 258),
+            ]
+
+            for arg in self.path_or_fd('.'):
+                with self.subTest(type=type(arg)):
+                    self.assertEqual(list(btrfsutil.SubvolumeIterator(arg)), subvols)
+            self.assertEqual(list(btrfsutil.SubvolumeIterator('.', top=0)), subvols)
+
+            self.assertEqual(list(btrfsutil.SubvolumeIterator('.', post_order=True)),
+                             [('foo/bar/baz', 258),
+                              ('foo/bar', 257),
+                              ('foo', 256)])
+
+            subvols = [
+                ('bar', 257),
+                ('bar/baz', 258),
+            ]
+
+            self.assertEqual(list(btrfsutil.SubvolumeIterator('.', top=256)), subvols)
+            self.assertEqual(list(btrfsutil.SubvolumeIterator('foo', top=0)), subvols)
+
+            os.rename('foo/bar/baz', 'baz')
+            self.assertEqual(sorted(btrfsutil.SubvolumeIterator('.')),
+                             [('baz', 258),
+                              ('foo', 256),
+                              ('foo/bar', 257)])
+
+            with btrfsutil.SubvolumeIterator('.') as it:
+                self.assertGreaterEqual(it.fileno(), 0)
+                it.close()
+                with self.assertRaises(ValueError):
+                    next(iter(it))
+                with self.assertRaises(ValueError):
+                    it.fileno()
+                it.close()
+        finally:
+            os.chdir(pwd)
diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c
index eb57b4c4..2fb4e59f 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -710,3 +710,325 @@ enum btrfs_util_error btrfs_util_f_create_subvolume(int parent_fd,
 
 	return BTRFS_UTIL_OK;
 }
+
+#define BTRFS_UTIL_SUBVOLUME_ITERATOR_CLOSE_FD (1 << 30)
+
+struct search_stack_entry {
+	struct btrfs_ioctl_search_args search;
+	size_t items_pos, buf_off;
+	size_t path_len;
+};
+
+struct btrfs_util_subvolume_iterator {
+	int fd;
+	int flags;
+
+	struct search_stack_entry *search_stack;
+	size_t search_stack_len;
+	size_t search_stack_capacity;
+
+	char *cur_path;
+	size_t cur_path_capacity;
+};
+
+static enum btrfs_util_error append_to_search_stack(struct btrfs_util_subvolume_iterator *iter,
+						    uint64_t tree_id,
+						    size_t path_len)
+{
+	struct search_stack_entry *entry;
+
+	if (iter->search_stack_len >= iter->search_stack_capacity) {
+		size_t new_capacity = iter->search_stack_capacity * 2;
+		struct search_stack_entry *new_search_stack;
+
+		new_search_stack = reallocarray(iter->search_stack,
+						new_capacity,
+						sizeof(*iter->search_stack));
+		if (!new_search_stack)
+			return BTRFS_UTIL_ERROR_NO_MEMORY;
+
+		iter->search_stack_capacity = new_capacity;
+		iter->search_stack = new_search_stack;
+	}
+
+	entry = &iter->search_stack[iter->search_stack_len++];
+
+	memset(&entry->search, 0, sizeof(entry->search));
+	entry->search.key.tree_id = BTRFS_ROOT_TREE_OBJECTID;
+	entry->search.key.min_objectid = tree_id;
+	entry->search.key.max_objectid = tree_id;
+	entry->search.key.min_type = BTRFS_ROOT_REF_KEY;
+	entry->search.key.max_type = BTRFS_ROOT_REF_KEY;
+	entry->search.key.min_offset = 0;
+	entry->search.key.max_offset = UINT64_MAX;
+	entry->search.key.min_transid = 0;
+	entry->search.key.max_transid = UINT64_MAX;
+	entry->search.key.nr_items = 0;
+
+	entry->items_pos = 0;
+	entry->buf_off = 0;
+
+	entry->path_len = path_len;
+
+	return BTRFS_UTIL_OK;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_create_subvolume_iterator(const char *path,
+							   uint64_t top,
+							   int flags,
+							   struct btrfs_util_subvolume_iterator **ret)
+{
+	enum btrfs_util_error err;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+	err = btrfs_util_f_create_subvolume_iterator(fd, top, flags, ret);
+	if (err == BTRFS_UTIL_OK)
+		(*ret)->flags |= BTRFS_UTIL_SUBVOLUME_ITERATOR_CLOSE_FD;
+
+	return err;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_f_create_subvolume_iterator(int fd, uint64_t top,
+							     int flags,
+							     struct btrfs_util_subvolume_iterator **ret)
+{
+	struct btrfs_util_subvolume_iterator *iter;
+	enum btrfs_util_error err;
+
+	if (flags & ~BTRFS_UTIL_SUBVOLUME_ITERATOR_MASK) {
+		errno = EINVAL;
+		return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+	}
+
+	if (top == 0) {
+		err = btrfs_util_f_is_subvolume(fd);
+		if (err)
+			return err;
+
+		err = btrfs_util_f_subvolume_id(fd, &top);
+		if (err)
+			return err;
+	}
+
+	iter = malloc(sizeof(*iter));
+	if (!iter)
+		return BTRFS_UTIL_ERROR_NO_MEMORY;
+
+	iter->fd = fd;
+	iter->flags = flags;
+
+	iter->search_stack_len = 0;
+	iter->search_stack_capacity = 4;
+	iter->search_stack = malloc(sizeof(*iter->search_stack) *
+				    iter->search_stack_capacity);
+	if (!iter->search_stack) {
+		err = BTRFS_UTIL_ERROR_NO_MEMORY;
+		goto out_iter;
+	}
+
+	iter->cur_path_capacity = 256;
+	iter->cur_path = malloc(iter->cur_path_capacity);
+	if (!iter->cur_path) {
+		err = BTRFS_UTIL_ERROR_NO_MEMORY;
+		goto out_search_stack;
+	}
+
+	err = append_to_search_stack(iter, top, 0);
+	if (err)
+		goto out_cur_path;
+
+	*ret = iter;
+
+	return BTRFS_UTIL_OK;
+
+out_cur_path:
+	free(iter->cur_path);
+out_search_stack:
+	free(iter->search_stack);
+out_iter:
+	free(iter);
+	return err;
+}
+
+__attribute__((visibility("default")))
+void btrfs_util_destroy_subvolume_iterator(struct btrfs_util_subvolume_iterator *iter)
+{
+	if (iter) {
+		free(iter->cur_path);
+		free(iter->search_stack);
+		if (iter->flags & BTRFS_UTIL_SUBVOLUME_ITERATOR_CLOSE_FD)
+			SAVE_ERRNO_AND_CLOSE(iter->fd);
+		free(iter);
+	}
+}
+
+__attribute__((visibility("default")))
+int btrfs_util_subvolume_iterator_fd(const struct btrfs_util_subvolume_iterator *iter)
+{
+	return iter->fd;
+}
+
+static struct search_stack_entry *top_search_stack_entry(struct btrfs_util_subvolume_iterator *iter)
+{
+	return &iter->search_stack[iter->search_stack_len - 1];
+}
+
+static enum btrfs_util_error build_subvol_path(struct btrfs_util_subvolume_iterator *iter,
+					       const struct btrfs_ioctl_search_header *header,
+					       const struct btrfs_root_ref *ref,
+					       const char *name,
+					       size_t *path_len_ret)
+{
+	struct btrfs_ioctl_ino_lookup_args lookup = {
+		.treeid = header->objectid,
+		.objectid = le64_to_cpu(ref->dirid),
+	};
+	struct search_stack_entry *top = top_search_stack_entry(iter);
+	size_t dir_len, name_len, path_len;
+	char *p;
+	int ret;
+
+	ret = ioctl(iter->fd, BTRFS_IOC_INO_LOOKUP, &lookup);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED;
+
+	dir_len = strlen(lookup.name);
+	name_len = le16_to_cpu(ref->name_len);
+
+	path_len = top->path_len;
+	/*
+	 * We need a joining slash if we have a current path and a subdirectory.
+	 */
+	if (top->path_len && dir_len)
+		path_len++;
+	path_len += dir_len;
+	/*
+	 * We need another joining slash if we have a current path and a name,
+	 * but not if we have a subdirectory, because the lookup ioctl includes
+	 * a trailing slash.
+	 */
+	if (top->path_len && !dir_len && name_len)
+		path_len++;
+	path_len += name_len;
+
+	if (path_len > iter->cur_path_capacity) {
+		char *tmp = realloc(iter->cur_path, path_len);
+
+		if (!tmp)
+			return BTRFS_UTIL_ERROR_NO_MEMORY;
+		iter->cur_path = tmp;
+		iter->cur_path_capacity = path_len;
+	}
+
+	p = iter->cur_path + top->path_len;
+	if (top->path_len && dir_len)
+		*p++ = '/';
+	memcpy(p, lookup.name, dir_len);
+	p += dir_len;
+	if (top->path_len && !dir_len && name_len)
+		*p++ = '/';
+	memcpy(p, name, name_len);
+	p += name_len;
+
+	*path_len_ret = path_len;
+
+	return BTRFS_UTIL_OK;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_subvolume_iterator_next(struct btrfs_util_subvolume_iterator *iter,
+							 char **path_ret,
+							 uint64_t *id_ret)
+{
+	struct search_stack_entry *top;
+	const struct btrfs_ioctl_search_header *header;
+	const struct btrfs_root_ref *ref;
+	const char *name;
+	enum btrfs_util_error err;
+	size_t path_len;
+	int ret;
+
+	for (;;) {
+		for (;;) {
+			if (iter->search_stack_len == 0)
+				return BTRFS_UTIL_ERROR_STOP_ITERATION;
+
+			top = top_search_stack_entry(iter);
+			if (top->items_pos < top->search.key.nr_items) {
+				break;
+			} else {
+				top->search.key.nr_items = 4096;
+				ret = ioctl(iter->fd, BTRFS_IOC_TREE_SEARCH, &top->search);
+				if (ret == -1)
+					return BTRFS_UTIL_ERROR_SEARCH_FAILED;
+				top->items_pos = 0;
+				top->buf_off = 0;
+
+				if (top->search.key.nr_items == 0) {
+					iter->search_stack_len--;
+					if ((iter->flags & BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER) &&
+					    iter->search_stack_len)
+						goto out;
+				}
+			}
+		}
+
+		header = (struct btrfs_ioctl_search_header *)(top->search.buf + top->buf_off);
+
+		top->items_pos++;
+		top->buf_off += sizeof(*header) + header->len;
+		top->search.key.min_offset = header->offset + 1;
+
+		/* This shouldn't happen, but handle it just in case. */
+		if (header->type != BTRFS_ROOT_REF_KEY)
+			continue;
+
+		ref = (struct btrfs_root_ref *)(header + 1);
+		name = (const char *)(ref + 1);
+		err = build_subvol_path(iter, header, ref, name, &path_len);
+		if (err)
+			return err;
+
+		err = append_to_search_stack(iter, header->offset, path_len);
+		if (err)
+			return err;
+
+		if (!(iter->flags & BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER)) {
+			top = top_search_stack_entry(iter);
+			goto out;
+		}
+	}
+
+out:
+	if (path_ret) {
+		*path_ret = malloc(top->path_len + 1);
+		if (!*path_ret)
+			return BTRFS_UTIL_ERROR_NO_MEMORY;
+		memcpy(*path_ret, iter->cur_path, top->path_len);
+		(*path_ret)[top->path_len] = '\0';
+	}
+	if (id_ret)
+		*id_ret = top->search.key.min_objectid;
+	return BTRFS_UTIL_OK;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_subvolume_iterator_next_info(struct btrfs_util_subvolume_iterator *iter,
+							      char **path_ret,
+							      struct btrfs_util_subvolume_info *subvol)
+{
+	enum btrfs_util_error err;
+	uint64_t id;
+
+	err = btrfs_util_subvolume_iterator_next(iter, path_ret, &id);
+	if (err)
+		return err;
+
+	return btrfs_util_f_subvolume_info(iter->fd, id, subvol);
+}
-- 
2.16.1


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

* [PATCH 11/26] libbtrfsutil: add btrfs_util_create_snapshot()
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (9 preceding siblings ...)
  2018-01-26 18:40 ` [PATCH 10/26] libbtrfsutil: add subvolume iterator helpers Omar Sandoval
@ 2018-01-26 18:40 ` Omar Sandoval
  2018-01-26 19:31   ` Goffredo Baroncelli
  2018-01-26 18:41 ` [PATCH 12/26] libbtrfsutil: add btrfs_util_delete_subvolume() Omar Sandoval
                   ` (15 subsequent siblings)
  26 siblings, 1 reply; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

Thanks to subvolume iterators, we can also implement recursive snapshot
fairly easily.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 libbtrfsutil/btrfsutil.h                    |  55 +++++++++
 libbtrfsutil/python/btrfsutilpy.h           |   1 +
 libbtrfsutil/python/module.c                |  11 ++
 libbtrfsutil/python/subvolume.c             |  49 ++++++++
 libbtrfsutil/python/tests/test_qgroup.py    |  10 ++
 libbtrfsutil/python/tests/test_subvolume.py |  55 +++++++++
 libbtrfsutil/subvolume.c                    | 166 ++++++++++++++++++++++++++++
 7 files changed, 347 insertions(+)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 3f78a34d..829f72b2 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -332,6 +332,61 @@ enum btrfs_util_error btrfs_util_f_create_subvolume(int parent_fd,
 						    uint64_t *async_transid,
 						    struct btrfs_util_qgroup_inherit *qgroup_inherit);
 
+/**
+ * BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE - Also snapshot subvolumes beneath the
+ * source subvolume onto the same location on the new snapshot. Note that this
+ * modifies the newly-created snapshot, so it cannot be combined with
+ * %BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY.
+ */
+#define BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE (1 << 0)
+/**
+ * BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY - Create a read-only snapshot.
+ */
+#define BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY (1 << 1)
+#define BTRFS_UTIL_CREATE_SNAPSHOT_MASK ((1 << 2) - 1)
+
+/**
+ * btrfs_util_create_snapshot() - Create a new snapshot from a source subvolume
+ * path.
+ * @source: Path of the existing subvolume to snapshot.
+ * @path: Where to create the snapshot.
+ * @flags: Bitmask of BTRFS_UTIL_CREATE_SNAPSHOT_* flags.
+ * @async_transid: See btrfs_util_create_subvolume(). If
+ * %BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE was in @flags, then this will contain
+ * the largest transaction ID of all created subvolumes.
+ * @qgroup_inherit: See btrfs_util_create_subvolume().
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_create_snapshot(const char *source,
+						 const char *path, int flags,
+						 uint64_t *async_transid,
+						 struct btrfs_util_qgroup_inherit *qgroup_inherit);
+
+/**
+ * btrfs_util_f_create_snapshot() - See btrfs_util_create_snapshot().
+ */
+enum btrfs_util_error btrfs_util_f_create_snapshot(int fd, const char *path,
+						   int flags,
+						   uint64_t *async_transid,
+						   struct btrfs_util_qgroup_inherit *qgroup_inherit);
+
+/**
+ * btrfs_util_f2_create_snapshot() - Create a new snapshot from a source
+ * subvolume file descriptor and a target parent file descriptor and name.
+ * @fd: File descriptor of the existing subvolume to snapshot.
+ * @parent_fd: File descriptor of the parent directory where the snapshot should
+ * be created.
+ * @name: Name of the snapshot to create.
+ * @flags: See btrfs_util_create_snapshot().
+ * @async_transid: See btrfs_util_create_snapshot().
+ * @qgroup_inherit: See btrfs_util_create_snapshot().
+ */
+enum btrfs_util_error btrfs_util_f2_create_snapshot(int fd, int parent_fd,
+						    const char *name, int flags,
+						    uint64_t *async_transid,
+						    struct btrfs_util_qgroup_inherit *qgroup_inherit);
+
 struct btrfs_util_subvolume_iterator;
 
 /**
diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
index a9c15219..d552e416 100644
--- a/libbtrfsutil/python/btrfsutilpy.h
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -70,6 +70,7 @@ PyObject *set_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds
 PyObject *get_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *set_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *create_snapshot(PyObject *self, PyObject *args, PyObject *kwds);
 
 void add_module_constants(PyObject *m);
 
diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
index daf0747f..d8f797cb 100644
--- a/libbtrfsutil/python/module.c
+++ b/libbtrfsutil/python/module.c
@@ -195,6 +195,17 @@ static PyMethodDef btrfsutil_methods[] = {
 	 "path -- string, bytes, or path-like object\n"
 	 "async -- create the subvolume without waiting for it to commit to\n"
 	 "disk and return the transaction ID"},
+	{"create_snapshot", (PyCFunction)create_snapshot,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "create_snapshot(source, path, recursive=False, read_only=False, async=False)\n\n"
+	 "Create a new snapshot.\n\n"
+	 "Arguments:\n"
+	 "source -- string, bytes, path-like object, or open file descriptor\n"
+	 "path -- string, bytes, or path-like object\n"
+	 "recursive -- also snapshot child subvolumes\n"
+	 "read_only -- create a read-only snapshot\n"
+	 "async -- create the subvolume without waiting for it to commit to\n"
+	 "disk and return the transaction ID"},
 	{},
 };
 
diff --git a/libbtrfsutil/python/subvolume.c b/libbtrfsutil/python/subvolume.c
index 23163176..763a06d9 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -347,6 +347,55 @@ PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
 		Py_RETURN_NONE;
 }
 
+PyObject *create_snapshot(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *keywords[] = {
+		"source", "path", "recursive", "read_only", "async",
+		"qgroup_inherit", NULL,
+	};
+	struct path_arg src = {.allow_fd = true}, dst = {.allow_fd = false};
+	enum btrfs_util_error err;
+	int recursive = 0, read_only = 0, async = 0;
+	int flags = 0;
+	QgroupInherit *inherit = NULL;
+	uint64_t transid;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O&|pppO!:create_snapshot",
+					 keywords, &path_converter, &src,
+					 &path_converter, &dst, &recursive,
+					 &read_only, &async,
+					 &QgroupInherit_type, &inherit))
+		return NULL;
+
+	if (recursive)
+		flags |= BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE;
+	if (read_only)
+		flags |= BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY;
+
+	if (src.path) {
+		err = btrfs_util_create_snapshot(src.path, dst.path, flags,
+						 async ? &transid : NULL,
+						 inherit ? inherit->inherit : NULL);
+	} else {
+		err = btrfs_util_f_create_snapshot(src.fd, dst.path, flags,
+						   async ? &transid : NULL,
+						   inherit ? inherit->inherit : NULL);
+	}
+	if (err) {
+		SetFromBtrfsUtilErrorWithPaths(err, &src, &dst);
+		path_cleanup(&src);
+		path_cleanup(&dst);
+		return NULL;
+	}
+
+	path_cleanup(&src);
+	path_cleanup(&dst);
+	if (async)
+		return PyLong_FromUnsignedLongLong(transid);
+	else
+		Py_RETURN_NONE;
+}
+
 typedef struct {
 	PyObject_HEAD
 	struct btrfs_util_subvolume_iterator *iter;
diff --git a/libbtrfsutil/python/tests/test_qgroup.py b/libbtrfsutil/python/tests/test_qgroup.py
index 19e6b05a..74fc46b6 100644
--- a/libbtrfsutil/python/tests/test_qgroup.py
+++ b/libbtrfsutil/python/tests/test_qgroup.py
@@ -31,6 +31,16 @@ class TestQgroup(BtrfsTestCase):
 
         btrfsutil.create_subvolume(subvol, qgroup_inherit=inherit)
 
+    def test_snapshot_inherit(self):
+        subvol = os.path.join(self.mountpoint, 'subvol')
+        snapshot = os.path.join(self.mountpoint, 'snapshot')
+
+        inherit = btrfsutil.QgroupInherit()
+        inherit.add_group(5)
+
+        btrfsutil.create_subvolume(subvol)
+        btrfsutil.create_snapshot(subvol, snapshot, qgroup_inherit=inherit)
+
 
 class TestQgroupInherit(unittest.TestCase):
     def test_new(self):
diff --git a/libbtrfsutil/python/tests/test_subvolume.py b/libbtrfsutil/python/tests/test_subvolume.py
index b43beca7..2951154e 100644
--- a/libbtrfsutil/python/tests/test_subvolume.py
+++ b/libbtrfsutil/python/tests/test_subvolume.py
@@ -129,6 +129,13 @@ class TestSubvolume(BtrfsTestCase):
         self.assertEqual(info.stime, 0)
         self.assertEqual(info.rtime, 0)
 
+        subvol_uuid = info.uuid
+        snapshot = os.path.join(self.mountpoint, 'snapshot')
+        btrfsutil.create_snapshot(subvol, snapshot)
+
+        info = btrfsutil.subvolume_info(snapshot)
+        self.assertEqual(info.parent_uuid, subvol_uuid)
+
         # TODO: test received_uuid, stransid, rtransid, stime, and rtime
 
         for arg in self.path_or_fd(self.mountpoint):
@@ -215,6 +222,54 @@ class TestSubvolume(BtrfsTestCase):
         self.assertTrue(os.WIFEXITED(wstatus))
         self.assertEqual(os.WEXITSTATUS(wstatus), 0)
 
+    def test_create_snapshot(self):
+        subvol = os.path.join(self.mountpoint, 'subvol')
+
+        btrfsutil.create_subvolume(subvol)
+        os.mkdir(os.path.join(subvol, 'dir'))
+
+        for i, arg in enumerate(self.path_or_fd(subvol)):
+            with self.subTest(type=type(arg)):
+                snapshots_dir = os.path.join(self.mountpoint, 'snapshots{}'.format(i))
+                os.mkdir(snapshots_dir)
+                snapshot = os.path.join(snapshots_dir, 'snapshot')
+
+                btrfsutil.create_snapshot(subvol, snapshot + '1')
+                self.assertTrue(btrfsutil.is_subvolume(snapshot + '1'))
+                self.assertTrue(os.path.exists(os.path.join(snapshot + '1', 'dir')))
+
+                btrfsutil.create_snapshot(subvol, (snapshot + '2').encode())
+                self.assertTrue(btrfsutil.is_subvolume(snapshot + '2'))
+                self.assertTrue(os.path.exists(os.path.join(snapshot + '2', 'dir')))
+
+                if HAVE_PATH_LIKE:
+                    btrfsutil.create_snapshot(subvol, PurePath(snapshot + '3'))
+                    self.assertTrue(btrfsutil.is_subvolume(snapshot + '3'))
+                    self.assertTrue(os.path.exists(os.path.join(snapshot + '3', 'dir')))
+
+        nested_subvol = os.path.join(subvol, 'nested')
+        more_nested_subvol = os.path.join(nested_subvol, 'more_nested')
+        btrfsutil.create_subvolume(nested_subvol)
+        btrfsutil.create_subvolume(more_nested_subvol)
+        os.mkdir(os.path.join(more_nested_subvol, 'nested_dir'))
+
+        snapshot = os.path.join(self.mountpoint, 'snapshot')
+
+        btrfsutil.create_snapshot(subvol, snapshot + '1')
+        # Dummy subvolume.
+        self.assertEqual(os.stat(os.path.join(snapshot + '1', 'nested')).st_ino, 2)
+        self.assertFalse(os.path.exists(os.path.join(snapshot + '1', 'nested', 'more_nested')))
+
+        btrfsutil.create_snapshot(subvol, snapshot + '2', recursive=True)
+        self.assertTrue(os.path.exists(os.path.join(snapshot + '2', 'nested/more_nested/nested_dir')))
+
+        transid = btrfsutil.create_snapshot(subvol, snapshot + '3', recursive=True, async=True)
+        self.assertTrue(os.path.exists(os.path.join(snapshot + '3', 'nested/more_nested/nested_dir')))
+        self.assertGreater(transid, 0)
+
+        btrfsutil.create_snapshot(subvol, snapshot + '4', read_only=True)
+        self.assertTrue(btrfsutil.get_subvolume_read_only(snapshot + '4'))
+
     def test_subvolume_iterator(self):
         pwd = os.getcwd()
         try:
diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c
index 2fb4e59f..27f64657 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -856,6 +856,172 @@ out_iter:
 	return err;
 }
 
+static enum btrfs_util_error snapshot_subvolume_children(int fd, int parent_fd,
+							 const char *name,
+							 uint64_t *async_transid)
+{
+	struct btrfs_util_subvolume_iterator *iter;
+	enum btrfs_util_error err;
+	int dstfd;
+
+	dstfd = openat(parent_fd, name, O_RDONLY);
+	if (dstfd == -1)
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+	err = btrfs_util_f_create_subvolume_iterator(fd, 0, 0, &iter);
+	if (err)
+		goto out;
+
+	for (;;) {
+		char child_name[BTRFS_SUBVOL_NAME_MAX + 1];
+		char *child_path;
+		int child_fd, new_parent_fd;
+		uint64_t tmp_transid;
+
+		err = btrfs_util_subvolume_iterator_next(iter, &child_path,
+							 NULL);
+		if (err) {
+			if (err == BTRFS_UTIL_ERROR_STOP_ITERATION)
+				err = BTRFS_UTIL_OK;
+			break;
+		}
+
+		/* Remove the placeholder directory. */
+		if (unlinkat(dstfd, child_path, AT_REMOVEDIR) == -1) {
+			free(child_path);
+			err = BTRFS_UTIL_ERROR_RMDIR_FAILED;
+			break;
+		}
+
+		child_fd = openat(fd, child_path, O_RDONLY);
+		if (child_fd == -1) {
+			free(child_path);
+			err = BTRFS_UTIL_ERROR_OPEN_FAILED;
+			break;
+		}
+
+		err = openat_parent_and_name(dstfd, child_path, child_name,
+					     sizeof(child_name),
+					     &new_parent_fd);
+		free(child_path);
+		if (err) {
+			SAVE_ERRNO_AND_CLOSE(child_fd);
+			break;
+		}
+
+		err = btrfs_util_f2_create_snapshot(child_fd, new_parent_fd,
+						    child_name, 0,
+						    async_transid ? &tmp_transid : NULL,
+						    NULL);
+		SAVE_ERRNO_AND_CLOSE(child_fd);
+		SAVE_ERRNO_AND_CLOSE(new_parent_fd);
+		if (err)
+			break;
+		if (async_transid && tmp_transid > *async_transid)
+			*async_transid = tmp_transid;
+	}
+
+	btrfs_util_destroy_subvolume_iterator(iter);
+out:
+	SAVE_ERRNO_AND_CLOSE(dstfd);
+	return err;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_create_snapshot(const char *source,
+						 const char *path, int flags,
+						 uint64_t *async_transid,
+						 struct btrfs_util_qgroup_inherit *qgroup_inherit)
+{
+	enum btrfs_util_error err;
+	int fd;
+
+	fd = open(source, O_RDONLY);
+	if (fd == -1)
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+	err = btrfs_util_f_create_snapshot(fd, path, flags, async_transid,
+					   qgroup_inherit);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_f_create_snapshot(int fd, const char *path,
+						   int flags,
+						   uint64_t *async_transid,
+						   struct btrfs_util_qgroup_inherit *qgroup_inherit)
+{
+	char name[BTRFS_SUBVOL_NAME_MAX + 1];
+	enum btrfs_util_error err;
+	int parent_fd;
+
+	err = openat_parent_and_name(AT_FDCWD, path, name, sizeof(name),
+				     &parent_fd);
+	if (err)
+		return err;
+
+	err = btrfs_util_f2_create_snapshot(fd, parent_fd, name, flags,
+					    async_transid, qgroup_inherit);
+	SAVE_ERRNO_AND_CLOSE(parent_fd);
+	return err;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_f2_create_snapshot(int fd, int parent_fd,
+						    const char *name, int flags,
+						    uint64_t *async_transid,
+						    struct btrfs_util_qgroup_inherit *qgroup_inherit)
+{
+	struct btrfs_ioctl_vol_args_v2 args = {.fd = fd};
+	enum btrfs_util_error err;
+	size_t len;
+	int ret;
+
+	if ((flags & ~BTRFS_UTIL_CREATE_SNAPSHOT_MASK) ||
+	    ((flags & BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY) &&
+	     (flags & BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE))) {
+		errno = EINVAL;
+		return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+	}
+
+	if (flags & BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY)
+		args.flags |= BTRFS_SUBVOL_RDONLY;
+	if (async_transid)
+		args.flags |= BTRFS_SUBVOL_CREATE_ASYNC;
+	if (qgroup_inherit) {
+		args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
+		args.qgroup_inherit = (struct btrfs_qgroup_inherit *)qgroup_inherit;
+		args.size = (sizeof(*args.qgroup_inherit) +
+			     args.qgroup_inherit->num_qgroups *
+			     sizeof(args.qgroup_inherit->qgroups[0]));
+	}
+
+	len = strlen(name);
+	if (len >= sizeof(args.name)) {
+		errno = ENAMETOOLONG;
+		return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+	}
+	memcpy(args.name, name, len);
+	args.name[len] = '\0';
+
+	ret = ioctl(parent_fd, BTRFS_IOC_SNAP_CREATE_V2, &args);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED;
+
+	if (async_transid)
+		*async_transid = args.transid;
+
+	if (flags & BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE) {
+		err = snapshot_subvolume_children(fd, parent_fd, name,
+						  async_transid);
+		if (err)
+			return err;
+	}
+
+	return BTRFS_UTIL_OK;
+}
+
 __attribute__((visibility("default")))
 void btrfs_util_destroy_subvolume_iterator(struct btrfs_util_subvolume_iterator *iter)
 {
-- 
2.16.1


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

* [PATCH 12/26] libbtrfsutil: add btrfs_util_delete_subvolume()
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (10 preceding siblings ...)
  2018-01-26 18:40 ` [PATCH 11/26] libbtrfsutil: add btrfs_util_create_snapshot() Omar Sandoval
@ 2018-01-26 18:41 ` Omar Sandoval
  2018-01-26 18:41 ` [PATCH 13/26] libbtrfsutil: add btrfs_util_deleted_subvolumes() Omar Sandoval
                   ` (14 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:41 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

We also support recursive deletion using a subvolume iterator.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 libbtrfsutil/btrfsutil.h                    |  30 ++++++++
 libbtrfsutil/python/btrfsutilpy.h           |   1 +
 libbtrfsutil/python/module.c                |   8 +++
 libbtrfsutil/python/subvolume.c             |  27 ++++++++
 libbtrfsutil/python/tests/test_subvolume.py |  48 +++++++++++++
 libbtrfsutil/subvolume.c                    | 103 ++++++++++++++++++++++++++++
 6 files changed, 217 insertions(+)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 829f72b2..080268ce 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -387,6 +387,36 @@ enum btrfs_util_error btrfs_util_f2_create_snapshot(int fd, int parent_fd,
 						    uint64_t *async_transid,
 						    struct btrfs_util_qgroup_inherit *qgroup_inherit);
 
+/**
+ * BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE - Delete subvolumes beneath the given
+ * subvolume before attempting to delete the given subvolume. By default,
+ * deleting a subvolume with child subvolumes is an error.
+ */
+#define BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE (1 << 0)
+#define BTRFS_UTIL_DELETE_SUBVOLUME_MASK ((1 << 1) - 1)
+
+/**
+ * btrfs_util_delete_subvolume() - Delete a subvolume or snapshot.
+ * @path: Path of the subvolume to delete.
+ * @flags: Bitmask of BTRFS_UTIL_DELETE_SUBVOLUME_* flags.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_delete_subvolume(const char *path, int flags);
+
+/**
+ * btrfs_util_f_delete_subvolume() - Delete a subvolume or snapshot given its
+ * parent and name.
+ * @parent_fd: File descriptor of the subvolume's parent directory.
+ * @name: Name of the subvolume.
+ * @flags: See btrfs_util_delete_subvolume().
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_f_delete_subvolume(int parent_fd,
+						    const char *name,
+						    int flags);
+
 struct btrfs_util_subvolume_iterator;
 
 /**
diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
index d552e416..b3ec047f 100644
--- a/libbtrfsutil/python/btrfsutilpy.h
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -71,6 +71,7 @@ PyObject *get_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *set_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *create_snapshot(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *delete_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 
 void add_module_constants(PyObject *m);
 
diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
index d8f797cb..e995a1be 100644
--- a/libbtrfsutil/python/module.c
+++ b/libbtrfsutil/python/module.c
@@ -206,6 +206,14 @@ static PyMethodDef btrfsutil_methods[] = {
 	 "read_only -- create a read-only snapshot\n"
 	 "async -- create the subvolume without waiting for it to commit to\n"
 	 "disk and return the transaction ID"},
+	{"delete_subvolume", (PyCFunction)delete_subvolume,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "delete_subvolume(path, recursive=False)\n\n"
+	 "Delete a subvolume or snapshot.\n\n"
+	 "Arguments:\n"
+	 "path -- string, bytes, or path-like object\n"
+	 "recursive -- if the given subvolume has child subvolumes, delete\n"
+	 "them instead of failing"},
 	{},
 };
 
diff --git a/libbtrfsutil/python/subvolume.c b/libbtrfsutil/python/subvolume.c
index 763a06d9..f740ebdc 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -396,6 +396,33 @@ PyObject *create_snapshot(PyObject *self, PyObject *args, PyObject *kwds)
 		Py_RETURN_NONE;
 }
 
+PyObject *delete_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *keywords[] = {"path", "recursive", NULL};
+	struct path_arg path = {.allow_fd = false};
+	enum btrfs_util_error err;
+	int recursive = 0;
+	int flags = 0;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|p:delete_subvolume",
+					 keywords, &path_converter, &path,
+					 &recursive))
+		return NULL;
+
+	if (recursive)
+		flags |= BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE;
+
+	err = btrfs_util_delete_subvolume(path.path, flags);
+	if (err) {
+		SetFromBtrfsUtilErrorWithPath(err, &path);
+		path_cleanup(&path);
+		return NULL;
+	}
+
+	path_cleanup(&path);
+	Py_RETURN_NONE;
+}
+
 typedef struct {
 	PyObject_HEAD
 	struct btrfs_util_subvolume_iterator *iter;
diff --git a/libbtrfsutil/python/tests/test_subvolume.py b/libbtrfsutil/python/tests/test_subvolume.py
index 2951154e..08083abe 100644
--- a/libbtrfsutil/python/tests/test_subvolume.py
+++ b/libbtrfsutil/python/tests/test_subvolume.py
@@ -270,6 +270,54 @@ class TestSubvolume(BtrfsTestCase):
         btrfsutil.create_snapshot(subvol, snapshot + '4', read_only=True)
         self.assertTrue(btrfsutil.get_subvolume_read_only(snapshot + '4'))
 
+    def test_delete_subvolume(self):
+        subvol = os.path.join(self.mountpoint, 'subvol')
+        btrfsutil.create_subvolume(subvol + '1')
+        self.assertTrue(os.path.exists(subvol + '1'))
+        btrfsutil.create_subvolume(subvol + '2')
+        self.assertTrue(os.path.exists(subvol + '2'))
+        btrfsutil.create_subvolume(subvol + '3')
+        self.assertTrue(os.path.exists(subvol + '3'))
+
+        btrfsutil.delete_subvolume(subvol + '1')
+        self.assertFalse(os.path.exists(subvol + '1'))
+        btrfsutil.delete_subvolume((subvol + '2').encode())
+        self.assertFalse(os.path.exists(subvol + '2'))
+        if HAVE_PATH_LIKE:
+            btrfsutil.delete_subvolume(PurePath(subvol + '3'))
+            self.assertFalse(os.path.exists(subvol + '3'))
+
+        # Test deleting subvolumes under '/' in a chroot.
+        pid = os.fork()
+        if pid == 0:
+            try:
+                os.chroot(self.mountpoint)
+                os.chdir('/')
+                btrfsutil.create_subvolume('/subvol4')
+                self.assertTrue(os.path.exists('/subvol4'))
+                btrfsutil.delete_subvolume('/subvol4')
+                self.assertFalse(os.path.exists('/subvol4'))
+                with self.assertRaises(btrfsutil.BtrfsUtilError):
+                    btrfsutil.delete_subvolume('/')
+                os._exit(0)
+            except Exception:
+                traceback.print_exc()
+                os._exit(1)
+        wstatus = os.waitpid(pid, 0)[1]
+        self.assertTrue(os.WIFEXITED(wstatus))
+        self.assertEqual(os.WEXITSTATUS(wstatus), 0)
+
+        btrfsutil.create_subvolume(subvol + '5')
+        btrfsutil.create_subvolume(subvol + '5/foo')
+        btrfsutil.create_subvolume(subvol + '5/bar')
+        btrfsutil.create_subvolume(subvol + '5/bar/baz')
+        btrfsutil.create_subvolume(subvol + '5/bar/qux')
+        btrfsutil.create_subvolume(subvol + '5/quux')
+        with self.assertRaises(btrfsutil.BtrfsUtilError):
+            btrfsutil.delete_subvolume(subvol + '5')
+        btrfsutil.delete_subvolume(subvol + '5', recursive=True)
+        self.assertFalse(os.path.exists(subvol + '5'))
+
     def test_subvolume_iterator(self):
         pwd = os.getcwd()
         try:
diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c
index 27f64657..19ab2fcb 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -1022,6 +1022,109 @@ enum btrfs_util_error btrfs_util_f2_create_snapshot(int fd, int parent_fd,
 	return BTRFS_UTIL_OK;
 }
 
+static enum btrfs_util_error delete_subvolume_children(int parent_fd,
+						       const char *name)
+{
+	struct btrfs_util_subvolume_iterator *iter;
+	enum btrfs_util_error err;
+	int fd;
+
+	fd = openat(parent_fd, name, O_RDONLY);
+	if (fd == -1)
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+	err = btrfs_util_f_create_subvolume_iterator(fd, 0,
+						     BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER,
+						     &iter);
+	if (err)
+		goto out;
+
+	for (;;) {
+		char child_name[BTRFS_PATH_NAME_MAX + 1];
+		char *child_path;
+		int child_parent_fd;
+
+		err = btrfs_util_subvolume_iterator_next(iter, &child_path,
+							 NULL);
+		if (err) {
+			if (err == BTRFS_UTIL_ERROR_STOP_ITERATION)
+				err = BTRFS_UTIL_OK;
+			break;
+		}
+
+		err = openat_parent_and_name(fd, child_path, child_name,
+					     sizeof(child_name),
+					     &child_parent_fd);
+		free(child_path);
+		if (err)
+			break;
+
+		err = btrfs_util_f_delete_subvolume(child_parent_fd, child_name,
+						    0);
+		SAVE_ERRNO_AND_CLOSE(child_parent_fd);
+		if (err)
+			break;
+	}
+
+	btrfs_util_destroy_subvolume_iterator(iter);
+out:
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_delete_subvolume(const char *path, int flags)
+{
+	char name[BTRFS_PATH_NAME_MAX + 1];
+	enum btrfs_util_error err;
+	int parent_fd;
+
+	err = openat_parent_and_name(AT_FDCWD, path, name, sizeof(name),
+				     &parent_fd);
+	if (err)
+		return err;
+
+	err = btrfs_util_f_delete_subvolume(parent_fd, name, flags);
+	SAVE_ERRNO_AND_CLOSE(parent_fd);
+	return err;
+}
+
+__attribute__((visibility("default")))
+enum btrfs_util_error btrfs_util_f_delete_subvolume(int parent_fd,
+						    const char *name,
+						    int flags)
+{
+	struct btrfs_ioctl_vol_args args = {};
+	enum btrfs_util_error err;
+	size_t len;
+	int ret;
+
+	if (flags & ~BTRFS_UTIL_DELETE_SUBVOLUME_MASK) {
+		errno = EINVAL;
+		return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+	}
+
+	if (flags & BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE) {
+		err = delete_subvolume_children(parent_fd, name);
+		if (err)
+			return err;
+	}
+
+	len = strlen(name);
+	if (len >= sizeof(args.name)) {
+		errno = ENAMETOOLONG;
+		return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+	}
+	memcpy(args.name, name, len);
+	args.name[len] = '\0';
+
+	ret = ioctl(parent_fd, BTRFS_IOC_SNAP_DESTROY, &args);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_SNAP_DESTROY_FAILED;
+
+	return BTRFS_UTIL_OK;
+}
+
 __attribute__((visibility("default")))
 void btrfs_util_destroy_subvolume_iterator(struct btrfs_util_subvolume_iterator *iter)
 {
-- 
2.16.1


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

* [PATCH 13/26] libbtrfsutil: add btrfs_util_deleted_subvolumes()
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (11 preceding siblings ...)
  2018-01-26 18:41 ` [PATCH 12/26] libbtrfsutil: add btrfs_util_delete_subvolume() Omar Sandoval
@ 2018-01-26 18:41 ` Omar Sandoval
  2018-01-26 18:41 ` [PATCH 14/26] libbtrfsutil: add filesystem sync helpers Omar Sandoval
                   ` (13 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:41 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 libbtrfsutil/btrfsutil.h                    | 19 +++++++
 libbtrfsutil/python/btrfsutilpy.h           |  3 +
 libbtrfsutil/python/module.c                | 30 ++++++++++
 libbtrfsutil/python/qgroup.c                | 17 +-----
 libbtrfsutil/python/subvolume.c             | 30 ++++++++++
 libbtrfsutil/python/tests/test_subvolume.py |  8 +++
 libbtrfsutil/subvolume.c                    | 87 +++++++++++++++++++++++++++++
 7 files changed, 179 insertions(+), 15 deletions(-)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 080268ce..74b05e29 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -509,6 +509,25 @@ enum btrfs_util_error btrfs_util_subvolume_iterator_next_info(struct btrfs_util_
 							      char **path_ret,
 							      struct btrfs_util_subvolume_info *subvol);
 
+/**
+ * btrfs_util_deleted_subvolumes() - Get a list of subvolume which have been
+ * deleted but not yet cleaned up.
+ * @path: Path on a Btrfs filesystem.
+ * @ids: Returned array of subvolume IDs.
+ * @n: Returned number of IDs in the @ids array.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_deleted_subvolumes(const char *path,
+						    uint64_t **ids,
+						    size_t *n);
+
+/**
+ * btrfs_util_f_deleted_subvolumes() - See btrfs_util_deleted_subvolumes().
+ */
+enum btrfs_util_error btrfs_util_f_deleted_subvolumes(int fd, uint64_t **ids,
+						      size_t *n);
+
 /**
  * btrfs_util_create_qgroup_inherit() - Create a qgroup inheritance specifier
  * for btrfs_util_create_subvolume() or btrfs_util_create_snapshot().
diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
index b3ec047f..be5122e2 100644
--- a/libbtrfsutil/python/btrfsutilpy.h
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -54,6 +54,8 @@ struct path_arg {
 int path_converter(PyObject *o, void *p);
 void path_cleanup(struct path_arg *path);
 
+PyObject *list_from_uint64_array(const uint64_t *arr, size_t n);
+
 void SetFromBtrfsUtilError(enum btrfs_util_error err);
 void SetFromBtrfsUtilErrorWithPath(enum btrfs_util_error err,
 				   struct path_arg *path);
@@ -72,6 +74,7 @@ PyObject *set_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *create_snapshot(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *delete_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *deleted_subvolumes(PyObject *self, PyObject *args, PyObject *kwds);
 
 void add_module_constants(PyObject *m);
 
diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
index e995a1be..eaa062ac 100644
--- a/libbtrfsutil/python/module.c
+++ b/libbtrfsutil/python/module.c
@@ -125,6 +125,29 @@ err:
 	return 0;
 }
 
+PyObject *list_from_uint64_array(const uint64_t *arr, size_t n)
+{
+    PyObject *ret;
+    size_t i;
+
+    ret = PyList_New(n);
+    if (!ret)
+	    return NULL;
+
+    for (i = 0; i < n; i++) {
+	    PyObject *tmp;
+
+	    tmp = PyLong_FromUnsignedLongLong(arr[i]);
+	    if (!tmp) {
+		    Py_DECREF(ret);
+		    return NULL;
+	    }
+	    PyList_SET_ITEM(ret, i, tmp);
+    }
+
+    return ret;
+}
+
 void path_cleanup(struct path_arg *path)
 {
 	Py_CLEAR(path->object);
@@ -214,6 +237,13 @@ static PyMethodDef btrfsutil_methods[] = {
 	 "path -- string, bytes, or path-like object\n"
 	 "recursive -- if the given subvolume has child subvolumes, delete\n"
 	 "them instead of failing"},
+	{"deleted_subvolumes", (PyCFunction)deleted_subvolumes,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "deleted_subvolumes(path)\n\n"
+	 "Get the list of subvolume IDs which have been deleted but not yet\n"
+	 "cleaned up\n\n"
+	 "Arguments:\n"
+	 "path -- string, bytes, path-like object, or open file descriptor"},
 	{},
 };
 
diff --git a/libbtrfsutil/python/qgroup.c b/libbtrfsutil/python/qgroup.c
index 69716d92..44ac5ebc 100644
--- a/libbtrfsutil/python/qgroup.c
+++ b/libbtrfsutil/python/qgroup.c
@@ -55,25 +55,12 @@ static PyObject *QgroupInherit_getattro(QgroupInherit *self, PyObject *nameobj)
     }
 
     if (strcmp(name, "groups") == 0) {
-	    PyObject *ret, *tmp;
 	    const uint64_t *arr;
-	    size_t n, i;
+	    size_t n;
 
 	    btrfs_util_qgroup_inherit_get_groups(self->inherit, &arr, &n);
-	    ret = PyList_New(n);
-	    if (!ret)
-		    return NULL;
-
-	    for (i = 0; i < n; i++) {
-		    tmp = PyLong_FromUnsignedLongLong(arr[i]);
-		    if (!tmp) {
-			    Py_DECREF(ret);
-			    return NULL;
-		    }
-		    PyList_SET_ITEM(ret, i, tmp);
-	    }
 
-	    return ret;
+	    return list_from_uint64_array(arr, n);
     } else {
 	    return PyObject_GenericGetAttr((PyObject *)self, nameobj);
     }
diff --git a/libbtrfsutil/python/subvolume.c b/libbtrfsutil/python/subvolume.c
index f740ebdc..52b1a5c4 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -423,6 +423,36 @@ PyObject *delete_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
 	Py_RETURN_NONE;
 }
 
+PyObject *deleted_subvolumes(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *keywords[] = {"path", NULL};
+	struct path_arg path = {.allow_fd = true};
+	PyObject *ret;
+	uint64_t *ids;
+	size_t n;
+	enum btrfs_util_error err;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:deleted_subvolumes",
+					 keywords, &path_converter, &path))
+		return NULL;
+
+	if (path.path)
+		err = btrfs_util_deleted_subvolumes(path.path, &ids, &n);
+	else
+		err = btrfs_util_f_deleted_subvolumes(path.fd, &ids, &n);
+	if (err) {
+		SetFromBtrfsUtilErrorWithPath(err, &path);
+		path_cleanup(&path);
+		return NULL;
+	}
+
+	path_cleanup(&path);
+
+	ret = list_from_uint64_array(ids, n);
+	free(ids);
+	return ret;
+}
+
 typedef struct {
 	PyObject_HEAD
 	struct btrfs_util_subvolume_iterator *iter;
diff --git a/libbtrfsutil/python/tests/test_subvolume.py b/libbtrfsutil/python/tests/test_subvolume.py
index 08083abe..a46d4a34 100644
--- a/libbtrfsutil/python/tests/test_subvolume.py
+++ b/libbtrfsutil/python/tests/test_subvolume.py
@@ -318,6 +318,14 @@ class TestSubvolume(BtrfsTestCase):
         btrfsutil.delete_subvolume(subvol + '5', recursive=True)
         self.assertFalse(os.path.exists(subvol + '5'))
 
+    def test_deleted_subvolumes(self):
+        subvol = os.path.join(self.mountpoint, 'subvol')
+        btrfsutil.create_subvolume(subvol + '1')
+        btrfsutil.delete_subvolume(subvol + '1')
+        for arg in self.path_or_fd(self.mountpoint):
+            with self.subTest(type=type(arg)):
+                self.assertEqual(btrfsutil.deleted_subvolumes(arg), [256])
+
     def test_subvolume_iterator(self):
         pwd = os.getcwd()
         try:
diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c
index 19ab2fcb..903bcfb6 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -1301,3 +1301,90 @@ enum btrfs_util_error btrfs_util_subvolume_iterator_next_info(struct btrfs_util_
 
 	return btrfs_util_f_subvolume_info(iter->fd, id, subvol);
 }
+
+enum btrfs_util_error btrfs_util_deleted_subvolumes(const char *path,
+						    uint64_t **ids, size_t *n)
+{
+	enum btrfs_util_error err;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+	err = btrfs_util_f_deleted_subvolumes(fd, ids, n);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+enum btrfs_util_error btrfs_util_f_deleted_subvolumes(int fd, uint64_t **ids,
+						      size_t *n)
+{
+	size_t capacity = 0;
+	struct btrfs_ioctl_search_args search = {
+		.key = {
+			.tree_id = BTRFS_ROOT_TREE_OBJECTID,
+			.min_objectid = BTRFS_ORPHAN_OBJECTID,
+			.max_objectid = BTRFS_ORPHAN_OBJECTID,
+			.min_type = BTRFS_ORPHAN_ITEM_KEY,
+			.max_type = BTRFS_ORPHAN_ITEM_KEY,
+			.min_offset = 0,
+			.max_offset = UINT64_MAX,
+			.min_transid = 0,
+			.max_transid = UINT64_MAX,
+			.nr_items = 0,
+		},
+	};
+	enum btrfs_util_error err;
+	size_t items_pos = 0, buf_off = 0;
+	int ret;
+
+	*ids = NULL;
+	*n = 0;
+	for (;;) {
+		const struct btrfs_ioctl_search_header *header;
+
+		if (items_pos >= search.key.nr_items) {
+			search.key.nr_items = 4096;
+			ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search);
+			if (ret == -1) {
+				err = BTRFS_UTIL_ERROR_SEARCH_FAILED;
+				goto out;
+			}
+			items_pos = 0;
+			buf_off = 0;
+
+			if (search.key.nr_items == 0)
+				break;
+		}
+
+		header = (struct btrfs_ioctl_search_header *)(search.buf + buf_off);
+		if (*n >= capacity) {
+			size_t new_capacity = capacity ? capacity * 2 : 1;
+			uint64_t *new_ids;
+
+			new_ids = reallocarray(*ids, new_capacity,
+					       sizeof(**ids));
+			if (!new_ids)
+				return BTRFS_UTIL_ERROR_NO_MEMORY;
+
+			*ids = new_ids;
+			capacity = new_capacity;
+		}
+
+		(*ids)[(*n)++] = header->offset;
+
+		items_pos++;
+		buf_off += sizeof(*header) + header->len;
+		search.key.min_offset = header->offset + 1;
+	}
+
+	err = BTRFS_UTIL_OK;
+out:
+	if (err) {
+		free(ids);
+		*ids = NULL;
+		*n = 0;
+	}
+	return err;
+}
-- 
2.16.1


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

* [PATCH 14/26] libbtrfsutil: add filesystem sync helpers
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (12 preceding siblings ...)
  2018-01-26 18:41 ` [PATCH 13/26] libbtrfsutil: add btrfs_util_deleted_subvolumes() Omar Sandoval
@ 2018-01-26 18:41 ` Omar Sandoval
  2018-01-26 18:41 ` [PATCH 15/26] btrfs-progs: use libbtrfsutil for read-only property Omar Sandoval
                   ` (12 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:41 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

Namely, sync, start_sync, and wait_sync.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 Makefile                                     |   4 +-
 libbtrfsutil/btrfsutil.h                     |  44 ++++++++++++
 libbtrfsutil/filesystem.c                    | 103 +++++++++++++++++++++++++++
 libbtrfsutil/internal.h                      |   7 ++
 libbtrfsutil/python/btrfsutilpy.h            |   3 +
 libbtrfsutil/python/filesystem.c             |  94 ++++++++++++++++++++++++
 libbtrfsutil/python/module.c                 |  21 ++++++
 libbtrfsutil/python/setup.py                 |   1 +
 libbtrfsutil/python/tests/test_filesystem.py |  73 +++++++++++++++++++
 libbtrfsutil/subvolume.c                     |   7 --
 10 files changed, 348 insertions(+), 9 deletions(-)
 create mode 100644 libbtrfsutil/filesystem.c
 create mode 100644 libbtrfsutil/python/filesystem.c
 create mode 100644 libbtrfsutil/python/tests/test_filesystem.py

diff --git a/Makefile b/Makefile
index 2f798f11..8bbe1c5e 100644
--- a/Makefile
+++ b/Makefile
@@ -123,8 +123,8 @@ libbtrfs_headers = send-stream.h send-utils.h send.h kernel-lib/rbtree.h btrfs-l
 	       kernel-lib/radix-tree.h kernel-lib/sizes.h kernel-lib/raid56.h \
 	       extent-cache.h extent_io.h ioctl.h ctree.h btrfsck.h version.h
 libbtrfsutil_version := 0.1
-libbtrfsutil_objects = libbtrfsutil/errors.o libbtrfsutil/qgroup.o \
-		       libbtrfsutil/subvolume.o
+libbtrfsutil_objects = libbtrfsutil/errors.o libbtrfsutil/filesystem.o \
+		       libbtrfsutil/qgroup.o libbtrfsutil/subvolume.o
 convert_objects = convert/main.o convert/common.o convert/source-fs.o \
 		  convert/source-ext2.o convert/source-reiserfs.o
 mkfs_objects = mkfs/main.o mkfs/common.o
diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 74b05e29..d968f4de 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -70,6 +70,50 @@ enum btrfs_util_error {
  */
 const char *btrfs_util_strerror(enum btrfs_util_error err);
 
+/**
+ * btrfs_util_sync() - Force a sync on a specific Btrfs filesystem.
+ * @path: Path on a Btrfs filesystem.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_sync(const char *path);
+
+/**
+ * btrfs_util_f_sync() - See btrfs_util_sync().
+ */
+enum btrfs_util_error btrfs_util_f_sync(int fd);
+
+/**
+ * btrfs_util_start_sync() - Start a sync on a specific Btrfs filesystem but
+ * don't wait for it.
+ * @path: Path on a Btrfs filesystem.
+ * @transid: Returned transaction ID which can be waited on with
+ * btrfs_util_wait_sync(). This can be %NULL.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_start_sync(const char *path,
+					    uint64_t *transid);
+
+/**
+ * btrfs_util_f_start_sync() - See btrfs_util_start_sync().
+ */
+enum btrfs_util_error btrfs_util_f_start_sync(int fd, uint64_t *transid);
+
+/**
+ * btrfs_util_wait_sync() - Wait for a transaction with a given ID to sync.
+ * @path: Path on a Btrfs filesystem.
+ * @transid: Transaction ID to wait for, or zero for the current transaction.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_wait_sync(const char *path, uint64_t transid);
+
+/**
+ * btrfs_util_f_wait_sync() - See btrfs_util_wait_sync().
+ */
+enum btrfs_util_error btrfs_util_f_wait_sync(int fd, uint64_t transid);
+
 /**
  * btrfs_util_is_subvolume() - Return whether a given path is a Btrfs subvolume.
  * @path: Path to check.
diff --git a/libbtrfsutil/filesystem.c b/libbtrfsutil/filesystem.c
new file mode 100644
index 00000000..7199f920
--- /dev/null
+++ b/libbtrfsutil/filesystem.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/btrfs.h>
+
+#include "btrfsutil.h"
+#include "internal.h"
+
+enum btrfs_util_error btrfs_util_sync(const char *path)
+{
+	enum btrfs_util_error err;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+	err = btrfs_util_f_sync(fd);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+enum btrfs_util_error btrfs_util_f_sync(int fd)
+{
+	int ret;
+
+	ret = ioctl(fd, BTRFS_IOC_SYNC, NULL);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_SYNC_FAILED;
+
+	return BTRFS_UTIL_OK;
+}
+
+enum btrfs_util_error btrfs_util_start_sync(const char *path,
+					    uint64_t *transid)
+{
+	enum btrfs_util_error err;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+	err = btrfs_util_f_start_sync(fd, transid);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+enum btrfs_util_error btrfs_util_f_start_sync(int fd, uint64_t *transid)
+{
+	int ret;
+
+	ret = ioctl(fd, BTRFS_IOC_START_SYNC, transid);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_START_SYNC_FAILED;
+
+	return BTRFS_UTIL_OK;
+}
+
+enum btrfs_util_error btrfs_util_wait_sync(const char *path, uint64_t transid)
+{
+	enum btrfs_util_error err;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+	err = btrfs_util_f_wait_sync(fd, transid);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+enum btrfs_util_error btrfs_util_f_wait_sync(int fd, uint64_t transid)
+{
+	int ret;
+
+	ret = ioctl(fd, BTRFS_IOC_WAIT_SYNC, &transid);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_WAIT_SYNC_FAILED;
+
+	return BTRFS_UTIL_OK;
+}
diff --git a/libbtrfsutil/internal.h b/libbtrfsutil/internal.h
index bc91ad88..9bc00d0e 100644
--- a/libbtrfsutil/internal.h
+++ b/libbtrfsutil/internal.h
@@ -26,4 +26,11 @@
 #define le32_to_cpu __le32_to_cpu
 #define le64_to_cpu __le64_to_cpu
 
+#define SAVE_ERRNO_AND_CLOSE(fd) {	\
+	int saved_errno = errno;	\
+					\
+	close(fd);			\
+	errno = saved_errno;		\
+}
+
 #endif /* BTRFS_UTIL_INTERNAL_H */
diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
index be5122e2..af3a6edf 100644
--- a/libbtrfsutil/python/btrfsutilpy.h
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -63,6 +63,9 @@ void SetFromBtrfsUtilErrorWithPaths(enum btrfs_util_error err,
 				    struct path_arg *path1,
 				    struct path_arg *path2);
 
+PyObject *filesystem_sync(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *start_sync(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *wait_sync(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *is_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds);
 PyObject *subvolume_path(PyObject *self, PyObject *args, PyObject *kwds);
diff --git a/libbtrfsutil/python/filesystem.c b/libbtrfsutil/python/filesystem.c
new file mode 100644
index 00000000..05cc49a3
--- /dev/null
+++ b/libbtrfsutil/python/filesystem.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "btrfsutilpy.h"
+
+PyObject *filesystem_sync(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *keywords[] = {"path", NULL};
+	struct path_arg path = {.allow_fd = true};
+	enum btrfs_util_error err;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:sync", keywords,
+					 &path_converter, &path))
+		return NULL;
+
+	if (path.path)
+		err = btrfs_util_sync(path.path);
+	else
+		err = btrfs_util_f_sync(path.fd);
+	if (err) {
+		SetFromBtrfsUtilErrorWithPath(err, &path);
+		path_cleanup(&path);
+		return NULL;
+	}
+
+	path_cleanup(&path);
+	Py_RETURN_NONE;
+}
+
+PyObject *start_sync(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *keywords[] = {"path", NULL};
+	struct path_arg path = {.allow_fd = true};
+	uint64_t transid;
+	enum btrfs_util_error err;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:start_sync", keywords,
+					 &path_converter, &path))
+		return NULL;
+
+	if (path.path)
+		err = btrfs_util_start_sync(path.path, &transid);
+	else
+		err = btrfs_util_f_start_sync(path.fd, &transid);
+	if (err) {
+		SetFromBtrfsUtilErrorWithPath(err, &path);
+		path_cleanup(&path);
+		return NULL;
+	}
+
+	path_cleanup(&path);
+	return PyLong_FromUnsignedLongLong(transid);
+}
+
+PyObject *wait_sync(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *keywords[] = {"path", "transid", NULL};
+	struct path_arg path = {.allow_fd = true};
+	unsigned long long transid = 0;
+	enum btrfs_util_error err;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:wait_sync", keywords,
+					 &path_converter, &path, &transid))
+		return NULL;
+
+	if (path.path)
+		err = btrfs_util_wait_sync(path.path, transid);
+	else
+		err = btrfs_util_f_wait_sync(path.fd, transid);
+	if (err) {
+		SetFromBtrfsUtilErrorWithPath(err, &path);
+		path_cleanup(&path);
+		return NULL;
+	}
+
+	path_cleanup(&path);
+	Py_RETURN_NONE;
+}
diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
index eaa062ac..2dbdc7be 100644
--- a/libbtrfsutil/python/module.c
+++ b/libbtrfsutil/python/module.c
@@ -155,6 +155,27 @@ void path_cleanup(struct path_arg *path)
 }
 
 static PyMethodDef btrfsutil_methods[] = {
+	{"sync", (PyCFunction)filesystem_sync,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "sync(path)\n\n"
+	 "Sync a specific Btrfs filesystem.\n\n"
+	 "Arguments:\n"
+	 "path -- string, bytes, path-like object, or open file descriptor"},
+	{"start_sync", (PyCFunction)start_sync,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "start_sync(path) -> int\n\n"
+	 "Start a sync on a specific Btrfs filesystem and return the\n"
+	 "transaction ID.\n\n"
+	 "Arguments:\n"
+	 "path -- string, bytes, path-like object, or open file descriptor"},
+	{"wait_sync", (PyCFunction)wait_sync,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "wait_sync(path, transid=0)\n\n"
+	 "Wait for a transaction to sync.\n"
+	 "Arguments:\n"
+	 "path -- string, bytes, path-like object, or open file descriptor\n"
+	 "transid -- int transaction ID to wait for, or zero for the current\n"
+	 "transaction"},
 	{"is_subvolume", (PyCFunction)is_subvolume,
 	 METH_VARARGS | METH_KEYWORDS,
 	 "is_subvolume(path) -> bool\n\n"
diff --git a/libbtrfsutil/python/setup.py b/libbtrfsutil/python/setup.py
index a2a43f80..6ef3d515 100755
--- a/libbtrfsutil/python/setup.py
+++ b/libbtrfsutil/python/setup.py
@@ -78,6 +78,7 @@ module = Extension(
     sources=[
         'constants.c',
         'error.c',
+        'filesystem.c',
         'module.c',
         'qgroup.c',
         'subvolume.c',
diff --git a/libbtrfsutil/python/tests/test_filesystem.py b/libbtrfsutil/python/tests/test_filesystem.py
new file mode 100644
index 00000000..006a6b1e
--- /dev/null
+++ b/libbtrfsutil/python/tests/test_filesystem.py
@@ -0,0 +1,73 @@
+# Copyright (C) 2018 Facebook
+#
+# This file is part of libbtrfsutil.
+#
+# libbtrfsutil is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# libbtrfsutil 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import time
+
+import btrfsutil
+from tests import BtrfsTestCase, HAVE_PATH_LIKE
+
+
+def touch(path):
+    now = time.time()
+    os.utime(path, (now, now))
+
+
+class TestSubvolume(BtrfsTestCase):
+    def super_generation(self):
+        with open(self.image, 'rb') as f:
+            # csum is 32 bytes, fsid is 16 bytes, bytenr is 8 bytes, flags is 8
+            # bytes
+            f.seek(65536 + 32 + 16 + 8 + 8)
+            self.assertEqual(f.read(8), b'_BHRfS_M')
+            return int.from_bytes(f.read(8), 'little')
+
+    def test_sync(self):
+        old_generation = self.super_generation()
+        for arg in self.path_or_fd(self.mountpoint):
+            with self.subTest(type=type(arg)):
+                touch(arg)
+                btrfsutil.sync(arg)
+                new_generation = self.super_generation()
+                self.assertGreater(new_generation, old_generation)
+                old_generation = new_generation
+
+    def test_start_sync(self):
+        old_generation = self.super_generation()
+        for arg in self.path_or_fd(self.mountpoint):
+            with self.subTest(type=type(arg)):
+                touch(arg)
+                transid = btrfsutil.start_sync(arg)
+                self.assertGreater(transid, old_generation)
+
+    def test_wait_sync(self):
+        old_generation = self.super_generation()
+        for arg in self.path_or_fd(self.mountpoint):
+            with self.subTest(type=type(arg)):
+                touch(arg)
+                transid = btrfsutil.start_sync(arg)
+                btrfsutil.wait_sync(arg, transid)
+                new_generation = self.super_generation()
+                self.assertGreater(new_generation, old_generation)
+                old_generation = new_generation
+
+                touch(arg)
+                btrfsutil.start_sync(arg)
+                btrfsutil.wait_sync(arg)
+                new_generation = self.super_generation()
+                self.assertGreater(new_generation, old_generation)
+                old_generation = new_generation
diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c
index 903bcfb6..8b9a888e 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -33,13 +33,6 @@
 #include "btrfsutil.h"
 #include "internal.h"
 
-#define SAVE_ERRNO_AND_CLOSE(fd) {	\
-	int saved_errno = errno;	\
-					\
-	close(fd);			\
-	errno = saved_errno;		\
-}
-
 /*
  * This intentionally duplicates btrfs_util_f_is_subvolume() instead of opening
  * a file descriptor and calling it, because fstat() and fstatfs() don't accept
-- 
2.16.1


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

* [PATCH 15/26] btrfs-progs: use libbtrfsutil for read-only property
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (13 preceding siblings ...)
  2018-01-26 18:41 ` [PATCH 14/26] libbtrfsutil: add filesystem sync helpers Omar Sandoval
@ 2018-01-26 18:41 ` Omar Sandoval
  2018-01-26 18:41 ` [PATCH 16/26] btrfs-progs: use libbtrfsutil for sync ioctls Omar Sandoval
                   ` (11 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:41 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 props.c | 69 ++++++++++++++++++++++++-----------------------------------------
 1 file changed, 25 insertions(+), 44 deletions(-)

diff --git a/props.c b/props.c
index cddbd927..e4edba06 100644
--- a/props.c
+++ b/props.c
@@ -21,6 +21,8 @@
 #include <fcntl.h>
 #include <unistd.h>
 
+#include <btrfsutil.h>
+
 #include "ctree.h"
 #include "commands.h"
 #include "utils.h"
@@ -41,56 +43,35 @@ static int prop_read_only(enum prop_object_type type,
 			  const char *name,
 			  const char *value)
 {
-	int ret = 0;
-	int fd = -1;
-	u64 flags = 0;
-
-	fd = open(object, O_RDONLY);
-	if (fd < 0) {
-		ret = -errno;
-		error("failed to open %s: %s", object, strerror(-ret));
-		goto out;
-	}
+	enum btrfs_util_error err;
+	bool read_only;
 
-	ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
-	if (ret < 0) {
-		ret = -errno;
-		error("failed to get flags for %s: %s", object,
-				strerror(-ret));
-		goto out;
-	}
-
-	if (!value) {
-		if (flags & BTRFS_SUBVOL_RDONLY)
-			fprintf(stdout, "ro=true\n");
-		else
-			fprintf(stdout, "ro=false\n");
-		ret = 0;
-		goto out;
-	}
+	if (value) {
+		if (!strcmp(value, "true")) {
+			read_only = true;
+		} else if (!strcmp(value, "false")) {
+			read_only = false;
+		} else {
+			error("invalid value for property: %s", value);
+			return -EINVAL;
+		}
 
-	if (!strcmp(value, "true")) {
-		flags |= BTRFS_SUBVOL_RDONLY;
-	} else if (!strcmp(value, "false")) {
-		flags = flags & ~BTRFS_SUBVOL_RDONLY;
+		err = btrfs_util_set_subvolume_read_only(object, read_only);
+		if (err) {
+			error_btrfs_util(err);
+			return -errno;
+		}
 	} else {
-		ret = -EINVAL;
-		error("invalid value for property: %s", value);
-		goto out;
-	}
+		err = btrfs_util_get_subvolume_read_only(object, &read_only);
+		if (err) {
+			error_btrfs_util(err);
+			return -errno;
+		}
 
-	ret = ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &flags);
-	if (ret < 0) {
-		ret = -errno;
-		error("failed to set flags for %s: %s", object,
-				strerror(-ret));
-		goto out;
+		printf("ro=%s\n", read_only ? "true" : "false");
 	}
 
-out:
-	if (fd != -1)
-		close(fd);
-	return ret;
+	return 0;
 }
 
 static int prop_label(enum prop_object_type type,
-- 
2.16.1


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

* [PATCH 16/26] btrfs-progs: use libbtrfsutil for sync ioctls
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (14 preceding siblings ...)
  2018-01-26 18:41 ` [PATCH 15/26] btrfs-progs: use libbtrfsutil for read-only property Omar Sandoval
@ 2018-01-26 18:41 ` Omar Sandoval
  2018-01-26 18:41 ` [PATCH 17/26] btrfs-progs: use libbtrfsutil for set-default Omar Sandoval
                   ` (10 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:41 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 cmds-filesystem.c | 20 ++++++--------------
 cmds-qgroup.c     |  7 ++++---
 cmds-subvolume.c  | 23 ++++++++++++++---------
 3 files changed, 24 insertions(+), 26 deletions(-)

diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index 17d399d5..2353fdb0 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -28,6 +28,8 @@
 #include <linux/limits.h>
 #include <getopt.h>
 
+#include <btrfsutil.h>
+
 #include "kerncompat.h"
 #include "ctree.h"
 #include "utils.h"
@@ -815,26 +817,16 @@ static const char * const cmd_filesystem_sync_usage[] = {
 
 static int cmd_filesystem_sync(int argc, char **argv)
 {
-	int 	fd, res, e;
-	char	*path;
-	DIR	*dirstream = NULL;
+	enum btrfs_util_error err;
 
 	clean_args_no_options(argc, argv, cmd_filesystem_sync_usage);
 
 	if (check_argc_exact(argc - optind, 1))
 		usage(cmd_filesystem_sync_usage);
 
-	path = argv[optind];
-
-	fd = btrfs_open_dir(path, &dirstream, 1);
-	if (fd < 0)
-		return 1;
-
-	res = ioctl(fd, BTRFS_IOC_SYNC);
-	e = errno;
-	close_file_or_dir(fd, dirstream);
-	if( res < 0 ){
-		error("sync ioctl failed on '%s': %s", path, strerror(e));
+	err = btrfs_util_sync(argv[optind]);
+	if (err) {
+		error_btrfs_util(err);
 		return 1;
 	}
 
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index 38382ea9..fac692d4 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -302,6 +302,7 @@ static int cmd_qgroup_show(int argc, char **argv)
 	int filter_flag = 0;
 	unsigned unit_mode;
 	int sync = 0;
+	enum btrfs_util_error err;
 
 	struct btrfs_qgroup_comparer_set *comparer_set;
 	struct btrfs_qgroup_filter_set *filter_set;
@@ -375,10 +376,10 @@ static int cmd_qgroup_show(int argc, char **argv)
 	}
 
 	if (sync) {
-		ret = ioctl(fd, BTRFS_IOC_SYNC);
-		if (ret < 0)
+		err = btrfs_util_f_sync(fd);
+		if (err)
 			warning("sync ioctl failed on '%s': %s", path,
-			      strerror(errno));
+				strerror(errno));
 	}
 
 	if (filter_flag) {
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index f8e34bc8..89a4ebb0 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -219,12 +219,18 @@ out:
 
 static int wait_for_commit(int fd)
 {
-	int ret;
+	enum btrfs_util_error err;
+	uint64_t transid;
 
-	ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
-	if (ret < 0)
-		return ret;
-	return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
+	err = btrfs_util_f_start_sync(fd, &transid);
+	if (err)
+		return -1;
+
+	err = btrfs_util_f_wait_sync(fd, transid);
+	if (err)
+		return -1;
+
+	return 0;
 }
 
 static const char * const cmd_subvol_delete_usage[] = {
@@ -940,10 +946,9 @@ static int cmd_subvol_find_new(int argc, char **argv)
 	if (fd < 0)
 		return 1;
 
-	ret = ioctl(fd, BTRFS_IOC_SYNC);
-	if (ret < 0) {
-		error("sync ioctl failed on '%s': %s",
-			subvol, strerror(errno));
+	err = btrfs_util_f_sync(fd);
+	if (err) {
+		error_btrfs_util(err);
 		close_file_or_dir(fd, dirstream);
 		return 1;
 	}
-- 
2.16.1


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

* [PATCH 17/26] btrfs-progs: use libbtrfsutil for set-default
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (15 preceding siblings ...)
  2018-01-26 18:41 ` [PATCH 16/26] btrfs-progs: use libbtrfsutil for sync ioctls Omar Sandoval
@ 2018-01-26 18:41 ` Omar Sandoval
  2018-01-26 18:41 ` [PATCH 18/26] btrfs-progs: use libbtrfsutil for get-default Omar Sandoval
                   ` (9 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:41 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 cmds-subvolume.c | 47 ++++++++++-------------------------------------
 messages.h       | 14 ++++++++++++++
 2 files changed, 24 insertions(+), 37 deletions(-)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 89a4ebb0..aaf88af8 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -28,6 +28,8 @@
 #include <uuid/uuid.h>
 #include <linux/magic.h>
 
+#include <btrfsutil.h>
+
 #include "kerncompat.h"
 #include "ioctl.h"
 #include "qgroup.h"
@@ -853,11 +855,9 @@ static const char * const cmd_subvol_set_default_usage[] = {
 
 static int cmd_subvol_set_default(int argc, char **argv)
 {
-	int	ret=0, fd, e;
-	u64	objectid;
-	char	*path;
-	char	*subvolid;
-	DIR	*dirstream = NULL;
+	u64 objectid;
+	char *path;
+	enum btrfs_util_error err;
 
 	clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
 
@@ -867,44 +867,17 @@ static int cmd_subvol_set_default(int argc, char **argv)
 
 	if (argc - optind == 1) {
 		/* path to the subvolume is specified */
+		objectid = 0;
 		path = argv[optind];
-
-		ret = test_issubvolume(path);
-		if (ret < 0) {
-			error("stat error: %s", strerror(-ret));
-			return 1;
-		} else if (!ret) {
-			error("'%s' is not a subvolume", path);
-			return 1;
-		}
-
-		fd = btrfs_open_dir(path, &dirstream, 1);
-		if (fd < 0)
-			return 1;
-
-		ret = lookup_path_rootid(fd, &objectid);
-		if (ret) {
-			error("unable to get subvol id: %s", strerror(-ret));
-			close_file_or_dir(fd, dirstream);
-			return 1;
-		}
 	} else {
 		/* subvol id and path to the filesystem are specified */
-		subvolid = argv[optind];
+		objectid = arg_strtou64(argv[optind]);
 		path = argv[optind + 1];
-		objectid = arg_strtou64(subvolid);
-
-		fd = btrfs_open_dir(path, &dirstream, 1);
-		if (fd < 0)
-			return 1;
 	}
 
-	ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
-	e = errno;
-	close_file_or_dir(fd, dirstream);
-	if (ret < 0) {
-		error("unable to set a new default subvolume: %s",
-			strerror(e));
+	err = btrfs_util_set_default_subvolume(path, objectid);
+	if (err) {
+		error_btrfs_util(err);
 		return 1;
 	}
 	return 0;
diff --git a/messages.h b/messages.h
index 4999c7b9..cfabb86f 100644
--- a/messages.h
+++ b/messages.h
@@ -54,6 +54,20 @@
 			DO_ABORT_ON_ERROR;				\
 	} while (0)
 
+#define error_btrfs_util(err)						\
+	do {								\
+		const char *errno_str = strerror(errno);		\
+		const char *lib_str = btrfs_util_strerror(err)		\
+		PRINT_TRACE_ON_ERROR;					\
+		PRINT_VERBOSE_ERROR;					\
+		if (lib_str && strcmp(errno_str, lib_str) != 0)		\
+			__btrfs_error("%s: %s", lib_str, errno_str);	\
+		else							\
+			__btrfs_error("%s", errno_str);			\
+		DO_ABORT_ON_ERROR;					\
+	} while (0)
+
+
 #define warning(fmt, ...)						\
 	do {								\
 		PRINT_TRACE_ON_ERROR;					\
-- 
2.16.1


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

* [PATCH 18/26] btrfs-progs: use libbtrfsutil for get-default
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (16 preceding siblings ...)
  2018-01-26 18:41 ` [PATCH 17/26] btrfs-progs: use libbtrfsutil for set-default Omar Sandoval
@ 2018-01-26 18:41 ` Omar Sandoval
  2018-01-26 18:41 ` [PATCH 19/26] btrfs-progs: use libbtrfsutil for subvol create and snapshot Omar Sandoval
                   ` (8 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:41 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

The only thing of note here is the "top level" column. This used to mean
something else, but for a long time it has been equal to the parent ID.
I preserved this for backwards compatability.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 cmds-subvolume.c | 55 ++++++++++++++++++++++++++-----------------------------
 1 file changed, 26 insertions(+), 29 deletions(-)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index aaf88af8..557108c1 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -14,6 +14,7 @@
  * Boston, MA 021110-1307, USA.
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -788,32 +789,25 @@ static const char * const cmd_subvol_get_default_usage[] = {
 static int cmd_subvol_get_default(int argc, char **argv)
 {
 	int fd = -1;
-	int ret;
-	char *subvol;
-	struct btrfs_list_filter_set *filter_set;
-	u64 default_id;
+	int ret = 1;
+	uint64_t default_id;
 	DIR *dirstream = NULL;
+	enum btrfs_util_error err;
+	struct btrfs_util_subvolume_info subvol;
+	char *path;
 
 	clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
 
 	if (check_argc_exact(argc - optind, 1))
 		usage(cmd_subvol_get_default_usage);
 
-	subvol = argv[1];
-	fd = btrfs_open_dir(subvol, &dirstream, 1);
+	fd = btrfs_open_dir(argv[1], &dirstream, 1);
 	if (fd < 0)
 		return 1;
 
-	ret = btrfs_list_get_default_subvolume(fd, &default_id);
-	if (ret) {
-		error("failed to look up default subvolume: %s",
-			strerror(errno));
-		goto out;
-	}
-
-	ret = 1;
-	if (default_id == 0) {
-		error("'default' dir item not found");
+	err = btrfs_util_f_get_default_subvolume(fd, &default_id);
+	if (err) {
+		error_btrfs_util(err);
 		goto out;
 	}
 
@@ -824,24 +818,27 @@ static int cmd_subvol_get_default(int argc, char **argv)
 		goto out;
 	}
 
-	filter_set = btrfs_list_alloc_filter_set();
-	btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
-				default_id);
+	err = btrfs_util_f_subvolume_info(fd, default_id, &subvol);
+	if (err) {
+		error_btrfs_util(err);
+		goto out;
+	}
 
-	/* by default we shall print the following columns*/
-	btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
-	btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
-	btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
-	btrfs_list_setup_print_column(BTRFS_LIST_PATH);
+	err = btrfs_util_f_subvolume_path(fd, default_id, &path);
+	if (err) {
+		error_btrfs_util(err);
+		goto out;
+	}
 
-	ret = btrfs_list_subvols_print(fd, filter_set, NULL,
-		BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
+	printf("ID %" PRIu64 " gen %" PRIu64 " top level %" PRIu64 " path %s\n",
+	       subvol.id, subvol.generation, subvol.parent_id, path);
 
-	if (filter_set)
-		free(filter_set);
+	free(path);
+
+	ret = 0;
 out:
 	close_file_or_dir(fd, dirstream);
-	return !!ret;
+	return ret;
 }
 
 static const char * const cmd_subvol_set_default_usage[] = {
-- 
2.16.1


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

* [PATCH 19/26] btrfs-progs: use libbtrfsutil for subvol create and snapshot
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (17 preceding siblings ...)
  2018-01-26 18:41 ` [PATCH 18/26] btrfs-progs: use libbtrfsutil for get-default Omar Sandoval
@ 2018-01-26 18:41 ` Omar Sandoval
  2018-01-26 18:41 ` [PATCH 20/26] btrfs-progs: use libbtrfsutil for subvol delete Omar Sandoval
                   ` (7 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:41 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

These become trivial calls to the libbtrfsutil helpers, and we can get
rid of the qgroup inherit code in progs.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 cmds-subvolume.c | 225 ++++++++++++++-----------------------------------------
 qgroup.c         |  64 ----------------
 qgroup.h         |   2 -
 3 files changed, 58 insertions(+), 233 deletions(-)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 557108c1..6b474fe4 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -103,6 +103,35 @@ static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
 	return 0;
 }
 
+static int qgroup_inherit_add_group(struct btrfs_util_qgroup_inherit **inherit,
+				    const char *arg)
+{
+	enum btrfs_util_error err;
+	u64 qgroupid;
+
+	if (!*inherit) {
+		err = btrfs_util_create_qgroup_inherit(0, inherit);
+		if (err) {
+			error_btrfs_util(err);
+			return -1;
+		}
+	}
+
+	qgroupid = parse_qgroupid(optarg);
+	if (qgroupid == 0) {
+		error("invalid qgroup specification, qgroupid must not be 0");
+		return -1;
+	}
+
+	err = btrfs_util_qgroup_inherit_add_group(inherit, qgroupid);
+	if (err) {
+		error_btrfs_util(err);
+		return -1;
+	}
+
+	return 0;
+}
+
 static const char * const subvolume_cmd_group_usage[] = {
 	"btrfs subvolume <command> <args>",
 	NULL
@@ -121,15 +150,9 @@ static const char * const cmd_subvol_create_usage[] = {
 
 static int cmd_subvol_create(int argc, char **argv)
 {
-	int	retval, res, len;
-	int	fddst = -1;
-	char	*dupname = NULL;
-	char	*dupdir = NULL;
-	char	*newname;
-	char	*dstdir;
-	char	*dst;
-	struct btrfs_qgroup_inherit *inherit = NULL;
-	DIR	*dirstream = NULL;
+	struct btrfs_util_qgroup_inherit *inherit = NULL;
+	enum btrfs_util_error err;
+	int retval = 1;
 
 	while (1) {
 		int c = getopt(argc, argv, "i:");
@@ -138,11 +161,8 @@ static int cmd_subvol_create(int argc, char **argv)
 
 		switch (c) {
 		case 'i':
-			res = qgroup_inherit_add_group(&inherit, optarg);
-			if (res) {
-				retval = res;
+			if (qgroup_inherit_add_group(&inherit, optarg) == -1)
 				goto out;
-			}
 			break;
 		default:
 			usage(cmd_subvol_create_usage);
@@ -152,70 +172,18 @@ static int cmd_subvol_create(int argc, char **argv)
 	if (check_argc_exact(argc - optind, 1))
 		usage(cmd_subvol_create_usage);
 
-	dst = argv[optind];
-
-	retval = 1;	/* failure */
-	res = test_isdir(dst);
-	if (res < 0 && res != -ENOENT) {
-		error("cannot access %s: %s", dst, strerror(-res));
-		goto out;
-	}
-	if (res >= 0) {
-		error("target path already exists: %s", dst);
-		goto out;
-	}
-
-	dupname = strdup(dst);
-	newname = basename(dupname);
-	dupdir = strdup(dst);
-	dstdir = dirname(dupdir);
-
-	if (!test_issubvolname(newname)) {
-		error("invalid subvolume name: %s", newname);
-		goto out;
-	}
-
-	len = strlen(newname);
-	if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
-		error("subvolume name too long: %s", newname);
-		goto out;
-	}
-
-	fddst = btrfs_open_dir(dstdir, &dirstream, 1);
-	if (fddst < 0)
-		goto out;
-
-	printf("Create subvolume '%s/%s'\n", dstdir, newname);
-	if (inherit) {
-		struct btrfs_ioctl_vol_args_v2	args;
-
-		memset(&args, 0, sizeof(args));
-		strncpy_null(args.name, newname);
-		args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
-		args.size = qgroup_inherit_size(inherit);
-		args.qgroup_inherit = inherit;
-
-		res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
-	} else {
-		struct btrfs_ioctl_vol_args	args;
-
-		memset(&args, 0, sizeof(args));
-		strncpy_null(args.name, newname);
-
-		res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
-	}
+	printf("Create subvolume '%s'\n", argv[optind]);
 
-	if (res < 0) {
-		error("cannot create subvolume: %s", strerror(errno));
+	err = btrfs_util_create_subvolume(argv[optind], 0, NULL, inherit);
+	if (err) {
+		error_btrfs_util(err);
 		goto out;
 	}
 
-	retval = 0;	/* success */
+	retval = 0;
 out:
-	close_file_or_dir(fddst, dirstream);
-	free(inherit);
-	free(dupname);
-	free(dupdir);
+	if (inherit)
+		btrfs_util_destroy_qgroup_inherit(inherit);
 
 	return retval;
 }
@@ -653,19 +621,11 @@ static const char * const cmd_subvol_snapshot_usage[] = {
 
 static int cmd_subvol_snapshot(int argc, char **argv)
 {
-	char	*subvol, *dst;
-	int	res, retval;
-	int	fd = -1, fddst = -1;
-	int	len, readonly = 0;
-	char	*dupname = NULL;
-	char	*dupdir = NULL;
-	char	*newname;
-	char	*dstdir;
-	struct btrfs_ioctl_vol_args_v2	args;
-	struct btrfs_qgroup_inherit *inherit = NULL;
-	DIR *dirstream1 = NULL, *dirstream2 = NULL;
+	struct btrfs_util_qgroup_inherit *inherit = NULL;
+	enum btrfs_util_error err;
+	int flags = 0;
+	int retval = 1;
 
-	memset(&args, 0, sizeof(args));
 	while (1) {
 		int c = getopt(argc, argv, "i:r");
 		if (c < 0)
@@ -673,14 +633,11 @@ static int cmd_subvol_snapshot(int argc, char **argv)
 
 		switch (c) {
 		case 'i':
-			res = qgroup_inherit_add_group(&inherit, optarg);
-			if (res) {
-				retval = res;
+			if (qgroup_inherit_add_group(&inherit, optarg) == -1)
 				goto out;
-			}
 			break;
 		case 'r':
-			readonly = 1;
+			flags |= BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY;
 			break;
 		default:
 			usage(cmd_subvol_snapshot_usage);
@@ -690,92 +647,26 @@ static int cmd_subvol_snapshot(int argc, char **argv)
 	if (check_argc_exact(argc - optind, 2))
 		usage(cmd_subvol_snapshot_usage);
 
-	subvol = argv[optind];
-	dst = argv[optind + 1];
-
-	retval = 1;	/* failure */
-	res = test_issubvolume(subvol);
-	if (res < 0) {
-		error("cannot access subvolume %s: %s", subvol, strerror(-res));
-		goto out;
-	}
-	if (!res) {
-		error("not a subvolume: %s", subvol);
-		goto out;
-	}
-
-	res = test_isdir(dst);
-	if (res < 0 && res != -ENOENT) {
-		error("cannot access %s: %s", dst, strerror(-res));
-		goto out;
-	}
-	if (res == 0) {
-		error("'%s' exists and it is not a directory", dst);
-		goto out;
-	}
-
-	if (res > 0) {
-		dupname = strdup(subvol);
-		newname = basename(dupname);
-		dstdir = dst;
+	if (flags & BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY) {
+		printf("Create a readonly snapshot of '%s' in '%s'\n",
+		       argv[optind], argv[optind + 1]);
 	} else {
-		dupname = strdup(dst);
-		newname = basename(dupname);
-		dupdir = strdup(dst);
-		dstdir = dirname(dupdir);
+		printf("Create a snapshot of '%s' in '%s'\n", argv[optind],
+		       argv[optind + 1]);
 	}
 
-	if (!test_issubvolname(newname)) {
-		error("invalid snapshot name '%s'", newname);
-		goto out;
-	}
-
-	len = strlen(newname);
-	if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
-		error("snapshot name too long '%s'", newname);
-		goto out;
-	}
-
-	fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
-	if (fddst < 0)
-		goto out;
-
-	fd = btrfs_open_dir(subvol, &dirstream2, 1);
-	if (fd < 0)
-		goto out;
-
-	if (readonly) {
-		args.flags |= BTRFS_SUBVOL_RDONLY;
-		printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
-		       subvol, dstdir, newname);
-	} else {
-		printf("Create a snapshot of '%s' in '%s/%s'\n",
-		       subvol, dstdir, newname);
-	}
-
-	args.fd = fd;
-	if (inherit) {
-		args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
-		args.size = qgroup_inherit_size(inherit);
-		args.qgroup_inherit = inherit;
-	}
-	strncpy_null(args.name, newname);
-
-	res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
-
-	if (res < 0) {
-		error("cannot snapshot '%s': %s", subvol, strerror(errno));
+	err = btrfs_util_create_snapshot(argv[optind], argv[optind + 1], flags,
+					 NULL, inherit);
+	if (err) {
+		error_btrfs_util(err);
 		goto out;
 	}
 
-	retval = 0;	/* success */
 
+	retval = 0;
 out:
-	close_file_or_dir(fddst, dirstream1);
-	close_file_or_dir(fd, dirstream2);
-	free(inherit);
-	free(dupname);
-	free(dupdir);
+	if (inherit)
+		btrfs_util_destroy_qgroup_inherit(inherit);
 
 	return retval;
 }
diff --git a/qgroup.c b/qgroup.c
index e9158a26..6e5fdb01 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -1246,67 +1246,3 @@ out:
 	free(opt_tmp);
 	return ret;
 }
-
-int qgroup_inherit_size(struct btrfs_qgroup_inherit *p)
-{
-	return sizeof(*p) + sizeof(p->qgroups[0]) *
-			    (p->num_qgroups + 2 * p->num_ref_copies +
-			     2 * p->num_excl_copies);
-}
-
-static int
-qgroup_inherit_realloc(struct btrfs_qgroup_inherit **inherit, int n, int pos)
-{
-	struct btrfs_qgroup_inherit *out;
-	int nitems = 0;
-
-	if (*inherit) {
-		nitems = (*inherit)->num_qgroups +
-			 (*inherit)->num_ref_copies +
-			 (*inherit)->num_excl_copies;
-	}
-
-	out = calloc(sizeof(*out) + sizeof(out->qgroups[0]) * (nitems + n), 1);
-	if (out == NULL) {
-		error("not enough memory");
-		return -ENOMEM;
-	}
-
-	if (*inherit) {
-		struct btrfs_qgroup_inherit *i = *inherit;
-		int s = sizeof(out->qgroups[0]);
-
-		out->num_qgroups = i->num_qgroups;
-		out->num_ref_copies = i->num_ref_copies;
-		out->num_excl_copies = i->num_excl_copies;
-		memcpy(out->qgroups, i->qgroups, pos * s);
-		memcpy(out->qgroups + pos + n, i->qgroups + pos,
-		       (nitems - pos) * s);
-	}
-	free(*inherit);
-	*inherit = out;
-
-	return 0;
-}
-
-int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg)
-{
-	int ret;
-	u64 qgroupid = parse_qgroupid(arg);
-	int pos = 0;
-
-	if (qgroupid == 0) {
-		error("invalid qgroup specification, qgroupid must not 0");
-		return -EINVAL;
-	}
-
-	if (*inherit)
-		pos = (*inherit)->num_qgroups;
-	ret = qgroup_inherit_realloc(inherit, 1, pos);
-	if (ret)
-		return ret;
-
-	(*inherit)->qgroups[(*inherit)->num_qgroups++] = qgroupid;
-
-	return 0;
-}
diff --git a/qgroup.h b/qgroup.h
index bb6610d7..97c6cd9e 100644
--- a/qgroup.h
+++ b/qgroup.h
@@ -90,7 +90,5 @@ struct btrfs_qgroup_comparer_set *btrfs_qgroup_alloc_comparer_set(void);
 int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set **comp_set,
 				enum btrfs_qgroup_comp_enum comparer,
 				int is_descending);
-int qgroup_inherit_size(struct btrfs_qgroup_inherit *p);
-int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg);
 
 #endif
-- 
2.16.1


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

* [PATCH 20/26] btrfs-progs: use libbtrfsutil for subvol delete
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (18 preceding siblings ...)
  2018-01-26 18:41 ` [PATCH 19/26] btrfs-progs: use libbtrfsutil for subvol create and snapshot Omar Sandoval
@ 2018-01-26 18:41 ` Omar Sandoval
  2018-01-26 18:41 ` [PATCH 21/26] btrfs-progs: use libbtrfsutil for subvol show Omar Sandoval
                   ` (6 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:41 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

Most of the interesting part of this command is the commit mode, so this
only saves a little bit of code.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 cmds-subvolume.c | 23 ++++++++---------------
 1 file changed, 8 insertions(+), 15 deletions(-)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 6b474fe4..c5e03011 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -225,7 +225,6 @@ static int cmd_subvol_delete(int argc, char **argv)
 	int res, ret = 0;
 	int cnt;
 	int fd = -1;
-	struct btrfs_ioctl_vol_args	args;
 	char	*dname, *vname, *cpath;
 	char	*dupdname = NULL;
 	char	*dupvname = NULL;
@@ -237,6 +236,7 @@ static int cmd_subvol_delete(int argc, char **argv)
 	char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
 	struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = { NULL, };
 	enum { COMMIT_AFTER = 1, COMMIT_EACH = 2 };
+	enum btrfs_util_error err;
 
 	while (1) {
 		int c;
@@ -280,14 +280,9 @@ static int cmd_subvol_delete(int argc, char **argv)
 again:
 	path = argv[cnt];
 
-	res = test_issubvolume(path);
-	if (res < 0) {
-		error("cannot access subvolume %s: %s", path, strerror(-res));
-		ret = 1;
-		goto out;
-	}
-	if (!res) {
-		error("not a subvolume: %s", path);
+	err = btrfs_util_is_subvolume(path);
+	if (err) {
+		error_btrfs_util(err);
 		ret = 1;
 		goto out;
 	}
@@ -314,12 +309,10 @@ again:
 	printf("Delete subvolume (%s): '%s/%s'\n",
 		commit_mode == COMMIT_EACH || (commit_mode == COMMIT_AFTER && cnt + 1 == argc)
 		? "commit" : "no-commit", dname, vname);
-	memset(&args, 0, sizeof(args));
-	strncpy_null(args.name, vname);
-	res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
-	if(res < 0 ){
-		error("cannot delete '%s/%s': %s", dname, vname,
-			strerror(errno));
+
+	err = btrfs_util_f_delete_subvolume(fd, vname, 0);
+	if (err) {
+		error_btrfs_util(err);
 		ret = 1;
 		goto out;
 	}
-- 
2.16.1


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

* [PATCH 21/26] btrfs-progs: use libbtrfsutil for subvol show
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (19 preceding siblings ...)
  2018-01-26 18:41 ` [PATCH 20/26] btrfs-progs: use libbtrfsutil for subvol delete Omar Sandoval
@ 2018-01-26 18:41 ` Omar Sandoval
  2018-02-02 23:18   ` Hans van Kranenburg
  2018-01-26 18:41 ` [PATCH 22/26] btrfs-progs: use libbtrfsutil for subvol sync Omar Sandoval
                   ` (5 subsequent siblings)
  26 siblings, 1 reply; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:41 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

Now implemented with btrfs_util_subvolume_path(),
btrfs_util_subvolume_info(), and subvolume iterators.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 cmds-subvolume.c | 150 ++++++++++++++++++++++++++++++++++++-------------------
 utils.c          | 118 -------------------------------------------
 utils.h          |   5 --
 3 files changed, 99 insertions(+), 174 deletions(-)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index c5e03011..b969fc88 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -825,19 +825,20 @@ static const char * const cmd_subvol_show_usage[] = {
 
 static int cmd_subvol_show(int argc, char **argv)
 {
-	struct root_info get_ri;
-	struct btrfs_list_filter_set *filter_set = NULL;
 	char tstr[256];
 	char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
 	char *fullpath = NULL;
-	char raw_prefix[] = "\t\t\t\t";
 	int fd = -1;
 	int ret = 1;
 	DIR *dirstream1 = NULL;
 	int by_rootid = 0;
 	int by_uuid = 0;
-	u64 rootid_arg;
+	u64 rootid_arg = 0;
 	u8 uuid_arg[BTRFS_UUID_SIZE];
+	struct btrfs_util_subvolume_iterator *iter;
+	struct btrfs_util_subvolume_info subvol;
+	char *subvol_path = NULL;
+	enum btrfs_util_error err;
 
 	while (1) {
 		int c;
@@ -874,7 +875,6 @@ static int cmd_subvol_show(int argc, char **argv)
 		usage(cmd_subvol_show_usage);
 	}
 
-	memset(&get_ri, 0, sizeof(get_ri));
 	fullpath = realpath(argv[optind], NULL);
 	if (!fullpath) {
 		error("cannot find real path for '%s': %s",
@@ -882,89 +882,137 @@ static int cmd_subvol_show(int argc, char **argv)
 		goto out;
 	}
 
-	if (by_rootid) {
-		ret = get_subvol_info_by_rootid(fullpath, &get_ri, rootid_arg);
-	} else if (by_uuid) {
-		ret = get_subvol_info_by_uuid(fullpath, &get_ri, uuid_arg);
-	} else {
-		ret = get_subvol_info(fullpath, &get_ri);
+	fd = open_file_or_dir(fullpath, &dirstream1);
+	if (fd < 0) {
+		error("can't access '%s'", fullpath);
+		goto out;
 	}
 
-	if (ret) {
-		if (ret < 0) {
-			error("Failed to get subvol info %s: %s",
-					fullpath, strerror(-ret));
-		} else {
-			error("Failed to get subvol info %s: %d",
-					fullpath, ret);
+	if (by_uuid) {
+		err = btrfs_util_f_create_subvolume_iterator(fd,
+							     BTRFS_FS_TREE_OBJECTID,
+							     0, &iter);
+		if (err) {
+			error_btrfs_util(err);
+			goto out;
+		}
+
+		for (;;) {
+			err = btrfs_util_subvolume_iterator_next_info(iter,
+								      &subvol_path,
+								      &subvol);
+			if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
+				uuid_unparse(uuid_arg, uuidparse);
+				error("can't find uuid '%s' on '%s'", uuidparse,
+				      fullpath);
+				btrfs_util_destroy_subvolume_iterator(iter);
+				goto out;
+			} else if (err) {
+				error_btrfs_util(err);
+				btrfs_util_destroy_subvolume_iterator(iter);
+				goto out;
+			}
+
+			if (uuid_compare(subvol.uuid, uuid_arg) == 0)
+				break;
+
+			free(subvol_path);
+		}
+		btrfs_util_destroy_subvolume_iterator(iter);
+	} else {
+		/*
+		 * If !by_rootid, rootid_arg = 0, which means find the
+		 * subvolume ID of the fd and use that.
+		 */
+		err = btrfs_util_f_subvolume_info(fd, rootid_arg,
+						  &subvol);
+		if (err) {
+			error_btrfs_util(err);
+			goto out;
+		}
+
+		err = btrfs_util_f_subvolume_path(fd, subvol.id, &subvol_path);
+		if (err) {
+			error_btrfs_util(err);
+			goto out;
 		}
-		return ret;
+
 	}
 
 	/* print the info */
-	printf("%s\n", get_ri.full_path);
-	printf("\tName: \t\t\t%s\n", get_ri.name);
+	printf("%s\n", subvol.id == BTRFS_FS_TREE_OBJECTID ? "/" : subvol_path);
+	printf("\tName: \t\t\t%s\n",
+	       (subvol.id == BTRFS_FS_TREE_OBJECTID ? "<FS_TREE>" :
+		basename(subvol_path)));
 
-	if (uuid_is_null(get_ri.uuid))
+	if (uuid_is_null(subvol.uuid))
 		strcpy(uuidparse, "-");
 	else
-		uuid_unparse(get_ri.uuid, uuidparse);
+		uuid_unparse(subvol.uuid, uuidparse);
 	printf("\tUUID: \t\t\t%s\n", uuidparse);
 
-	if (uuid_is_null(get_ri.puuid))
+	if (uuid_is_null(subvol.parent_uuid))
 		strcpy(uuidparse, "-");
 	else
-		uuid_unparse(get_ri.puuid, uuidparse);
+		uuid_unparse(subvol.parent_uuid, uuidparse);
 	printf("\tParent UUID: \t\t%s\n", uuidparse);
 
-	if (uuid_is_null(get_ri.ruuid))
+	if (uuid_is_null(subvol.received_uuid))
 		strcpy(uuidparse, "-");
 	else
-		uuid_unparse(get_ri.ruuid, uuidparse);
+		uuid_unparse(subvol.received_uuid, uuidparse);
 	printf("\tReceived UUID: \t\t%s\n", uuidparse);
 
-	if (get_ri.otime) {
+	if (subvol.otime.tv_sec) {
 		struct tm tm;
 
-		localtime_r(&get_ri.otime, &tm);
+		localtime_r(&subvol.otime.tv_sec, &tm);
 		strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
 	} else
 		strcpy(tstr, "-");
 	printf("\tCreation time: \t\t%s\n", tstr);
 
-	printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
-	printf("\tGeneration: \t\t%llu\n", get_ri.gen);
-	printf("\tGen at creation: \t%llu\n", get_ri.ogen);
-	printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
-	printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
+	printf("\tSubvolume ID: \t\t%" PRIu64 "\n", subvol.id);
+	printf("\tGeneration: \t\t%" PRIu64 "\n", subvol.generation);
+	printf("\tGen at creation: \t%" PRIu64 "\n", subvol.otransid);
+	printf("\tParent ID: \t\t%" PRIu64 "\n", subvol.parent_id);
+	printf("\tTop level ID: \t\t%" PRIu64 "\n", subvol.parent_id);
 
-	if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
+	if (subvol.flags & BTRFS_ROOT_SUBVOL_RDONLY)
 		printf("\tFlags: \t\t\treadonly\n");
 	else
 		printf("\tFlags: \t\t\t-\n");
 
 	/* print the snapshots of the given subvol if any*/
 	printf("\tSnapshot(s):\n");
-	filter_set = btrfs_list_alloc_filter_set();
-	btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
-				(u64)(unsigned long)get_ri.uuid);
-	btrfs_list_setup_print_column(BTRFS_LIST_PATH);
 
-	fd = open_file_or_dir(fullpath, &dirstream1);
-	if (fd < 0) {
-		fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
-		goto out;
+	err = btrfs_util_f_create_subvolume_iterator(fd,
+						     BTRFS_FS_TREE_OBJECTID,
+						     0, &iter);
+
+	for (;;) {
+		struct btrfs_util_subvolume_info subvol2;
+		char *path;
+
+		err = btrfs_util_subvolume_iterator_next_info(iter, &path, &subvol2);
+		if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
+			break;
+		} else if (err) {
+			error_btrfs_util(err);
+			btrfs_util_destroy_subvolume_iterator(iter);
+			goto out;
+		}
+
+		if (uuid_compare(subvol2.parent_uuid, subvol.uuid) == 0)
+			printf("\t\t\t\t%s\n", path);
+
+		free(path);
 	}
-	btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
-			1, raw_prefix);
+	btrfs_util_destroy_subvolume_iterator(iter);
 
+	ret = 0;
 out:
-	/* clean up */
-	free(get_ri.path);
-	free(get_ri.name);
-	free(get_ri.full_path);
-	free(filter_set);
-
+	free(subvol_path);
 	close_file_or_dir(fd, dirstream1);
 	free(fullpath);
 	return !!ret;
diff --git a/utils.c b/utils.c
index 524f463d..6a667907 100644
--- a/utils.c
+++ b/utils.c
@@ -2482,124 +2482,6 @@ const char *subvol_strip_mountpoint(const char *mnt, const char *full_path)
 	return full_path + len;
 }
 
-/*
- * Returns
- * <0: Std error
- * 0: All fine
- * 1: Error; and error info printed to the terminal. Fixme.
- * 2: If the fullpath is root tree instead of subvol tree
- */
-int get_subvol_info(const char *fullpath, struct root_info *get_ri)
-{
-	u64 sv_id;
-	int ret = 1;
-	int fd = -1;
-	int mntfd = -1;
-	char *mnt = NULL;
-	const char *svpath = NULL;
-	DIR *dirstream1 = NULL;
-	DIR *dirstream2 = NULL;
-
-	ret = test_issubvolume(fullpath);
-	if (ret < 0)
-		return ret;
-	if (!ret) {
-		error("not a subvolume: %s", fullpath);
-		return 1;
-	}
-
-	ret = find_mount_root(fullpath, &mnt);
-	if (ret < 0)
-		return ret;
-	if (ret > 0) {
-		error("%s doesn't belong to btrfs mount point", fullpath);
-		return 1;
-	}
-	ret = 1;
-	svpath = subvol_strip_mountpoint(mnt, fullpath);
-
-	fd = btrfs_open_dir(fullpath, &dirstream1, 1);
-	if (fd < 0)
-		goto out;
-
-	ret = btrfs_list_get_path_rootid(fd, &sv_id);
-	if (ret)
-		goto out;
-
-	mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
-	if (mntfd < 0)
-		goto out;
-
-	memset(get_ri, 0, sizeof(*get_ri));
-	get_ri->root_id = sv_id;
-
-	if (sv_id == BTRFS_FS_TREE_OBJECTID)
-		ret = btrfs_get_toplevel_subvol(mntfd, get_ri);
-	else
-		ret = btrfs_get_subvol(mntfd, get_ri);
-	if (ret)
-		error("can't find '%s': %d", svpath, ret);
-
-out:
-	close_file_or_dir(mntfd, dirstream2);
-	close_file_or_dir(fd, dirstream1);
-	free(mnt);
-
-	return ret;
-}
-
-int get_subvol_info_by_rootid(const char *mnt, struct root_info *get_ri, u64 r_id)
-{
-	int fd;
-	int ret;
-	DIR *dirstream = NULL;
-
-	fd = btrfs_open_dir(mnt, &dirstream, 1);
-	if (fd < 0)
-		return -EINVAL;
-
-	memset(get_ri, 0, sizeof(*get_ri));
-	get_ri->root_id = r_id;
-
-	if (r_id == BTRFS_FS_TREE_OBJECTID)
-		ret = btrfs_get_toplevel_subvol(fd, get_ri);
-	else
-		ret = btrfs_get_subvol(fd, get_ri);
-
-	if (ret)
-		error("can't find rootid '%llu' on '%s': %d", r_id, mnt, ret);
-
-	close_file_or_dir(fd, dirstream);
-
-	return ret;
-}
-
-int get_subvol_info_by_uuid(const char *mnt, struct root_info *get_ri, u8 *uuid_arg)
-{
-	int fd;
-	int ret;
-	DIR *dirstream = NULL;
-
-	fd = btrfs_open_dir(mnt, &dirstream, 1);
-	if (fd < 0)
-		return -EINVAL;
-
-	memset(get_ri, 0, sizeof(*get_ri));
-	uuid_copy(get_ri->uuid, uuid_arg);
-
-	ret = btrfs_get_subvol(fd, get_ri);
-	if (ret) {
-		char uuid_parsed[BTRFS_UUID_UNPARSED_SIZE];
-		uuid_unparse(uuid_arg, uuid_parsed);
-		error("can't find uuid '%s' on '%s': %d",
-					uuid_parsed, mnt, ret);
-	}
-
-	close_file_or_dir(fd, dirstream);
-
-	return ret;
-}
-
 /* Set the seed manually */
 void init_rand_seed(u64 seed)
 {
diff --git a/utils.h b/utils.h
index a82d46f6..4a95bfb5 100644
--- a/utils.h
+++ b/utils.h
@@ -151,11 +151,6 @@ int test_issubvolume(const char *path);
 int test_isdir(const char *path);
 
 const char *subvol_strip_mountpoint(const char *mnt, const char *full_path);
-int get_subvol_info(const char *fullpath, struct root_info *get_ri);
-int get_subvol_info_by_rootid(const char *mnt, struct root_info *get_ri,
-							u64 rootid_arg);
-int get_subvol_info_by_uuid(const char *mnt, struct root_info *get_ri,
-							u8 *uuid_arg);
 int find_next_key(struct btrfs_path *path, struct btrfs_key *key);
 const char* btrfs_group_type_str(u64 flag);
 const char* btrfs_group_profile_str(u64 flag);
-- 
2.16.1


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

* [PATCH 22/26] btrfs-progs: use libbtrfsutil for subvol sync
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (20 preceding siblings ...)
  2018-01-26 18:41 ` [PATCH 21/26] btrfs-progs: use libbtrfsutil for subvol show Omar Sandoval
@ 2018-01-26 18:41 ` Omar Sandoval
  2018-01-26 18:41 ` [PATCH 23/26] btrfs-progs: replace test_issubvolume() with btrfs_util_is_subvolume() Omar Sandoval
                   ` (4 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:41 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

btrfs_util_f_deleted_subvolumes() replaces enumerate_dead_subvols() and
btrfs_util_f_subvolume_info() replaces is_subvolume_cleaned().

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 cmds-subvolume.c | 217 ++++++-------------------------------------------------
 1 file changed, 21 insertions(+), 196 deletions(-)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index b969fc88..8dbb3e49 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -42,38 +42,11 @@
 #include "utils.h"
 #include "help.h"
 
-static int is_subvolume_cleaned(int fd, u64 subvolid)
+static int wait_for_subvolume_cleaning(int fd, size_t count, uint64_t *ids,
+				       int sleep_interval)
 {
-	int ret;
-	struct btrfs_ioctl_search_args args;
-	struct btrfs_ioctl_search_key *sk = &args.key;
-
-	sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
-	sk->min_objectid = subvolid;
-	sk->max_objectid = subvolid;
-	sk->min_type = BTRFS_ROOT_ITEM_KEY;
-	sk->max_type = BTRFS_ROOT_ITEM_KEY;
-	sk->min_offset = 0;
-	sk->max_offset = (u64)-1;
-	sk->min_transid = 0;
-	sk->max_transid = (u64)-1;
-	sk->nr_items = 1;
-
-	ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
-	if (ret < 0)
-		return -errno;
-
-	if (sk->nr_items == 0)
-		return 1;
-
-	return 0;
-}
-
-static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
-		int sleep_interval)
-{
-	int ret;
-	int i;
+	size_t i;
+	enum btrfs_util_error err;
 
 	while (1) {
 		int clean = 1;
@@ -81,16 +54,14 @@ static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
 		for (i = 0; i < count; i++) {
 			if (!ids[i])
 				continue;
-			ret = is_subvolume_cleaned(fd, ids[i]);
-			if (ret < 0) {
-				error(
-			    "cannot read status of dead subvolume %llu: %s",
-					(unsigned long long)ids[i], strerror(-ret));
-				return ret;
-			}
-			if (ret) {
-				printf("Subvolume id %llu is gone\n", ids[i]);
+			err = btrfs_util_f_subvolume_info(fd, ids[i], NULL);
+			if (err == BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND) {
+				printf("Subvolume id %" PRIu64 " is gone\n",
+				       ids[i]);
 				ids[i] = 0;
+			} else if (err) {
+				error_btrfs_util(err);
+				return -errno;
 			} else {
 				clean = 0;
 			}
@@ -1031,160 +1002,15 @@ static const char * const cmd_subvol_sync_usage[] = {
 	NULL
 };
 
-#if 0
-/*
- * If we're looking for any dead subvolume, take a shortcut and look
- * for any ORPHAN_ITEMs in the tree root
- */
-static int fs_has_dead_subvolumes(int fd)
-{
-	int ret;
-	struct btrfs_ioctl_search_args args;
-	struct btrfs_ioctl_search_key *sk = &args.key;
-	struct btrfs_ioctl_search_header sh;
-	u64 min_subvolid = 0;
-
-again:
-	sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
-	sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
-	sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
-	sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
-	sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
-	sk->min_offset = min_subvolid;
-	sk->max_offset = (u64)-1;
-	sk->min_transid = 0;
-	sk->max_transid = (u64)-1;
-	sk->nr_items = 1;
-
-	ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
-	if (ret < 0)
-		return -errno;
-
-	if (!sk->nr_items)
-		return 0;
-
-	memcpy(&sh, args.buf, sizeof(sh));
-	min_subvolid = sh.offset;
-
-	/*
-	 * Verify that the root item is really there and we haven't hit
-	 * a stale orphan
-	 */
-	sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
-	sk->min_objectid = min_subvolid;
-	sk->max_objectid = min_subvolid;
-	sk->min_type = BTRFS_ROOT_ITEM_KEY;
-	sk->max_type = BTRFS_ROOT_ITEM_KEY;
-	sk->min_offset = 0;
-	sk->max_offset = (u64)-1;
-	sk->min_transid = 0;
-	sk->max_transid = (u64)-1;
-	sk->nr_items = 1;
-
-	ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
-	if (ret < 0)
-		return -errno;
-
-	/*
-	 * Stale orphan, try the next one
-	 */
-	if (!sk->nr_items) {
-		min_subvolid++;
-		goto again;
-	}
-
-	return 1;
-}
-#endif
-
-#define SUBVOL_ID_BATCH		1024
-
-/*
- * Enumerate all dead subvolumes that exist in the filesystem.
- * Fill @ids and reallocate to bigger size if needed.
- */
-static int enumerate_dead_subvols(int fd, u64 **ids)
-{
-	int ret;
-	struct btrfs_ioctl_search_args args;
-	struct btrfs_ioctl_search_key *sk = &args.key;
-	int idx = 0;
-	int count = 0;
-
-	memset(&args, 0, sizeof(args));
-
-	sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
-	sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
-	sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
-	sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
-	sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
-	sk->min_offset = 0;
-	sk->max_offset = (u64)-1;
-	sk->min_transid = 0;
-	sk->max_transid = (u64)-1;
-	sk->nr_items = 4096;
-
-	*ids = NULL;
-	while (1) {
-		struct btrfs_ioctl_search_header *sh;
-		unsigned long off;
-		int i;
-
-		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
-		if (ret < 0)
-			return -errno;
-
-		if (!sk->nr_items)
-			return idx;
-
-		off = 0;
-		for (i = 0; i < sk->nr_items; i++) {
-			sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
-			off += sizeof(*sh);
-
-			if (btrfs_search_header_type(sh)
-			    == BTRFS_ORPHAN_ITEM_KEY) {
-				if (idx >= count) {
-					u64 *newids;
-
-					count += SUBVOL_ID_BATCH;
-					newids = (u64*)realloc(*ids,
-							count * sizeof(u64));
-					if (!newids)
-						return -ENOMEM;
-					*ids = newids;
-				}
-				(*ids)[idx] = btrfs_search_header_offset(sh);
-				idx++;
-			}
-			off += btrfs_search_header_len(sh);
-
-			sk->min_objectid = btrfs_search_header_objectid(sh);
-			sk->min_type = btrfs_search_header_type(sh);
-			sk->min_offset = btrfs_search_header_offset(sh);
-		}
-		if (sk->min_offset < (u64)-1)
-			sk->min_offset++;
-		else
-			break;
-		if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
-			break;
-		if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
-			break;
-	}
-
-	return idx;
-}
-
 static int cmd_subvol_sync(int argc, char **argv)
 {
 	int fd = -1;
-	int i;
 	int ret = 1;
 	DIR *dirstream = NULL;
-	u64 *ids = NULL;
-	int id_count;
+	uint64_t *ids;
+	size_t id_count, i;
 	int sleep_interval = 1;
+	enum btrfs_util_error err;
 
 	while (1) {
 		int c = getopt(argc, argv, "s:");
@@ -1218,10 +1044,9 @@ static int cmd_subvol_sync(int argc, char **argv)
 
 	id_count = argc - optind;
 	if (!id_count) {
-		id_count = enumerate_dead_subvols(fd, &ids);
-		if (id_count < 0) {
-			error("can't enumerate dead subvolumes: %s",
-					strerror(-id_count));
+		err = btrfs_util_f_deleted_subvolumes(fd, &ids, &id_count);
+		if (err) {
+			error_btrfs_util(err);
 			ret = 1;
 			goto out;
 		}
@@ -1230,7 +1055,7 @@ static int cmd_subvol_sync(int argc, char **argv)
 			goto out;
 		}
 	} else {
-		ids = (u64*)malloc(id_count * sizeof(u64));
+		ids = malloc(id_count * sizeof(uint64_t));
 		if (!ids) {
 			error("not enough memory");
 			ret = 1;
@@ -1244,13 +1069,13 @@ static int cmd_subvol_sync(int argc, char **argv)
 			arg = argv[optind + i];
 			errno = 0;
 			id = strtoull(arg, NULL, 10);
-			if (errno < 0) {
+			if (errno) {
 				error("unrecognized subvolume id %s", arg);
 				ret = 1;
 				goto out;
 			}
-			if (id < BTRFS_FIRST_FREE_OBJECTID
-					|| id > BTRFS_LAST_FREE_OBJECTID) {
+			if (id < BTRFS_FIRST_FREE_OBJECTID ||
+			    id > BTRFS_LAST_FREE_OBJECTID) {
 				error("subvolume id %s out of range", arg);
 				ret = 1;
 				goto out;
-- 
2.16.1


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

* [PATCH 23/26] btrfs-progs: replace test_issubvolume() with btrfs_util_is_subvolume()
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (21 preceding siblings ...)
  2018-01-26 18:41 ` [PATCH 22/26] btrfs-progs: use libbtrfsutil for subvol sync Omar Sandoval
@ 2018-01-26 18:41 ` Omar Sandoval
  2018-01-26 18:41 ` [PATCH 24/26] btrfs-progs: add recursive snapshot/delete using libbtrfsutil Omar Sandoval
                   ` (3 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:41 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

This gets the remaining occurrences that weren't covered by previous
conversions.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 cmds-qgroup.c    | 13 ++++++-------
 cmds-subvolume.c | 11 ++++-------
 utils.c          | 34 +++++-----------------------------
 utils.h          |  1 -
 4 files changed, 15 insertions(+), 44 deletions(-)

diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index fac692d4..16ba6f86 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -20,6 +20,8 @@
 #include <unistd.h>
 #include <getopt.h>
 
+#include <btrfsutil.h>
+
 #include "ctree.h"
 #include "ioctl.h"
 
@@ -431,6 +433,7 @@ static int cmd_qgroup_limit(int argc, char **argv)
 	int compressed = 0;
 	int exclusive = 0;
 	DIR *dirstream = NULL;
+	enum btrfs_util_error err;
 
 	while (1) {
 		int c = getopt(argc, argv, "ce");
@@ -471,13 +474,9 @@ static int cmd_qgroup_limit(int argc, char **argv)
 	if (argc - optind == 2) {
 		args.qgroupid = 0;
 		path = argv[optind + 1];
-		ret = test_issubvolume(path);
-		if (ret < 0) {
-			error("cannot access '%s': %s", path, strerror(-ret));
-			return 1;
-		}
-		if (!ret) {
-			error("'%s' is not a subvolume", path);
+		err = btrfs_util_is_subvolume(path);
+		if (err) {
+			error_btrfs_util(err);
 			return 1;
 		}
 		/*
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 8dbb3e49..5c75799c 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -748,6 +748,7 @@ static int cmd_subvol_find_new(int argc, char **argv)
 	char *subvol;
 	u64 last_gen;
 	DIR *dirstream = NULL;
+	enum btrfs_util_error err;
 
 	clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
 
@@ -757,13 +758,9 @@ static int cmd_subvol_find_new(int argc, char **argv)
 	subvol = argv[optind];
 	last_gen = arg_strtou64(argv[optind + 1]);
 
-	ret = test_issubvolume(subvol);
-	if (ret < 0) {
-		error("cannot access subvolume %s: %s", subvol, strerror(-ret));
-		return 1;
-	}
-	if (!ret) {
-		error("not a subvolume: %s", subvol);
+	err = btrfs_util_is_subvolume(subvol);
+	if (err) {
+		error_btrfs_util(err);
 		return 1;
 	}
 
diff --git a/utils.c b/utils.c
index 6a667907..bdb389cf 100644
--- a/utils.c
+++ b/utils.c
@@ -40,6 +40,8 @@
 #include <linux/magic.h>
 #include <getopt.h>
 
+#include <btrfsutil.h>
+
 #include "kerncompat.h"
 #include "radix-tree.h"
 #include "ctree.h"
@@ -1451,6 +1453,7 @@ u64 parse_qgroupid(const char *p)
 	char *s = strchr(p, '/');
 	const char *ptr_src_end = p + strlen(p);
 	char *ptr_parse_end = NULL;
+	enum btrfs_util_error err;
 	u64 level;
 	u64 id;
 	int fd;
@@ -1478,8 +1481,8 @@ u64 parse_qgroupid(const char *p)
 
 path:
 	/* Path format like subv at 'my_subvol' is the fallback case */
-	ret = test_issubvolume(p);
-	if (ret < 0 || !ret)
+	err = btrfs_util_is_subvolume(p);
+	if (err)
 		goto err;
 	fd = open(p, O_RDONLY);
 	if (fd < 0)
@@ -2443,33 +2446,6 @@ int test_issubvolname(const char *name)
 		strcmp(name, ".") && strcmp(name, "..");
 }
 
-/*
- * Test if path is a subvolume
- * Returns:
- *   0 - path exists but it is not a subvolume
- *   1 - path exists and it is  a subvolume
- * < 0 - error
- */
-int test_issubvolume(const char *path)
-{
-	struct stat	st;
-	struct statfs stfs;
-	int		res;
-
-	res = stat(path, &st);
-	if (res < 0)
-		return -errno;
-
-	if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode))
-		return 0;
-
-	res = statfs(path, &stfs);
-	if (res < 0)
-		return -errno;
-
-	return (int)stfs.f_type == BTRFS_SUPER_MAGIC;
-}
-
 const char *subvol_strip_mountpoint(const char *mnt, const char *full_path)
 {
 	int len = strlen(mnt);
diff --git a/utils.h b/utils.h
index 4a95bfb5..4bfbf63e 100644
--- a/utils.h
+++ b/utils.h
@@ -147,7 +147,6 @@ u64 disk_size(const char *path);
 u64 get_partition_size(const char *dev);
 
 int test_issubvolname(const char *name);
-int test_issubvolume(const char *path);
 int test_isdir(const char *path);
 
 const char *subvol_strip_mountpoint(const char *mnt, const char *full_path);
-- 
2.16.1


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

* [PATCH 24/26] btrfs-progs: add recursive snapshot/delete using libbtrfsutil
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (22 preceding siblings ...)
  2018-01-26 18:41 ` [PATCH 23/26] btrfs-progs: replace test_issubvolume() with btrfs_util_is_subvolume() Omar Sandoval
@ 2018-01-26 18:41 ` Omar Sandoval
  2018-01-26 18:41 ` [PATCH 25/26] btrfs-progs: deprecate libbtrfs helpers with libbtrfsutil equivalents Omar Sandoval
                   ` (2 subsequent siblings)
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:41 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

And update the documentation.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 Documentation/btrfs-subvolume.asciidoc | 14 ++++++++++++--
 cmds-subvolume.c                       | 25 +++++++++++++++++++++----
 2 files changed, 33 insertions(+), 6 deletions(-)

diff --git a/Documentation/btrfs-subvolume.asciidoc b/Documentation/btrfs-subvolume.asciidoc
index a8c4af4b..7a2ec8d2 100644
--- a/Documentation/btrfs-subvolume.asciidoc
+++ b/Documentation/btrfs-subvolume.asciidoc
@@ -81,6 +81,12 @@ wait for transaction commit at the end of the operation
 +
 -C|--commit-each::::
 wait for transaction commit after deleting each subvolume
++
+-R|--recursive::::
+delete subvolumes beneath each subvolume recursively
++
+-v|--verbose::::
+output more verbosely
 
 *find-new* <subvolume> <last_gen>::
 List the recently modified files in a subvolume, after <last_gen> ID.
@@ -157,7 +163,7 @@ The id can be obtained from *btrfs subvolume list*, *btrfs subvolume show* or
 *show* <path>::
 Show information of a given subvolume in the <path>.
 
-*snapshot* [-r] <source> <dest>|[<dest>/]<name>::
+*snapshot* [-r|-R] <source> <dest>|[<dest>/]<name>::
 Create a snapshot of the subvolume <source> with the
 name <name> in the <dest> directory.
 +
@@ -167,7 +173,11 @@ If <source> is not a subvolume, btrfs returns an error.
 `Options`
 +
 -r::::
-Make the new snapshot read only.
+make the new snapshot read-only
++
+-R::::
+recursively snapshot subvolumes beneath the source; this option cannot be
+combined with -r
 
 *sync* <path> [subvolid...]::
 Wait until given subvolume(s) are completely removed from the filesystem after
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 5c75799c..3521e7b7 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -187,6 +187,7 @@ static const char * const cmd_subvol_delete_usage[] = {
 	"",
 	"-c|--commit-after      wait for transaction commit at the end of the operation",
 	"-C|--commit-each       wait for transaction commit after deleting each subvolume",
+	"-R|--recursive         delete subvolumes beneath each subvolume recursively",
 	"-v|--verbose           verbose output of operations",
 	NULL
 };
@@ -203,6 +204,7 @@ static int cmd_subvol_delete(int argc, char **argv)
 	DIR	*dirstream = NULL;
 	int verbose = 0;
 	int commit_mode = 0;
+	int flags = 0;
 	u8 fsid[BTRFS_FSID_SIZE];
 	char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
 	struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = { NULL, };
@@ -214,11 +216,12 @@ static int cmd_subvol_delete(int argc, char **argv)
 		static const struct option long_options[] = {
 			{"commit-after", no_argument, NULL, 'c'},
 			{"commit-each", no_argument, NULL, 'C'},
+			{"recursive", no_argument, NULL, 'R'},
 			{"verbose", no_argument, NULL, 'v'},
 			{NULL, 0, NULL, 0}
 		};
 
-		c = getopt_long(argc, argv, "cCv", long_options, NULL);
+		c = getopt_long(argc, argv, "cCRv", long_options, NULL);
 		if (c < 0)
 			break;
 
@@ -229,6 +232,9 @@ static int cmd_subvol_delete(int argc, char **argv)
 		case 'C':
 			commit_mode = COMMIT_EACH;
 			break;
+		case 'R':
+			flags |= BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE;
+			break;
 		case 'v':
 			verbose++;
 			break;
@@ -281,7 +287,7 @@ again:
 		commit_mode == COMMIT_EACH || (commit_mode == COMMIT_AFTER && cnt + 1 == argc)
 		? "commit" : "no-commit", dname, vname);
 
-	err = btrfs_util_f_delete_subvolume(fd, vname, 0);
+	err = btrfs_util_f_delete_subvolume(fd, vname, flags);
 	if (err) {
 		error_btrfs_util(err);
 		ret = 1;
@@ -571,13 +577,15 @@ out:
 }
 
 static const char * const cmd_subvol_snapshot_usage[] = {
-	"btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
+	"btrfs subvolume snapshot [-r|-R] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
 	"Create a snapshot of the subvolume",
 	"Create a writable/readonly snapshot of the subvolume <source> with",
 	"the name <name> in the <dest> directory.  If only <dest> is given,",
 	"the subvolume will be named the basename of <source>.",
 	"",
 	"-r             create a readonly snapshot",
+	"-R             recursively snapshot subvolumes beneath the source; this",
+	"               option cannot be combined with -r",
 	"-i <qgroupid>  add the newly created snapshot to a qgroup. This",
 	"               option can be given multiple times.",
 	NULL
@@ -591,7 +599,7 @@ static int cmd_subvol_snapshot(int argc, char **argv)
 	int retval = 1;
 
 	while (1) {
-		int c = getopt(argc, argv, "i:r");
+		int c = getopt(argc, argv, "i:rR");
 		if (c < 0)
 			break;
 
@@ -603,11 +611,20 @@ static int cmd_subvol_snapshot(int argc, char **argv)
 		case 'r':
 			flags |= BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY;
 			break;
+		case 'R':
+			flags |= BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE;
+			break;
 		default:
 			usage(cmd_subvol_snapshot_usage);
 		}
 	}
 
+	if ((flags & BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY) &&
+	    (flags & BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE)) {
+		error("-r and -R cannot be combined");
+		return 1;
+	}
+
 	if (check_argc_exact(argc - optind, 2))
 		usage(cmd_subvol_snapshot_usage);
 
-- 
2.16.1


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

* [PATCH 25/26] btrfs-progs: deprecate libbtrfs helpers with libbtrfsutil equivalents
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (23 preceding siblings ...)
  2018-01-26 18:41 ` [PATCH 24/26] btrfs-progs: add recursive snapshot/delete using libbtrfsutil Omar Sandoval
@ 2018-01-26 18:41 ` Omar Sandoval
  2018-02-02 14:49   ` David Sterba
  2018-01-26 18:41 ` [PATCH 26/26] btrfs-progs: use libbtrfsutil for subvolume list Omar Sandoval
  2018-01-26 18:51 ` [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Hugo Mills
  26 siblings, 1 reply; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:41 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

The old libbtrfs defines some helpers which do the same thing as some
libbtrfsutil helpers. Reimplement the libbtrfs helpers in terms of the
libbtrfsutil APIs and mark the libbtrfs versions as deprecated, which we
could ideally get rid of eventually.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 Makefile         |   4 +-
 btrfs-list.c     | 252 +++++++++++++++++++++----------------------------------
 btrfs-list.h     |  15 ++--
 cmds-inspect.c   |   9 +-
 cmds-receive.c   |  12 ++-
 cmds-subvolume.c |  10 ++-
 send-utils.c     |  25 ++++--
 7 files changed, 146 insertions(+), 181 deletions(-)

diff --git a/Makefile b/Makefile
index 8bbe1c5e..b37d1ae9 100644
--- a/Makefile
+++ b/Makefile
@@ -372,10 +372,10 @@ kernel-lib/tables.c:
 	@echo "    [TABLE]  $@"
 	$(Q)./mktables > $@ || ($(RM) -f $@ && exit 1)
 
-libbtrfs.so.0.1: $(libbtrfs_objects)
+libbtrfs.so.0.1: $(libbtrfs_objects) libbtrfsutil.so
 	@echo "    [LD]     $@"
 	$(Q)$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LIBBTRFS_LIBS) \
-		-shared -Wl,-soname,libbtrfs.so.0 -o $@
+		-shared -Wl,-soname,libbtrfs.so.0 -o $@ -L. -lbtrfsutil
 
 libbtrfs.a: $(libbtrfs_objects)
 	@echo "    [AR]     $@"
diff --git a/btrfs-list.c b/btrfs-list.c
index b6d76585..267aef98 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -34,6 +34,8 @@
 #include "btrfs-list.h"
 #include "rbtree-utils.h"
 
+#include <btrfsutil.h>
+
 #define BTRFS_LIST_NFILTERS_INCREASE	(2 * BTRFS_LIST_FILTER_MAX)
 #define BTRFS_LIST_NCOMPS_INCREASE	(2 * BTRFS_LIST_COMP_MAX)
 
@@ -907,55 +909,14 @@ build:
 
 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;
-	struct btrfs_ioctl_search_header *sh;
-	u64 found = 0;
-	int ret;
-
-	memset(&args, 0, sizeof(args));
-
-	/*
-	 * search for a dir item with a name 'default' in the tree of
-	 * tree roots, it should point us to a default root
-	 */
-	sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
-
-	/* don't worry about ancient format and request only one item */
-	sk->nr_items = 1;
-
-	sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
-	sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
-	sk->max_type = BTRFS_DIR_ITEM_KEY;
-	sk->min_type = BTRFS_DIR_ITEM_KEY;
-	sk->max_offset = (u64)-1;
-	sk->max_transid = (u64)-1;
-
-	ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
-	if (ret < 0)
-		return ret;
-
-	/* the ioctl returns the number of items it found in nr_items */
-	if (sk->nr_items == 0)
-		goto out;
+	enum btrfs_util_error err;
+	uint64_t id;
 
-	sh = (struct btrfs_ioctl_search_header *)args.buf;
+	err = btrfs_util_f_get_default_subvolume(fd, &id);
+	if (err)
+		return -1;
 
-	if (btrfs_search_header_type(sh) == BTRFS_DIR_ITEM_KEY) {
-		struct btrfs_dir_item *di;
-		int name_len;
-		char *name;
-
-		di = (struct btrfs_dir_item *)(sh + 1);
-		name_len = btrfs_stack_dir_name_len(di);
-		name = (char *)(di + 1);
-
-		if (!strncmp("default", name, name_len))
-			found = btrfs_disk_key_objectid(&di->location);
-	}
-
-out:
-	*default_id = found;
+	*default_id = id;
 	return 0;
 }
 
@@ -1518,10 +1479,11 @@ int btrfs_list_subvols_print(int fd, struct btrfs_list_filter_set *filter_set,
 	int ret = 0;
 	u64 top_id = 0;
 
-	if (full_path)
-		ret = btrfs_list_get_path_rootid(fd, &top_id);
-	if (ret)
-		return ret;
+	/*
+	 * full_path hasn't done anything since 4f5ebb3ef553 ("Btrfs-progs: fix
+	 * to make list specified directory's subvolumes work"). See
+	 * https://www.spinics.net/lists/linux-btrfs/msg69820.html
+	 */
 
 	ret = btrfs_list_subvols(fd, &root_lookup);
 	if (ret)
@@ -1535,83 +1497,84 @@ int btrfs_list_subvols_print(int fd, struct btrfs_list_filter_set *filter_set,
 	return 0;
 }
 
-static char *strdup_or_null(const char *s)
+static void ri_from_subvolume_info(struct root_info *ri,
+				   const struct btrfs_util_subvolume_info *subvol)
 {
-	if (!s)
-		return NULL;
-	return strdup(s);
+	ri->root_id = subvol->id;
+	/*
+	 * struct btrfs_util_subvolume_info doesn't have the root item key
+	 * offset, but it is equal to otransid for snapshots and zero otherwise.
+	 */
+	if (uuid_is_null(subvol->parent_uuid))
+		ri->root_offset = 0;
+	else
+		ri->root_offset = subvol->otransid;
+	ri->flags = subvol->flags;
+	ri->ref_tree = subvol->parent_id;
+	ri->dir_id = subvol->dir_id;
+	ri->top_id = subvol->parent_id;
+	ri->gen = subvol->generation;
+	ri->ogen = subvol->otransid;
+	ri->otime = subvol->otime.tv_sec;
+	memcpy(ri->uuid, subvol->uuid, BTRFS_UUID_SIZE);
+	memcpy(ri->puuid, subvol->parent_uuid, BTRFS_UUID_SIZE);
+	memcpy(ri->ruuid, subvol->received_uuid, BTRFS_UUID_SIZE);
+	ri->deleted = 0;
 }
 
-int btrfs_get_toplevel_subvol(int fd, struct root_info *the_ri)
+int btrfs_get_toplevel_subvol(int fd, struct root_info *ri)
 {
-	int ret;
-	struct root_lookup rl;
-	struct rb_node *rbn;
-	struct root_info *ri;
-	u64 root_id;
+	struct btrfs_util_subvolume_info subvol;
+	enum btrfs_util_error err;
 
-	ret = btrfs_list_get_path_rootid(fd, &root_id);
-	if (ret)
-		return ret;
-
-	ret = btrfs_list_subvols(fd, &rl);
-	if (ret)
-		return ret;
-
-	rbn = rb_first(&rl.root);
-	ri = rb_entry(rbn, struct root_info, rb_node);
+	err = btrfs_util_f_subvolume_info(fd, BTRFS_FS_TREE_OBJECTID, &subvol);
+	if (err)
+		return -1;
 
-	if (ri->root_id != BTRFS_FS_TREE_OBJECTID)
-		return -ENOENT;
+	ri_from_subvolume_info(ri, &subvol);
 
-	memcpy(the_ri, ri, offsetof(struct root_info, path));
-	the_ri->path = strdup_or_null("/");
-	the_ri->name = strdup_or_null("<FS_TREE>");
-	the_ri->full_path = strdup_or_null("/");
-	rb_free_nodes(&rl.root, free_root_info);
+	ri->path = strdup("/");
+	ri->name = strdup("<FS_TREE>");
+	ri->full_path = strdup("/");
+	if (!ri->path || !ri->name || !ri->full_path) {
+		free(ri->path);
+		free(ri->name);
+		free(ri->full_path);
+		return -1;
+	}
 
-	return ret;
+	return 0;
 }
 
-int btrfs_get_subvol(int fd, struct root_info *the_ri)
+int btrfs_get_subvol(int fd, struct root_info *ri)
 {
-	int ret, rr;
-	struct root_lookup rl;
-	struct rb_node *rbn;
-	struct root_info *ri;
-	u64 root_id;
-
-	ret = btrfs_list_get_path_rootid(fd, &root_id);
-	if (ret)
-		return ret;
-
-	ret = btrfs_list_subvols(fd, &rl);
-	if (ret)
-		return ret;
-
-	rbn = rb_first(&rl.root);
-	while(rbn) {
-		ri = rb_entry(rbn, struct root_info, rb_node);
-		rr = resolve_root(&rl, ri, root_id);
-		if (rr == -ENOENT) {
-			ret = -ENOENT;
-			rbn = rb_next(rbn);
-			continue;
-		}
-
-		if (!comp_entry_with_rootid(the_ri, ri, 0) ||
-			!uuid_compare(the_ri->uuid, ri->uuid)) {
-			memcpy(the_ri, ri, offsetof(struct root_info, path));
-			the_ri->path = strdup_or_null(ri->path);
-			the_ri->name = strdup_or_null(ri->name);
-			the_ri->full_path = strdup_or_null(ri->full_path);
-			ret = 0;
-			break;
-		}
-		rbn = rb_next(rbn);
+	struct btrfs_util_subvolume_info subvol;
+	enum btrfs_util_error err;
+	char *slash;
+
+	err = btrfs_util_f_subvolume_info(fd, 0, &subvol);
+	if (err)
+		return -1;
+
+	ri_from_subvolume_info(ri, &subvol);
+
+	err = btrfs_util_f_subvolume_path(fd, 0, &ri->path);
+	if (err)
+		return -1;
+	slash = strrchr(ri->path, '/');
+	if (slash)
+		ri->name = strdup(slash + 1);
+	else
+		ri->name = strdup(ri->path);
+	ri->full_path = strdup(ri->path);
+	if (!!ri->name || !ri->full_path) {
+		free(ri->path);
+		free(ri->name);
+		free(ri->full_path);
+		return -1;
 	}
-	rb_free_nodes(&rl.root, free_root_info);
-	return ret;
+
+	return 0;
 }
 
 static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh,
@@ -1793,44 +1756,14 @@ int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen)
 
 char *btrfs_list_path_for_root(int fd, u64 root)
 {
-	struct root_lookup root_lookup;
-	struct rb_node *n;
-	char *ret_path = NULL;
-	int ret;
-	u64 top_id;
-
-	ret = btrfs_list_get_path_rootid(fd, &top_id);
-	if (ret)
-		return ERR_PTR(ret);
-
-	ret = list_subvol_search(fd, &root_lookup);
-	if (ret < 0)
-		return ERR_PTR(ret);
-
-	ret = list_subvol_fill_paths(fd, &root_lookup);
-	if (ret < 0)
-		return ERR_PTR(ret);
+	enum btrfs_util_error err;
+	char *ret;
 
-	n = rb_last(&root_lookup.root);
-	while (n) {
-		struct root_info *entry;
+	err = btrfs_util_f_subvolume_path(fd, root, &ret);
+	if (err)
+		return ERR_PTR(-errno);
 
-		entry = rb_entry(n, struct root_info, rb_node);
-		ret = resolve_root(&root_lookup, entry, top_id);
-		if (ret == -ENOENT && entry->root_id == root) {
-			ret_path = NULL;
-			break;
-		}
-		if (entry->root_id == root) {
-			ret_path = entry->full_path;
-			entry->full_path = NULL;
-		}
-
-		n = rb_prev(n);
-	}
-	rb_free_nodes(&root_lookup.root, free_root_info);
-
-	return ret_path;
+	return ret;
 }
 
 int btrfs_list_parse_sort_string(char *opt_arg,
@@ -1922,12 +1855,15 @@ int btrfs_list_parse_filter_string(char *opt_arg,
 
 int btrfs_list_get_path_rootid(int fd, u64 *treeid)
 {
-	int ret;
+	enum btrfs_util_error err;
+	uint64_t id;
 
-	ret = lookup_path_rootid(fd, treeid);
-	if (ret < 0)
-		error("cannot resolve rootid for path: %s",
-			strerror(errno));
+	err = btrfs_util_f_subvolume_id(fd, &id);
+	if (err) {
+		error_btrfs_util(err);
+		return -1;
+	}
 
-	return ret;
+	*treeid = id;
+	return 0;
 }
diff --git a/btrfs-list.h b/btrfs-list.h
index 6e5fc778..317b8cf7 100644
--- a/btrfs-list.h
+++ b/btrfs-list.h
@@ -171,10 +171,15 @@ int btrfs_list_subvols_print(int fd, struct btrfs_list_filter_set *filter_set,
 		       enum btrfs_list_layout layot, int full_path,
 		       const char *raw_prefix);
 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);
-int btrfs_list_get_path_rootid(int fd, u64 *treeid);
-int btrfs_get_subvol(int fd, struct root_info *the_ri);
-int btrfs_get_toplevel_subvol(int fd, struct root_info *the_ri);
+int btrfs_list_get_default_subvolume(int fd, u64 *default_id)
+	__attribute__((deprecated("use btrfs_util_f_get_default_subvolume()")));
+char *btrfs_list_path_for_root(int fd, u64 root)
+	__attribute__((deprecated("use btrfs_util_f_subvolume_path()")));
+int btrfs_list_get_path_rootid(int fd, u64 *treeid)
+	__attribute__((deprecated("use btrfs_util_f_subvolume_id()")));
+int btrfs_get_subvol(int fd, struct root_info *the_ri)
+	__attribute__((deprecated("use btrfs_util_f_subvolume_info() and btrfs_util_f_subvolume_path()")));
+int btrfs_get_toplevel_subvol(int fd, struct root_info *the_ri)
+	__attribute__((deprecated("use btrfs_util_f_subvolume_info() and btrfs_util_f_subvolume_path()")));
 
 #endif
diff --git a/cmds-inspect.c b/cmds-inspect.c
index 885f3abe..40ddd0d7 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -23,6 +23,8 @@
 #include <getopt.h>
 #include <limits.h>
 
+#include <btrfsutil.h>
+
 #include "kerncompat.h"
 #include "ioctl.h"
 #include "utils.h"
@@ -147,6 +149,7 @@ static int cmd_inspect_logical_resolve(int argc, char **argv)
 	char full_path[PATH_MAX];
 	char *path_ptr;
 	DIR *dirstream = NULL;
+	enum btrfs_util_error err;
 
 	while (1) {
 		int c = getopt(argc, argv, "Pvs:");
@@ -219,9 +222,9 @@ static int cmd_inspect_logical_resolve(int argc, char **argv)
 		DIR *dirs = NULL;
 
 		if (getpath) {
-			name = btrfs_list_path_for_root(fd, root);
-			if (IS_ERR(name)) {
-				ret = PTR_ERR(name);
+			err = btrfs_util_f_subvolume_path(fd, root, &name);
+			if (err) {
+				ret = -errno;
 				goto out;
 			}
 			if (!name) {
diff --git a/cmds-receive.c b/cmds-receive.c
index e584cef0..f8f65ec5 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -39,6 +39,8 @@
 #include <sys/xattr.h>
 #include <uuid/uuid.h>
 
+#include <btrfsutil.h>
+
 #include "ctree.h"
 #include "ioctl.h"
 #include "commands.h"
@@ -1086,12 +1088,13 @@ static struct btrfs_send_ops send_ops = {
 static int do_receive(struct btrfs_receive *rctx, const char *tomnt,
 		      char *realmnt, int r_fd, u64 max_errors)
 {
-	u64 subvol_id;
+	uint64_t subvol_id;
 	int ret;
 	char *dest_dir_full_path;
 	char root_subvol_path[PATH_MAX];
 	int end = 0;
 	int iterations = 0;
+	enum btrfs_util_error err;
 
 	dest_dir_full_path = realpath(tomnt, NULL);
 	if (!dest_dir_full_path) {
@@ -1136,9 +1139,12 @@ static int do_receive(struct btrfs_receive *rctx, const char *tomnt,
 	 * subvolume we're sitting in so that we can adjust the paths of any
 	 * subvols we want to receive in.
 	 */
-	ret = btrfs_list_get_path_rootid(rctx->mnt_fd, &subvol_id);
-	if (ret)
+	err = btrfs_util_f_subvolume_id(rctx->mnt_fd, &subvol_id);
+	if (err) {
+		error_btrfs_util(err);
+		ret = -1;
 		goto out;
+	}
 
 	root_subvol_path[0] = 0;
 	ret = btrfs_subvolid_resolve(rctx->mnt_fd, root_subvol_path,
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 3521e7b7..7788ed9e 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -424,13 +424,14 @@ static int cmd_subvol_list(int argc, char **argv)
 	struct btrfs_list_comparer_set *comparer_set;
 	u64 flags = 0;
 	int fd = -1;
-	u64 top_id;
+	uint64_t top_id;
 	int ret = -1, uerr = 0;
 	char *subvol;
 	int is_list_all = 0;
 	int is_only_in_path = 0;
 	DIR *dirstream = NULL;
 	enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT;
+	enum btrfs_util_error err;
 
 	filter_set = btrfs_list_alloc_filter_set();
 	comparer_set = btrfs_list_alloc_comparer_set();
@@ -543,9 +544,12 @@ static int cmd_subvol_list(int argc, char **argv)
 		btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
 					flags);
 
-	ret = btrfs_list_get_path_rootid(fd, &top_id);
-	if (ret)
+	err = btrfs_util_f_subvolume_id(fd, &top_id);
+	if (err) {
+		error_btrfs_util(err);
+		ret = 1;
 		goto out;
+	}
 
 	if (is_list_all)
 		btrfs_list_setup_filter(&filter_set,
diff --git a/send-utils.c b/send-utils.c
index 384cc5b7..7913c501 100644
--- a/send-utils.c
+++ b/send-utils.c
@@ -23,6 +23,8 @@
 #include <limits.h>
 #include <errno.h>
 
+#include <btrfsutil.h>
+
 #include "ctree.h"
 #include "send-utils.h"
 #include "ioctl.h"
@@ -36,6 +38,8 @@ static int btrfs_get_root_id_by_sub_path(int mnt_fd, const char *sub_path,
 {
 	int ret;
 	int subvol_fd;
+	uint64_t id;
+	enum btrfs_util_error err;
 
 	subvol_fd = openat(mnt_fd, sub_path, O_RDONLY);
 	if (subvol_fd < 0) {
@@ -45,11 +49,18 @@ static int btrfs_get_root_id_by_sub_path(int mnt_fd, const char *sub_path,
 		return ret;
 	}
 
-	ret = btrfs_list_get_path_rootid(subvol_fd, root_id);
+	err = btrfs_util_f_subvolume_id(subvol_fd, &id);
+	if (err) {
+		ret = -errno;
+	} else {
+		*root_id = id;
+		ret = 0;
+	}
 	close(subvol_fd);
 	return ret;
 }
 
+
 static int btrfs_read_root_item_raw(int mnt_fd, u64 root_id, size_t buf_len,
 				    u32 *read_len, void *buf)
 {
@@ -575,6 +586,7 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
 	unsigned long off = 0;
 	int i;
 	char *path;
+	enum btrfs_util_error err;
 
 	s->mnt_fd = mnt_fd;
 
@@ -649,12 +661,11 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
 				if (!root_item_valid)
 					goto skip;
 
-				path = btrfs_list_path_for_root(mnt_fd,
-					btrfs_search_header_objectid(sh));
-				if (!path)
-					path = strdup("");
-				if (IS_ERR(path)) {
-					ret = PTR_ERR(path);
+				err = btrfs_util_f_subvolume_path(mnt_fd,
+								  btrfs_search_header_objectid(sh),
+								  &path);
+				if (err) {
+					ret = -errno;
 					fprintf(stderr, "ERROR: unable to "
 							"resolve path "
 							"for root %llu\n",
-- 
2.16.1


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

* [PATCH 26/26] btrfs-progs: use libbtrfsutil for subvolume list
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (24 preceding siblings ...)
  2018-01-26 18:41 ` [PATCH 25/26] btrfs-progs: deprecate libbtrfs helpers with libbtrfsutil equivalents Omar Sandoval
@ 2018-01-26 18:41 ` Omar Sandoval
  2018-01-26 18:51 ` [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Hugo Mills
  26 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 18:41 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

This is the most involved conversion because it replaces the libbtrfs
implementation entirely. I took care to preserve the existing behavior
in all cases, warts and all.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 btrfs-list.c | 953 +++++++++++++++++------------------------------------------
 btrfs-list.h |   7 +-
 2 files changed, 268 insertions(+), 692 deletions(-)

diff --git a/btrfs-list.c b/btrfs-list.c
index 267aef98..d9aef8fc 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -16,6 +16,7 @@
  * Boston, MA 021110-1307, USA.
  */
 
+#include <inttypes.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
 #include <stdio.h>
@@ -32,18 +33,20 @@
 #include "ioctl.h"
 #include <uuid/uuid.h>
 #include "btrfs-list.h"
-#include "rbtree-utils.h"
 
 #include <btrfsutil.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.
- */
-struct root_lookup {
-	struct rb_root root;
+struct listed_subvol {
+	struct btrfs_util_subvolume_info info;
+	char *path;
+};
+
+struct subvol_list {
+	size_t num;
+	struct listed_subvol subvols[];
 };
 
 static struct {
@@ -126,15 +129,15 @@ void btrfs_list_setup_print_column(enum btrfs_list_column_enum column)
 		btrfs_list_columns[i].need_print = 1;
 }
 
-static int comp_entry_with_rootid(struct root_info *entry1,
-				  struct root_info *entry2,
+static int comp_entry_with_rootid(const struct listed_subvol *entry1,
+				  const struct listed_subvol *entry2,
 				  int is_descending)
 {
 	int ret;
 
-	if (entry1->root_id > entry2->root_id)
+	if (entry1->info.id > entry2->info.id)
 		ret = 1;
-	else if (entry1->root_id < entry2->root_id)
+	else if (entry1->info.id < entry2->info.id)
 		ret = -1;
 	else
 		ret = 0;
@@ -142,15 +145,15 @@ static int comp_entry_with_rootid(struct root_info *entry1,
 	return is_descending ? -ret : ret;
 }
 
-static int comp_entry_with_gen(struct root_info *entry1,
-			       struct root_info *entry2,
+static int comp_entry_with_gen(const struct listed_subvol *entry1,
+			       const struct listed_subvol *entry2,
 			       int is_descending)
 {
 	int ret;
 
-	if (entry1->gen > entry2->gen)
+	if (entry1->info.generation > entry2->info.generation)
 		ret = 1;
-	else if (entry1->gen < entry2->gen)
+	else if (entry1->info.generation < entry2->info.generation)
 		ret = -1;
 	else
 		ret = 0;
@@ -158,15 +161,15 @@ static int comp_entry_with_gen(struct root_info *entry1,
 	return is_descending ? -ret : ret;
 }
 
-static int comp_entry_with_ogen(struct root_info *entry1,
-				struct root_info *entry2,
+static int comp_entry_with_ogen(const struct listed_subvol *entry1,
+				const struct listed_subvol *entry2,
 				int is_descending)
 {
 	int ret;
 
-	if (entry1->ogen > entry2->ogen)
+	if (entry1->info.otransid > entry2->info.otransid)
 		ret = 1;
-	else if (entry1->ogen < entry2->ogen)
+	else if (entry1->info.otransid < entry2->info.otransid)
 		ret = -1;
 	else
 		ret = 0;
@@ -174,15 +177,15 @@ static int comp_entry_with_ogen(struct root_info *entry1,
 	return is_descending ? -ret : ret;
 }
 
-static int comp_entry_with_path(struct root_info *entry1,
-				struct root_info *entry2,
+static int comp_entry_with_path(const struct listed_subvol *entry1,
+				const struct listed_subvol *entry2,
 				int is_descending)
 {
 	int ret;
 
-	if (strcmp(entry1->full_path, entry2->full_path) > 0)
+	if (strcmp(entry1->path, entry2->path) > 0)
 		ret = 1;
-	else if (strcmp(entry1->full_path, entry2->full_path) < 0)
+	else if (strcmp(entry1->path, entry2->path) < 0)
 		ret = -1;
 	else
 		ret = 0;
@@ -272,16 +275,14 @@ static int btrfs_list_setup_comparer(struct btrfs_list_comparer_set **comp_set,
 	return 0;
 }
 
-static int sort_comp(struct root_info *entry1, struct root_info *entry2,
-		     struct btrfs_list_comparer_set *set)
+static int subvol_comp(const void *entry1, const void *entry2, void *arg)
 {
+	const struct btrfs_list_comparer_set * const set = arg;
 	int rootid_compared = 0;
-	int i, ret = 0;
-
-	if (!set || !set->ncomps)
-		return comp_entry_with_rootid(entry1, entry2, 0);
+	int ret;
+	int i;
 
-	for (i = 0; i < set->ncomps; i++) {
+	for (i = 0; set && i < set->ncomps; i++) {
 		if (!set->comps[i].comp_func)
 			break;
 
@@ -295,383 +296,16 @@ static int sort_comp(struct root_info *entry1, struct root_info *entry2,
 	}
 
 	if (!rootid_compared)
-		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;
-}
-
-/*
- * insert a new root into the tree.  returns the existing root entry
- * if one is already there.  Both root_id and ref_tree are used
- * as the key
- */
-static int root_tree_insert(struct root_lookup *root_tree,
-			    struct root_info *ins)
-{
-	struct rb_node **p = &root_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, rb_node);
-
-		ret = comp_entry_with_rootid(ins, curr, 0);
-		if (ret < 0)
-			p = &(*p)->rb_left;
-		else if (ret > 0)
-			p = &(*p)->rb_right;
-		else
-			return -EEXIST;
-	}
-
-	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 *root_tree_search(struct root_lookup *root_tree,
-					  u64 root_id)
-{
-	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);
-
-		ret = comp_entry_with_rootid(&tmp, entry, 0);
-		if (ret < 0)
-			n = n->rb_left;
-		else if (ret > 0)
-			n = n->rb_right;
-		else
-			return entry;
-	}
-	return NULL;
-}
-
-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 otime, u8 *uuid, u8 *puuid, u8 *ruuid)
-{
-	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) {
-		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 (flags)
-		ri->flags = flags;
-	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 (otime)
-		ri->otime = otime;
-	if (uuid)
-		memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
-	if (puuid)
-		memcpy(&ri->puuid, puuid, BTRFS_UUID_SIZE);
-	if (ruuid)
-		memcpy(&ri->ruuid, ruuid, BTRFS_UUID_SIZE);
-
-	return 0;
-}
-
-/*
- * 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
- * otime: the original time (creation time) of the root
- * uuid: uuid of the root
- * puuid: uuid of the root parent if any
- * ruuid: uuid of the received subvol, 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 otime, u8 *uuid, u8 *puuid, u8 *ruuid)
-{
-	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, otime,
-			  uuid, puuid, ruuid);
-	if (!ret)
-		return 0;
-
-	ri = calloc(1, sizeof(*ri));
-	if (!ri) {
-		printf("memory allocation failed\n");
-		exit(1);
-	}
-	ri->root_id = root_id;
-
-	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);
-		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 (flags)
-		ri->flags = flags;
-	if (gen)
-		ri->gen = gen;
-	if (ogen)
-		ri->ogen = ogen;
-	if (!ri->ogen && root_offset)
-		ri->ogen = root_offset;
-	if (otime)
-		ri->otime = otime;
-
-	if (uuid)
-		memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
-
-	if (puuid)
-		memcpy(&ri->puuid, puuid, BTRFS_UUID_SIZE);
-
-	if (ruuid)
-		memcpy(&ri->ruuid, ruuid, BTRFS_UUID_SIZE);
-
-	ret = root_tree_insert(root_lookup, ri);
-	if (ret < 0) {
-		error("failed to insert subvolume %llu to tree: %s",
-				(unsigned long long)root_id, strerror(-ret));
-		exit(1);
-	}
-	return 0;
-}
-
-/*
- * Simplified add_root for back references, omits the uuid and original info
- * parameters, root offset and flags.
- */
-static int add_root_backref(struct root_lookup *root_lookup, u64 root_id,
-		u64 ref_tree, u64 dir_id, char *name, int name_len)
-{
-	return add_root(root_lookup, root_id, ref_tree, 0, 0, dir_id, name,
-			name_len, 0, 0, 0, NULL, NULL, NULL);
-}
-
-
-static void free_root_info(struct rb_node *node)
-{
-	struct root_info *ri;
-
-	ri = rb_entry(node, struct root_info, rb_node);
-	free(ri->name);
-	free(ri->path);
-	free(ri->full_path);
-	free(ri);
-}
-
-/*
- * for a given root_info, search through the root_lookup tree to construct
- * the full path name to it.
- *
- * This can't be called until all the root_info->path fields are filled
- * in by lookup_ino_path
- */
-static int resolve_root(struct root_lookup *rl, struct root_info *ri,
-		       u64 top_id)
-{
-	char *full_path = NULL;
-	int len = 0;
-	struct root_info *found;
-
-	/*
-	 * we go backwards from the root_info object and add pathnames
-	 * from parent directories as we go.
-	 */
-	found = ri;
-	while (1) {
-		char *tmp;
-		u64 next;
-		int add_len;
-
-		/*
-		 * ref_tree = 0 indicates the subvolume
-		 * has been deleted.
-		 */
-		if (!found->ref_tree) {
-			free(full_path);
-			return -ENOENT;
-		}
-
-		add_len = strlen(found->path);
-
-		if (full_path) {
-			/* room for / and for null */
-			tmp = malloc(add_len + 2 + len);
-			if (!tmp) {
-				perror("malloc failed");
-				exit(1);
-			}
-			memcpy(tmp + add_len + 1, full_path, len);
-			tmp[add_len] = '/';
-			memcpy(tmp, found->path, add_len);
-			tmp [add_len + len + 1] = '\0';
-			free(full_path);
-			full_path = tmp;
-			len += add_len + 1;
-		} else {
-			full_path = strdup(found->path);
-			len = add_len;
-		}
-		if (!ri->top_id)
-			ri->top_id = found->ref_tree;
-
-		next = found->ref_tree;
-		if (next == top_id)
-			break;
-		/*
-		* if the ref_tree = BTRFS_FS_TREE_OBJECTID,
-		* we are at the top
-		*/
-		if (next == BTRFS_FS_TREE_OBJECTID)
-			break;
-		/*
-		* if the ref_tree wasn't in our tree of roots, the
-		* subvolume was deleted.
-		*/
-		found = root_tree_search(rl, next);
-		if (!found) {
-			free(full_path);
-			return -ENOENT;
-		}
-	}
-
-	ri->full_path = full_path;
+		return comp_entry_with_rootid(entry1, entry2, 0);
 
 	return 0;
 }
 
-/*
- * for a single root_info, ask the kernel to give us a path name
- * inside it's ref_root for the dir_id where it lives.
- *
- * This fills in root_info->path with the path to the directory and and
- * appends this root's name.
- */
-static int lookup_ino_path(int fd, struct root_info *ri)
+static void sort_subvols(struct btrfs_list_comparer_set *comp_set,
+			 struct subvol_list *subvols)
 {
-	struct btrfs_ioctl_ino_lookup_args args;
-	int ret;
-
-	if (ri->path)
-		return 0;
-
-	if (!ri->ref_tree)
-		return -ENOENT;
-
-	memset(&args, 0, sizeof(args));
-	args.treeid = ri->ref_tree;
-	args.objectid = ri->dir_id;
-
-	ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
-	if (ret < 0) {
-		if (errno == ENOENT) {
-			ri->ref_tree = 0;
-			return -ENOENT;
-		}
-		error("failed to lookup path for root %llu: %s",
-			(unsigned long long)ri->ref_tree, strerror(errno));
-		return ret;
-	}
-
-	if (args.name[0]) {
-		/*
-		 * we're in a subdirectory of ref_tree, the kernel ioctl
-		 * puts a / in there for us
-		 */
-		ri->path = malloc(strlen(ri->name) + strlen(args.name) + 1);
-		if (!ri->path) {
-			perror("malloc failed");
-			exit(1);
-		}
-		strcpy(ri->path, args.name);
-		strcat(ri->path, ri->name);
-	} else {
-		/* we're at the root of ref_tree */
-		ri->path = strdup(ri->name);
-		if (!ri->path) {
-			perror("strdup failed");
-			exit(1);
-		}
-	}
-	return 0;
+	qsort_r(subvols->subvols, subvols->num, sizeof(subvols->subvols[0]),
+		subvol_comp, comp_set);
 }
 
 /* finding the generation for a given path is a two step process.
@@ -920,198 +554,83 @@ int btrfs_list_get_default_subvolume(int fd, u64 *default_id)
 	return 0;
 }
 
-static int list_subvol_search(int fd, struct root_lookup *root_lookup)
+static int filter_by_rootid(struct listed_subvol *subvol, uint64_t data)
 {
-	int ret;
-	struct btrfs_ioctl_search_args args;
-	struct btrfs_ioctl_search_key *sk = &args.key;
-	struct btrfs_ioctl_search_header sh;
-	struct btrfs_root_ref *ref;
-	struct btrfs_root_item *ri;
-	unsigned long off;
-	int name_len;
-	char *name;
-	u64 dir_id;
-	u64 gen = 0;
-	u64 ogen;
-	u64 flags;
-	int i;
-
-	root_lookup->root.rb_node = NULL;
-	memset(&args, 0, sizeof(args));
-
-	sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
-	/* Search both live and deleted subvolumes */
-	sk->min_type = BTRFS_ROOT_ITEM_KEY;
-	sk->max_type = BTRFS_ROOT_BACKREF_KEY;
-	sk->min_objectid = BTRFS_FS_TREE_OBJECTID;
-	sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
-	sk->max_offset = (u64)-1;
-	sk->max_transid = (u64)-1;
-
-	while(1) {
-		sk->nr_items = 4096;
-		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
-		if (ret < 0)
-			return ret;
-		if (sk->nr_items == 0)
-			break;
-
-		off = 0;
-
-		/*
-		 * for each item, pull the key out of the header and then
-		 * read the root_ref item it contains
-		 */
-		for (i = 0; i < sk->nr_items; i++) {
-			memcpy(&sh, args.buf + off, sizeof(sh));
-			off += sizeof(sh);
-			if (sh.type == BTRFS_ROOT_BACKREF_KEY) {
-				ref = (struct btrfs_root_ref *)(args.buf + off);
-				name_len = btrfs_stack_root_ref_name_len(ref);
-				name = (char *)(ref + 1);
-				dir_id = btrfs_stack_root_ref_dirid(ref);
-
-				add_root_backref(root_lookup, sh.objectid,
-						sh.offset, dir_id, name,
-						name_len);
-			} else if (sh.type == BTRFS_ROOT_ITEM_KEY &&
-				   (sh.objectid >= BTRFS_FIRST_FREE_OBJECTID ||
-				    sh.objectid == BTRFS_FS_TREE_OBJECTID)) {
-				time_t otime;
-				u8 uuid[BTRFS_UUID_SIZE];
-				u8 puuid[BTRFS_UUID_SIZE];
-				u8 ruuid[BTRFS_UUID_SIZE];
-
-				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)) {
-					otime = btrfs_stack_timespec_sec(&ri->otime);
-					ogen = btrfs_root_otransid(ri);
-					memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
-					memcpy(puuid, ri->parent_uuid, BTRFS_UUID_SIZE);
-					memcpy(ruuid, ri->received_uuid, BTRFS_UUID_SIZE);
-				} else {
-					otime = 0;
-					ogen = 0;
-					memset(uuid, 0, BTRFS_UUID_SIZE);
-					memset(puuid, 0, BTRFS_UUID_SIZE);
-					memset(ruuid, 0, BTRFS_UUID_SIZE);
-				}
-
-				add_root(root_lookup, sh.objectid, 0,
-					 sh.offset, flags, 0, NULL, 0, ogen,
-					 gen, otime, uuid, puuid, ruuid);
-			}
-
-			off += sh.len;
-
-			sk->min_objectid = sh.objectid;
-			sk->min_type = sh.type;
-			sk->min_offset = sh.offset;
-		}
-		sk->min_offset++;
-		if (!sk->min_offset)
-			sk->min_type++;
-		else
-			continue;
-
-		if (sk->min_type > BTRFS_ROOT_BACKREF_KEY) {
-			sk->min_type = BTRFS_ROOT_ITEM_KEY;
-			sk->min_objectid++;
-		} else
-			continue;
-
-		if (sk->min_objectid > sk->max_objectid)
-			break;
-	}
-
-	return 0;
+	return subvol->info.id == data;
 }
 
-static int filter_by_rootid(struct root_info *ri, u64 data)
+static int filter_snapshot(struct listed_subvol *subvol, uint64_t data)
 {
-	return ri->root_id == data;
+	return !uuid_is_null(subvol->info.parent_uuid);
 }
 
-static int filter_snapshot(struct root_info *ri, u64 data)
+static int filter_flags(struct listed_subvol *subvol, uint64_t data)
 {
-	return !!ri->root_offset;
+	return subvol->info.flags & data;
 }
 
-static int filter_flags(struct root_info *ri, u64 flags)
+static int filter_gen_more(struct listed_subvol *subvol, uint64_t data)
 {
-	return ri->flags & flags;
+	return subvol->info.generation >= data;
 }
 
-static int filter_gen_more(struct root_info *ri, u64 data)
+static int filter_gen_less(struct listed_subvol *subvol, uint64_t data)
 {
-	return ri->gen >= data;
+	return subvol->info.generation <= data;
 }
 
-static int filter_gen_less(struct root_info *ri, u64 data)
+static int filter_gen_equal(struct listed_subvol *subvol, uint64_t data)
 {
-	return ri->gen <= data;
+	return subvol->info.generation == data;
 }
 
-static int filter_gen_equal(struct root_info  *ri, u64 data)
+static int filter_cgen_more(struct listed_subvol *subvol, uint64_t data)
 {
-	return ri->gen == data;
+	return subvol->info.otransid >= data;
 }
 
-static int filter_cgen_more(struct root_info *ri, u64 data)
+static int filter_cgen_less(struct listed_subvol *subvol, uint64_t data)
 {
-	return ri->ogen >= data;
+	return subvol->info.otransid <= data;
 }
 
-static int filter_cgen_less(struct root_info *ri, u64 data)
+static int filter_cgen_equal(struct listed_subvol *subvol, uint64_t data)
 {
-	return ri->ogen <= data;
+	return subvol->info.otransid == data;
 }
 
-static int filter_cgen_equal(struct root_info *ri, u64 data)
+static int filter_topid_equal(struct listed_subvol *subvol, uint64_t data)
 {
-	return ri->ogen == data;
+	/* See the comment in print_subvolume_column() about top level. */
+	return subvol->info.parent_id == data;
 }
 
-static int filter_topid_equal(struct root_info *ri, u64 data)
+static int filter_full_path(struct listed_subvol *subvol, uint64_t data)
 {
-	return ri->top_id == data;
-}
-
-static int filter_full_path(struct root_info *ri, u64 data)
-{
-	if (ri->full_path && ri->top_id != data) {
+	/*
+	 * This implements the same behavior as before the conversion to
+	 * libbtrfsutil, which is mostly nonsensical.
+	 */
+	if (subvol->info.parent_id != data) {
 		char *tmp;
-		char p[] = "<FS_TREE>";
-		int add_len = strlen(p);
-		int len = strlen(ri->full_path);
+		int ret;
 
-		tmp = malloc(len + add_len + 2);
-		if (!tmp) {
-			fprintf(stderr, "memory allocation failed\n");
+		ret = asprintf(&tmp, "<FS_TREE>/%s", subvol->path);
+		if (ret == -1) {
+			error("out of memory");
 			exit(1);
 		}
-		memcpy(tmp + add_len + 1, ri->full_path, len);
-		tmp[len + add_len + 1] = '\0';
-		tmp[add_len] = '/';
-		memcpy(tmp, p, add_len);
-		free(ri->full_path);
-		ri->full_path = tmp;
+
+		free(subvol->path);
+		subvol->path = tmp;
 	}
 	return 1;
 }
 
-static int filter_by_parent(struct root_info *ri, u64 data)
+static int filter_by_parent(struct listed_subvol *subvol, uint64_t data)
 {
-	return !uuid_compare(ri->puuid, (u8 *)(unsigned long)data);
-}
-
-static int filter_deleted(struct root_info *ri, u64 data)
-{
-	return ri->deleted;
+	return !uuid_compare(subvol->info.parent_uuid,
+			     (u8 *)(unsigned long)data);
 }
 
 static btrfs_list_filter_func all_filter_funcs[] = {
@@ -1127,7 +646,6 @@ static btrfs_list_filter_func all_filter_funcs[] = {
 	[BTRFS_LIST_FILTER_TOPID_EQUAL]		= filter_topid_equal,
 	[BTRFS_LIST_FILTER_FULL_PATH]		= filter_full_path,
 	[BTRFS_LIST_FILTER_BY_PARENT]		= filter_by_parent,
-	[BTRFS_LIST_FILTER_DELETED]		= filter_deleted,
 };
 
 struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void)
@@ -1184,95 +702,34 @@ void btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
 
 	ASSERT(set->filters[set->nfilters].filter_func == NULL);
 
-	if (filter == BTRFS_LIST_FILTER_DELETED)
+	if (filter == BTRFS_LIST_FILTER_DELETED) {
 		set->only_deleted = 1;
-
-	set->filters[set->nfilters].filter_func = all_filter_funcs[filter];
-	set->filters[set->nfilters].data = data;
-	set->nfilters++;
+	} else {
+		set->filters[set->nfilters].filter_func = all_filter_funcs[filter];
+		set->filters[set->nfilters].data = data;
+		set->nfilters++;
+	}
 }
 
-static int filter_root(struct root_info *ri,
-		       struct btrfs_list_filter_set *set)
+static int filters_match(struct listed_subvol *subvol,
+			 struct btrfs_list_filter_set *set)
 {
 	int i, ret;
 
 	if (!set)
 		return 1;
 
-	if (set->only_deleted && !ri->deleted)
-		return 0;
-
-	if (!set->only_deleted && ri->deleted)
-		return 0;
-
 	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);
+		ret = set->filters[i].filter_func(subvol, 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,
-				    u64 top_id)
-{
-	struct rb_node *n;
-	struct root_info *entry;
-	int ret;
-
-	sort_tree->root.rb_node = NULL;
-
-	n = rb_last(&all_subvols->root);
-	while (n) {
-		entry = rb_entry(n, struct root_info, rb_node);
-
-		ret = resolve_root(all_subvols, entry, top_id);
-		if (ret == -ENOENT) {
-			if (entry->root_id != BTRFS_FS_TREE_OBJECTID) {
-				entry->full_path = strdup("DELETED");
-				entry->deleted = 1;
-			} else {
-				/*
-				 * The full path is not supposed to be printed,
-				 * but we don't want to print an empty string,
-				 * in case it appears somewhere.
-				 */
-				entry->full_path = strdup("TOPLEVEL");
-				entry->deleted = 0;
-			}
-		}
-		ret = filter_root(entry, filter_set);
-		if (ret)
-			sort_tree_insert(sort_tree, entry, comp_set);
-		n = rb_prev(n);
-	}
-}
-
-static int list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
-{
-	struct rb_node *n;
-
-	n = rb_first(&root_lookup->root);
-	while (n) {
-		struct root_info *entry;
-		int ret;
-		entry = rb_entry(n, struct root_info, rb_node);
-		ret = lookup_ino_path(fd, entry);
-		if (ret && ret != -ENOENT)
-			return ret;
-		n = rb_next(n);
-	}
-
-	return 0;
-}
-
-static void print_subvolume_column(struct root_info *subv,
+static void print_subvolume_column(struct listed_subvol *subvol,
 				   enum btrfs_list_column_enum column)
 {
 	char tstr[256];
@@ -1282,62 +739,65 @@ static void print_subvolume_column(struct root_info *subv,
 
 	switch (column) {
 	case BTRFS_LIST_OBJECTID:
-		printf("%llu", subv->root_id);
+		printf("%" PRIu64, subvol->info.id);
 		break;
 	case BTRFS_LIST_GENERATION:
-		printf("%llu", subv->gen);
+		printf("%" PRIu64, subvol->info.generation);
 		break;
 	case BTRFS_LIST_OGENERATION:
-		printf("%llu", subv->ogen);
+		printf("%" PRIu64, subvol->info.otransid);
 		break;
 	case BTRFS_LIST_PARENT:
-		printf("%llu", subv->ref_tree);
-		break;
+	/*
+	 * Top level used to mean something else, but since 4f5ebb3ef553
+	 * ("Btrfs-progs: fix to make list specified directory's subvolumes
+	 * work") it was always set to the parent ID. See
+	 * https://www.spinics.net/lists/linux-btrfs/msg69820.html.
+	 */
 	case BTRFS_LIST_TOP_LEVEL:
-		printf("%llu", subv->top_id);
+		printf("%" PRIu64, subvol->info.parent_id);
 		break;
 	case BTRFS_LIST_OTIME:
-		if (subv->otime) {
+		if (subvol->info.otime.tv_sec) {
 			struct tm tm;
 
-			localtime_r(&subv->otime, &tm);
-			strftime(tstr, 256, "%Y-%m-%d %X", &tm);
+			localtime_r(&subvol->info.otime.tv_sec, &tm);
+			strftime(tstr, sizeof(tstr), "%Y-%m-%d %X", &tm);
 		} else
 			strcpy(tstr, "-");
 		printf("%s", tstr);
 		break;
 	case BTRFS_LIST_UUID:
-		if (uuid_is_null(subv->uuid))
+		if (uuid_is_null(subvol->info.uuid))
 			strcpy(uuidparse, "-");
 		else
-			uuid_unparse(subv->uuid, uuidparse);
+			uuid_unparse(subvol->info.uuid, uuidparse);
 		printf("%-36s", uuidparse);
 		break;
 	case BTRFS_LIST_PUUID:
-		if (uuid_is_null(subv->puuid))
+		if (uuid_is_null(subvol->info.parent_uuid))
 			strcpy(uuidparse, "-");
 		else
-			uuid_unparse(subv->puuid, uuidparse);
+			uuid_unparse(subvol->info.parent_uuid, uuidparse);
 		printf("%-36s", uuidparse);
 		break;
 	case BTRFS_LIST_RUUID:
-		if (uuid_is_null(subv->ruuid))
+		if (uuid_is_null(subvol->info.received_uuid))
 			strcpy(uuidparse, "-");
 		else
-			uuid_unparse(subv->ruuid, uuidparse);
+			uuid_unparse(subvol->info.received_uuid, uuidparse);
 		printf("%-36s", uuidparse);
 		break;
 	case BTRFS_LIST_PATH:
-		BUG_ON(!subv->full_path);
-		printf("%s", subv->full_path);
+		printf("%s", subvol->path);
 		break;
 	default:
 		break;
 	}
 }
 
-static void print_one_subvol_info_raw(struct root_info *subv,
-		const char *raw_prefix)
+static void print_one_subvol_info_raw(struct listed_subvol *subvol,
+				      const char *raw_prefix)
 {
 	int i;
 
@@ -1348,12 +808,12 @@ static void print_one_subvol_info_raw(struct root_info *subv,
 		if (raw_prefix)
 			printf("%s",raw_prefix);
 
-		print_subvolume_column(subv, i);
+		print_subvolume_column(subvol, i);
 	}
 	printf("\n");
 }
 
-static void print_one_subvol_info_table(struct root_info *subv)
+static void print_one_subvol_info_table(struct listed_subvol *subvol)
 {
 	int i;
 
@@ -1361,7 +821,7 @@ static void print_one_subvol_info_table(struct root_info *subv)
 		if (!btrfs_list_columns[i].need_print)
 			continue;
 
-		print_subvolume_column(subv, i);
+		print_subvolume_column(subvol, i);
 
 		if (i != BTRFS_LIST_PATH)
 			printf("\t");
@@ -1372,7 +832,7 @@ static void print_one_subvol_info_table(struct root_info *subv)
 	printf("\n");
 }
 
-static void print_one_subvol_info_default(struct root_info *subv)
+static void print_one_subvol_info_default(struct listed_subvol *subvol)
 {
 	int i;
 
@@ -1381,7 +841,7 @@ static void print_one_subvol_info_default(struct root_info *subv)
 			continue;
 
 		printf("%s ", btrfs_list_columns[i].name);
-		print_subvolume_column(subv, i);
+		print_subvolume_column(subvol, i);
 
 		if (i != BTRFS_LIST_PATH)
 			printf(" ");
@@ -1418,66 +878,174 @@ static void print_all_subvol_info_tab_head(void)
 	}
 }
 
-static void print_all_subvol_info(struct root_lookup *sorted_tree,
-		  enum btrfs_list_layout layout, const char *raw_prefix)
+static void print_all_subvol_info(struct subvol_list *subvols,
+				  enum btrfs_list_layout layout,
+				  const char *raw_prefix)
 {
-	struct rb_node *n;
-	struct root_info *entry;
+	size_t i;
 
 	if (layout == BTRFS_LIST_LAYOUT_TABLE)
 		print_all_subvol_info_tab_head();
 
-	n = rb_first(&sorted_tree->root);
-	while (n) {
-		entry = rb_entry(n, struct root_info, sort_node);
-
-		/* The toplevel subvolume is not listed by default */
-		if (entry->root_id == BTRFS_FS_TREE_OBJECTID)
-			goto next;
+	for (i = 0; i < subvols->num; i++) {
+		struct listed_subvol *subvol = &subvols->subvols[i];
 
 		switch (layout) {
 		case BTRFS_LIST_LAYOUT_DEFAULT:
-			print_one_subvol_info_default(entry);
+			print_one_subvol_info_default(subvol);
 			break;
 		case BTRFS_LIST_LAYOUT_TABLE:
-			print_one_subvol_info_table(entry);
+			print_one_subvol_info_table(subvol);
 			break;
 		case BTRFS_LIST_LAYOUT_RAW:
-			print_one_subvol_info_raw(entry, raw_prefix);
+			print_one_subvol_info_raw(subvol, raw_prefix);
 			break;
 		}
-next:
-		n = rb_next(n);
 	}
 }
 
-static int btrfs_list_subvols(int fd, struct root_lookup *root_lookup)
+static void free_subvol_list(struct subvol_list *subvols)
 {
-	int ret;
+	size_t i;
+
+	if (subvols) {
+		for (i = 0; i < subvols->num; i++)
+			free(subvols->subvols[i].path);
+		free(subvols);
+	}
+}
 
-	ret = list_subvol_search(fd, root_lookup);
+static struct subvol_list *btrfs_list_deleted_subvols(int fd,
+						      struct btrfs_list_filter_set *filter_set)
+{
+	struct subvol_list *subvols = NULL;
+	uint64_t *ids = NULL;
+	size_t i, n;
+	enum btrfs_util_error err;
+	int ret = -1;
+
+	err = btrfs_util_f_deleted_subvolumes(fd, &ids, &n);
+	if (err) {
+		error_btrfs_util(err);
+		return NULL;
+	}
+
+	subvols = malloc(sizeof(*subvols) + n * sizeof(subvols->subvols[0]));
+	if (!subvols) {
+		error("out of memory");
+		goto out;
+	}
+
+	subvols->num = 0;
+	for (i = 0; i < n; i++) {
+		struct listed_subvol *subvol = &subvols->subvols[subvols->num];
+
+		err = btrfs_util_f_subvolume_info(fd, ids[i], &subvol->info);
+		if (err) {
+			error_btrfs_util(err);
+			goto out;
+		}
+
+		subvol->path = strdup("DELETED");
+		if (!subvol->path)
+			goto out;
+
+		if (!filters_match(subvol, filter_set)) {
+			free(subvol->path);
+			continue;
+		}
+
+		subvols->num++;
+	}
+
+	ret = 0;
+out:
 	if (ret) {
-		error("can't perform the search: %s", strerror(errno));
-		return ret;
+		free_subvol_list(subvols);
+		subvols = NULL;
+		free(ids);
 	}
+	return subvols;
+}
 
-	/*
-	 * 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);
-	return ret;
+static struct subvol_list *btrfs_list_subvols(int fd,
+					      struct btrfs_list_filter_set *filter_set)
+{
+	struct subvol_list *subvols;
+	size_t capacity = 0;
+	struct btrfs_util_subvolume_iterator *iter;
+	enum btrfs_util_error err;
+	int ret = -1;
+
+	subvols = malloc(sizeof(*subvols));
+	if (!subvols) {
+		error("out of memory");
+		return NULL;
+	}
+	subvols->num = 0;
+
+	err = btrfs_util_f_create_subvolume_iterator(fd, BTRFS_FS_TREE_OBJECTID,
+						     0, &iter);
+	if (err) {
+		iter = NULL;
+		error_btrfs_util(err);
+		goto out;
+	}
+
+	for (;;) {
+		struct listed_subvol subvol;
+
+		err = btrfs_util_subvolume_iterator_next_info(iter,
+							      &subvol.path,
+							      &subvol.info);
+		if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
+			break;
+		} else if (err) {
+			error_btrfs_util(err);
+			goto out;
+		}
+
+		if (!filters_match(&subvol, filter_set)) {
+			free(subvol.path);
+			continue;
+		}
+
+		if (subvols->num >= capacity) {
+			struct subvol_list *new_subvols;
+			size_t new_capacity = max_t(size_t, 1, capacity * 2);
+
+			new_subvols = realloc(subvols,
+					      sizeof(*new_subvols) +
+					      new_capacity *
+					      sizeof(new_subvols->subvols[0]));
+			if (!new_subvols) {
+				error("out of memory");
+				goto out;
+			}
+
+			subvols = new_subvols;
+			capacity = new_capacity;
+		}
+
+		subvols->subvols[subvols->num] = subvol;
+		subvols->num++;
+	}
+
+	ret = 0;
+out:
+	if (iter)
+		btrfs_util_destroy_subvolume_iterator(iter);
+	if (ret)
+		free_subvol_list(subvols);
+	return subvols;
 }
 
 int btrfs_list_subvols_print(int fd, struct btrfs_list_filter_set *filter_set,
-		       struct btrfs_list_comparer_set *comp_set,
-		       enum btrfs_list_layout layout, int full_path,
-		       const char *raw_prefix)
+			     struct btrfs_list_comparer_set *comp_set,
+			     enum btrfs_list_layout layout, int full_path,
+			     const char *raw_prefix)
 {
-	struct root_lookup root_lookup;
-	struct root_lookup root_sort;
-	int ret = 0;
-	u64 top_id = 0;
+	struct subvol_list *subvols;
 
 	/*
 	 * full_path hasn't done anything since 4f5ebb3ef553 ("Btrfs-progs: fix
@@ -1485,14 +1053,19 @@ int btrfs_list_subvols_print(int fd, struct btrfs_list_filter_set *filter_set,
 	 * https://www.spinics.net/lists/linux-btrfs/msg69820.html
 	 */
 
-	ret = btrfs_list_subvols(fd, &root_lookup);
-	if (ret)
-		return ret;
-	filter_and_sort_subvol(&root_lookup, &root_sort, filter_set,
-				 comp_set, top_id);
+	if (filter_set->only_deleted) {
+		subvols = btrfs_list_deleted_subvols(fd, filter_set);
+	} else {
+		subvols = btrfs_list_subvols(fd, filter_set);
+	}
+	if (!subvols)
+		return -1;
+
+	sort_subvols(comp_set, subvols);
+
+	print_all_subvol_info(subvols, layout, raw_prefix);
 
-	print_all_subvol_info(&root_sort, layout, raw_prefix);
-	rb_free_nodes(&root_lookup.root, free_root_info);
+	free_subvol_list(subvols);
 
 	return 0;
 }
diff --git a/btrfs-list.h b/btrfs-list.h
index 317b8cf7..4780bfc2 100644
--- a/btrfs-list.h
+++ b/btrfs-list.h
@@ -87,8 +87,11 @@ struct root_info {
 	int deleted;
 };
 
-typedef int (*btrfs_list_filter_func)(struct root_info *, u64);
-typedef int (*btrfs_list_comp_func)(struct root_info *, struct root_info *,
+struct listed_subvol;
+
+typedef int (*btrfs_list_filter_func)(struct listed_subvol *, uint64_t);
+typedef int (*btrfs_list_comp_func)(const struct listed_subvol *,
+				    const struct listed_subvol *,
 				    int);
 
 struct btrfs_list_filter {
-- 
2.16.1


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

* Re: [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library"
  2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (25 preceding siblings ...)
  2018-01-26 18:41 ` [PATCH 26/26] btrfs-progs: use libbtrfsutil for subvolume list Omar Sandoval
@ 2018-01-26 18:51 ` Hugo Mills
  2018-02-02 23:04   ` Hans van Kranenburg
  26 siblings, 1 reply; 47+ messages in thread
From: Hugo Mills @ 2018-01-26 18:51 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: linux-btrfs, kernel-team

[-- Attachment #1: Type: text/plain, Size: 7122 bytes --]

On Fri, Jan 26, 2018 at 10:40:48AM -0800, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>
> 
> Hello,
> 
> One of the features requests I get most often is a library to do the
> sorts of operations that we do with btrfs-progs. We can shell out to
> btrfs-progs, but the output format isn't always easily parsasble, and
> shelling out isn't always ideal. There's libbtrfs, but it's very
> incomplete, doesn't have a well thought out API, and is licensed under
> the GPL, making it hard to use for many projects.
> 
> libbtrfsutil is a new library written from scratch to address these
> issues. The implementation is completely independent of the existing
> btrfs-progs code, including kerncompat.h, and has a clean API and naming
> scheme. It is licensed under the LGPL. It also includes Python bindings
> by default. I will maintain the library code.

   *speechless*

   That's awesome, Omar (although with the python bindings, you've
probably just ruined Hans' day ;) ).

   Hugo.

> Patch 1 is a preparation cleanup which can go in independently. Patch 2
> adds the build system stuff for the library, and patch 3 does the same
> for the Python bindings. Patches 4-14 implement the library helpers,
> currently subvolume helpers and the sync ioctls. Patches 15-26 replace
> the btrfs-progs and libbtrfs code to use libbtrfsutil instead. I took
> care to preserve backwards-compatibility. `btrfs subvol list` in
> particular had some buggy behaviors for -o and -a that I emulated in the
> new code, see the comments in the code.
> 
> These patches are also available on my GitHub:
> https://github.com/osandov/btrfs-progs/tree/libbtrfsutil. That branch
> will rebase as I update this series.
> 
> Please share feedback regarding the API, implementation, or anything
> else.
> 
> Thanks!
> 
> Omar Sandoval (26):
>   btrfs-progs: get rid of undocumented qgroup inheritance options
>   Add libbtrfsutil
>   libbtrfsutil: add Python bindings
>   libbtrfsutil: add btrfs_util_is_subvolume() and
>     btrfs_util_subvolume_id()
>   libbtrfsutil: add qgroup inheritance helpers
>   libbtrfsutil: add btrfs_util_create_subvolume()
>   libbtrfsutil: add btrfs_util_subvolume_info()
>   libbtrfsutil: add btrfs_util_[gs]et_read_only()
>   libbtrfsutil: add btrfs_util_[gs]et_default_subvolume()
>   libbtrfsutil: add subvolume iterator helpers
>   libbtrfsutil: add btrfs_util_create_snapshot()
>   libbtrfsutil: add btrfs_util_delete_subvolume()
>   libbtrfsutil: add btrfs_util_deleted_subvolumes()
>   libbtrfsutil: add filesystem sync helpers
>   btrfs-progs: use libbtrfsutil for read-only property
>   btrfs-progs: use libbtrfsutil for sync ioctls
>   btrfs-progs: use libbtrfsutil for set-default
>   btrfs-progs: use libbtrfsutil for get-default
>   btrfs-progs: use libbtrfsutil for subvol create and snapshot
>   btrfs-progs: use libbtrfsutil for subvol delete
>   btrfs-progs: use libbtrfsutil for subvol show
>   btrfs-progs: use libbtrfsutil for subvol sync
>   btrfs-progs: replace test_issubvolume() with btrfs_util_is_subvolume()
>   btrfs-progs: add recursive snapshot/delete using libbtrfsutil
>   btrfs-progs: deprecate libbtrfs helpers with libbtrfsutil equivalents
>   btrfs-progs: use libbtrfsutil for subvolume list
> 
>  .gitignore                                   |    2 +
>  Documentation/btrfs-subvolume.asciidoc       |   14 +-
>  INSTALL                                      |    4 +
>  Makefile                                     |   84 +-
>  Makefile.inc.in                              |    2 +
>  btrfs-list.c                                 | 1201 +++++++---------------
>  btrfs-list.h                                 |   22 +-
>  cmds-filesystem.c                            |   20 +-
>  cmds-inspect.c                               |    9 +-
>  cmds-qgroup.c                                |   20 +-
>  cmds-receive.c                               |   12 +-
>  cmds-subvolume.c                             |  807 +++++----------
>  configure.ac                                 |   15 +
>  libbtrfsutil/COPYING                         |  674 +++++++++++++
>  libbtrfsutil/COPYING.LESSER                  |  165 +++
>  libbtrfsutil/README.md                       |   35 +
>  libbtrfsutil/btrfsutil.h                     |  621 ++++++++++++
>  libbtrfsutil/errors.c                        |   55 +
>  libbtrfsutil/filesystem.c                    |  103 ++
>  libbtrfsutil/internal.h                      |   36 +
>  libbtrfsutil/python/.gitignore               |    7 +
>  libbtrfsutil/python/btrfsutilpy.h            |   84 ++
>  libbtrfsutil/python/error.c                  |  202 ++++
>  libbtrfsutil/python/filesystem.c             |   94 ++
>  libbtrfsutil/python/module.c                 |  321 ++++++
>  libbtrfsutil/python/qgroup.c                 |  141 +++
>  libbtrfsutil/python/setup.py                 |  103 ++
>  libbtrfsutil/python/subvolume.c              |  665 +++++++++++++
>  libbtrfsutil/python/tests/__init__.py        |   66 ++
>  libbtrfsutil/python/tests/test_filesystem.py |   73 ++
>  libbtrfsutil/python/tests/test_qgroup.py     |   57 ++
>  libbtrfsutil/python/tests/test_subvolume.py  |  383 +++++++
>  libbtrfsutil/qgroup.c                        |   86 ++
>  libbtrfsutil/subvolume.c                     | 1383 ++++++++++++++++++++++++++
>  messages.h                                   |   14 +
>  props.c                                      |   69 +-
>  qgroup.c                                     |  106 --
>  qgroup.h                                     |    4 -
>  send-utils.c                                 |   25 +-
>  utils.c                                      |  152 +--
>  utils.h                                      |    6 -
>  41 files changed, 6188 insertions(+), 1754 deletions(-)
>  create mode 100644 libbtrfsutil/COPYING
>  create mode 100644 libbtrfsutil/COPYING.LESSER
>  create mode 100644 libbtrfsutil/README.md
>  create mode 100644 libbtrfsutil/btrfsutil.h
>  create mode 100644 libbtrfsutil/errors.c
>  create mode 100644 libbtrfsutil/filesystem.c
>  create mode 100644 libbtrfsutil/internal.h
>  create mode 100644 libbtrfsutil/python/.gitignore
>  create mode 100644 libbtrfsutil/python/btrfsutilpy.h
>  create mode 100644 libbtrfsutil/python/error.c
>  create mode 100644 libbtrfsutil/python/filesystem.c
>  create mode 100644 libbtrfsutil/python/module.c
>  create mode 100644 libbtrfsutil/python/qgroup.c
>  create mode 100755 libbtrfsutil/python/setup.py
>  create mode 100644 libbtrfsutil/python/subvolume.c
>  create mode 100644 libbtrfsutil/python/tests/__init__.py
>  create mode 100644 libbtrfsutil/python/tests/test_filesystem.py
>  create mode 100644 libbtrfsutil/python/tests/test_qgroup.py
>  create mode 100644 libbtrfsutil/python/tests/test_subvolume.py
>  create mode 100644 libbtrfsutil/qgroup.c
>  create mode 100644 libbtrfsutil/subvolume.c
> 

-- 
Hugo Mills             | And what rough beast, its hour come round at last /
hugo@... carfax.org.uk | slouches towards Bethlehem, to be born?
http://carfax.org.uk/  |
PGP: E2AB1DE4          |                         W.B. Yeats, The Second Coming

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

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

* Re: [PATCH 11/26] libbtrfsutil: add btrfs_util_create_snapshot()
  2018-01-26 18:40 ` [PATCH 11/26] libbtrfsutil: add btrfs_util_create_snapshot() Omar Sandoval
@ 2018-01-26 19:31   ` Goffredo Baroncelli
  2018-01-26 19:46     ` Omar Sandoval
  0 siblings, 1 reply; 47+ messages in thread
From: Goffredo Baroncelli @ 2018-01-26 19:31 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: linux-btrfs, kernel-team

On 01/26/2018 07:40 PM, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>


Hi,

this is a great work; only few comments:
1) I found not intuitive the naming of the function: i.e. you have 

btrfs_util_create_snapshot()
btrfs_util_f_create_snapshot()

To me it seems more clear to have

btrfs_util_create_snapshot()
btrfs_util_create_snapshot_f()

I think that it is better move the 'f' at the end: at the begin you have the library "btrfs_util", in the middle you have the library function 'create_snapshot', at the end there is the function variant ('f', because it uses a file descriptor).

This is my opinion, even tough there are both examples like you (stat/fstat/lstat) and like my one (capt_get_fd/cap_get_file)...


2) I find the prefix 'btrfs_util_' a bit verbose. Why not a simple 'btrfs_', even at the cost of a possible renaming of the conflicting function in the current btrfs code.

3) regarding the btrfs_util_create_snapshot() function, I think that it would be useful to add some more information:
a) if used recursive is NOT atomic
b) if used recursive, root capabilities are needed

The same for the other functions: mark with a 'root required' tag all the functions which require the root capabilities.

BR
G.Baroncelli



> 
> Thanks to subvolume iterators, we can also implement recursive snapshot
> fairly easily.
> 
> Signed-off-by: Omar Sandoval <osandov@fb.com>
> ---
>  libbtrfsutil/btrfsutil.h                    |  55 +++++++++
>  libbtrfsutil/python/btrfsutilpy.h           |   1 +
>  libbtrfsutil/python/module.c                |  11 ++
>  libbtrfsutil/python/subvolume.c             |  49 ++++++++
>  libbtrfsutil/python/tests/test_qgroup.py    |  10 ++
>  libbtrfsutil/python/tests/test_subvolume.py |  55 +++++++++
>  libbtrfsutil/subvolume.c                    | 166 ++++++++++++++++++++++++++++
>  7 files changed, 347 insertions(+)
> 
> diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
> index 3f78a34d..829f72b2 100644
> --- a/libbtrfsutil/btrfsutil.h
> +++ b/libbtrfsutil/btrfsutil.h
> @@ -332,6 +332,61 @@ enum btrfs_util_error btrfs_util_f_create_subvolume(int parent_fd,
>  						    uint64_t *async_transid,
>  						    struct btrfs_util_qgroup_inherit *qgroup_inherit);
>  
> +/**
> + * BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE - Also snapshot subvolumes beneath the
> + * source subvolume onto the same location on the new snapshot. Note that this
> + * modifies the newly-created snapshot, so it cannot be combined with
> + * %BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY.
> + */
> +#define BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE (1 << 0)
> +/**
> + * BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY - Create a read-only snapshot.
> + */
> +#define BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY (1 << 1)
> +#define BTRFS_UTIL_CREATE_SNAPSHOT_MASK ((1 << 2) - 1)
> +
> +/**
> + * btrfs_util_create_snapshot() - Create a new snapshot from a source subvolume
> + * path.
> + * @source: Path of the existing subvolume to snapshot.
> + * @path: Where to create the snapshot.
> + * @flags: Bitmask of BTRFS_UTIL_CREATE_SNAPSHOT_* flags.
> + * @async_transid: See btrfs_util_create_subvolume(). If
> + * %BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE was in @flags, then this will contain
> + * the largest transaction ID of all created subvolumes.
> + * @qgroup_inherit: See btrfs_util_create_subvolume().
> + *
> + * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
> + */
> +enum btrfs_util_error btrfs_util_create_snapshot(const char *source,
> +						 const char *path, int flags,
> +						 uint64_t *async_transid,
> +						 struct btrfs_util_qgroup_inherit *qgroup_inherit);
> +
> +/**
> + * btrfs_util_f_create_snapshot() - See btrfs_util_create_snapshot().
> + */
> +enum btrfs_util_error btrfs_util_f_create_snapshot(int fd, const char *path,
> +						   int flags,
> +						   uint64_t *async_transid,
> +						   struct btrfs_util_qgroup_inherit *qgroup_inherit);
> +
> +/**
> + * btrfs_util_f2_create_snapshot() - Create a new snapshot from a source
> + * subvolume file descriptor and a target parent file descriptor and name.
> + * @fd: File descriptor of the existing subvolume to snapshot.
> + * @parent_fd: File descriptor of the parent directory where the snapshot should
> + * be created.
> + * @name: Name of the snapshot to create.
> + * @flags: See btrfs_util_create_snapshot().
> + * @async_transid: See btrfs_util_create_snapshot().
> + * @qgroup_inherit: See btrfs_util_create_snapshot().
> + */
> +enum btrfs_util_error btrfs_util_f2_create_snapshot(int fd, int parent_fd,
> +						    const char *name, int flags,
> +						    uint64_t *async_transid,
> +						    struct btrfs_util_qgroup_inherit *qgroup_inherit);
> +
>  struct btrfs_util_subvolume_iterator;
>  
>  /**
> diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
> index a9c15219..d552e416 100644
> --- a/libbtrfsutil/python/btrfsutilpy.h
> +++ b/libbtrfsutil/python/btrfsutilpy.h
> @@ -70,6 +70,7 @@ PyObject *set_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds
>  PyObject *get_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
>  PyObject *set_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
>  PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
> +PyObject *create_snapshot(PyObject *self, PyObject *args, PyObject *kwds);
>  
>  void add_module_constants(PyObject *m);
>  
> diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
> index daf0747f..d8f797cb 100644
> --- a/libbtrfsutil/python/module.c
> +++ b/libbtrfsutil/python/module.c
> @@ -195,6 +195,17 @@ static PyMethodDef btrfsutil_methods[] = {
>  	 "path -- string, bytes, or path-like object\n"
>  	 "async -- create the subvolume without waiting for it to commit to\n"
>  	 "disk and return the transaction ID"},
> +	{"create_snapshot", (PyCFunction)create_snapshot,
> +	 METH_VARARGS | METH_KEYWORDS,
> +	 "create_snapshot(source, path, recursive=False, read_only=False, async=False)\n\n"
> +	 "Create a new snapshot.\n\n"
> +	 "Arguments:\n"
> +	 "source -- string, bytes, path-like object, or open file descriptor\n"
> +	 "path -- string, bytes, or path-like object\n"
> +	 "recursive -- also snapshot child subvolumes\n"
> +	 "read_only -- create a read-only snapshot\n"
> +	 "async -- create the subvolume without waiting for it to commit to\n"
> +	 "disk and return the transaction ID"},
>  	{},
>  };
>  
> diff --git a/libbtrfsutil/python/subvolume.c b/libbtrfsutil/python/subvolume.c
> index 23163176..763a06d9 100644
> --- a/libbtrfsutil/python/subvolume.c
> +++ b/libbtrfsutil/python/subvolume.c
> @@ -347,6 +347,55 @@ PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
>  		Py_RETURN_NONE;
>  }
>  
> +PyObject *create_snapshot(PyObject *self, PyObject *args, PyObject *kwds)
> +{
> +	static char *keywords[] = {
> +		"source", "path", "recursive", "read_only", "async",
> +		"qgroup_inherit", NULL,
> +	};
> +	struct path_arg src = {.allow_fd = true}, dst = {.allow_fd = false};
> +	enum btrfs_util_error err;
> +	int recursive = 0, read_only = 0, async = 0;
> +	int flags = 0;
> +	QgroupInherit *inherit = NULL;
> +	uint64_t transid;
> +
> +	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O&|pppO!:create_snapshot",
> +					 keywords, &path_converter, &src,
> +					 &path_converter, &dst, &recursive,
> +					 &read_only, &async,
> +					 &QgroupInherit_type, &inherit))
> +		return NULL;
> +
> +	if (recursive)
> +		flags |= BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE;
> +	if (read_only)
> +		flags |= BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY;
> +
> +	if (src.path) {
> +		err = btrfs_util_create_snapshot(src.path, dst.path, flags,
> +						 async ? &transid : NULL,
> +						 inherit ? inherit->inherit : NULL);
> +	} else {
> +		err = btrfs_util_f_create_snapshot(src.fd, dst.path, flags,
> +						   async ? &transid : NULL,
> +						   inherit ? inherit->inherit : NULL);
> +	}
> +	if (err) {
> +		SetFromBtrfsUtilErrorWithPaths(err, &src, &dst);
> +		path_cleanup(&src);
> +		path_cleanup(&dst);
> +		return NULL;
> +	}
> +
> +	path_cleanup(&src);
> +	path_cleanup(&dst);
> +	if (async)
> +		return PyLong_FromUnsignedLongLong(transid);
> +	else
> +		Py_RETURN_NONE;
> +}
> +
>  typedef struct {
>  	PyObject_HEAD
>  	struct btrfs_util_subvolume_iterator *iter;
> diff --git a/libbtrfsutil/python/tests/test_qgroup.py b/libbtrfsutil/python/tests/test_qgroup.py
> index 19e6b05a..74fc46b6 100644
> --- a/libbtrfsutil/python/tests/test_qgroup.py
> +++ b/libbtrfsutil/python/tests/test_qgroup.py
> @@ -31,6 +31,16 @@ class TestQgroup(BtrfsTestCase):
>  
>          btrfsutil.create_subvolume(subvol, qgroup_inherit=inherit)
>  
> +    def test_snapshot_inherit(self):
> +        subvol = os.path.join(self.mountpoint, 'subvol')
> +        snapshot = os.path.join(self.mountpoint, 'snapshot')
> +
> +        inherit = btrfsutil.QgroupInherit()
> +        inherit.add_group(5)
> +
> +        btrfsutil.create_subvolume(subvol)
> +        btrfsutil.create_snapshot(subvol, snapshot, qgroup_inherit=inherit)
> +
>  
>  class TestQgroupInherit(unittest.TestCase):
>      def test_new(self):
> diff --git a/libbtrfsutil/python/tests/test_subvolume.py b/libbtrfsutil/python/tests/test_subvolume.py
> index b43beca7..2951154e 100644
> --- a/libbtrfsutil/python/tests/test_subvolume.py
> +++ b/libbtrfsutil/python/tests/test_subvolume.py
> @@ -129,6 +129,13 @@ class TestSubvolume(BtrfsTestCase):
>          self.assertEqual(info.stime, 0)
>          self.assertEqual(info.rtime, 0)
>  
> +        subvol_uuid = info.uuid
> +        snapshot = os.path.join(self.mountpoint, 'snapshot')
> +        btrfsutil.create_snapshot(subvol, snapshot)
> +
> +        info = btrfsutil.subvolume_info(snapshot)
> +        self.assertEqual(info.parent_uuid, subvol_uuid)
> +
>          # TODO: test received_uuid, stransid, rtransid, stime, and rtime
>  
>          for arg in self.path_or_fd(self.mountpoint):
> @@ -215,6 +222,54 @@ class TestSubvolume(BtrfsTestCase):
>          self.assertTrue(os.WIFEXITED(wstatus))
>          self.assertEqual(os.WEXITSTATUS(wstatus), 0)
>  
> +    def test_create_snapshot(self):
> +        subvol = os.path.join(self.mountpoint, 'subvol')
> +
> +        btrfsutil.create_subvolume(subvol)
> +        os.mkdir(os.path.join(subvol, 'dir'))
> +
> +        for i, arg in enumerate(self.path_or_fd(subvol)):
> +            with self.subTest(type=type(arg)):
> +                snapshots_dir = os.path.join(self.mountpoint, 'snapshots{}'.format(i))
> +                os.mkdir(snapshots_dir)
> +                snapshot = os.path.join(snapshots_dir, 'snapshot')
> +
> +                btrfsutil.create_snapshot(subvol, snapshot + '1')
> +                self.assertTrue(btrfsutil.is_subvolume(snapshot + '1'))
> +                self.assertTrue(os.path.exists(os.path.join(snapshot + '1', 'dir')))
> +
> +                btrfsutil.create_snapshot(subvol, (snapshot + '2').encode())
> +                self.assertTrue(btrfsutil.is_subvolume(snapshot + '2'))
> +                self.assertTrue(os.path.exists(os.path.join(snapshot + '2', 'dir')))
> +
> +                if HAVE_PATH_LIKE:
> +                    btrfsutil.create_snapshot(subvol, PurePath(snapshot + '3'))
> +                    self.assertTrue(btrfsutil.is_subvolume(snapshot + '3'))
> +                    self.assertTrue(os.path.exists(os.path.join(snapshot + '3', 'dir')))
> +
> +        nested_subvol = os.path.join(subvol, 'nested')
> +        more_nested_subvol = os.path.join(nested_subvol, 'more_nested')
> +        btrfsutil.create_subvolume(nested_subvol)
> +        btrfsutil.create_subvolume(more_nested_subvol)
> +        os.mkdir(os.path.join(more_nested_subvol, 'nested_dir'))
> +
> +        snapshot = os.path.join(self.mountpoint, 'snapshot')
> +
> +        btrfsutil.create_snapshot(subvol, snapshot + '1')
> +        # Dummy subvolume.
> +        self.assertEqual(os.stat(os.path.join(snapshot + '1', 'nested')).st_ino, 2)
> +        self.assertFalse(os.path.exists(os.path.join(snapshot + '1', 'nested', 'more_nested')))
> +
> +        btrfsutil.create_snapshot(subvol, snapshot + '2', recursive=True)
> +        self.assertTrue(os.path.exists(os.path.join(snapshot + '2', 'nested/more_nested/nested_dir')))
> +
> +        transid = btrfsutil.create_snapshot(subvol, snapshot + '3', recursive=True, async=True)
> +        self.assertTrue(os.path.exists(os.path.join(snapshot + '3', 'nested/more_nested/nested_dir')))
> +        self.assertGreater(transid, 0)
> +
> +        btrfsutil.create_snapshot(subvol, snapshot + '4', read_only=True)
> +        self.assertTrue(btrfsutil.get_subvolume_read_only(snapshot + '4'))
> +
>      def test_subvolume_iterator(self):
>          pwd = os.getcwd()
>          try:
> diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c
> index 2fb4e59f..27f64657 100644
> --- a/libbtrfsutil/subvolume.c
> +++ b/libbtrfsutil/subvolume.c
> @@ -856,6 +856,172 @@ out_iter:
>  	return err;
>  }
>  
> +static enum btrfs_util_error snapshot_subvolume_children(int fd, int parent_fd,
> +							 const char *name,
> +							 uint64_t *async_transid)
> +{
> +	struct btrfs_util_subvolume_iterator *iter;
> +	enum btrfs_util_error err;
> +	int dstfd;
> +
> +	dstfd = openat(parent_fd, name, O_RDONLY);
> +	if (dstfd == -1)
> +		return BTRFS_UTIL_ERROR_OPEN_FAILED;
> +
> +	err = btrfs_util_f_create_subvolume_iterator(fd, 0, 0, &iter);
> +	if (err)
> +		goto out;
> +
> +	for (;;) {
> +		char child_name[BTRFS_SUBVOL_NAME_MAX + 1];
> +		char *child_path;
> +		int child_fd, new_parent_fd;
> +		uint64_t tmp_transid;
> +
> +		err = btrfs_util_subvolume_iterator_next(iter, &child_path,
> +							 NULL);
> +		if (err) {
> +			if (err == BTRFS_UTIL_ERROR_STOP_ITERATION)
> +				err = BTRFS_UTIL_OK;
> +			break;
> +		}
> +
> +		/* Remove the placeholder directory. */
> +		if (unlinkat(dstfd, child_path, AT_REMOVEDIR) == -1) {
> +			free(child_path);
> +			err = BTRFS_UTIL_ERROR_RMDIR_FAILED;
> +			break;
> +		}
> +
> +		child_fd = openat(fd, child_path, O_RDONLY);
> +		if (child_fd == -1) {
> +			free(child_path);
> +			err = BTRFS_UTIL_ERROR_OPEN_FAILED;
> +			break;
> +		}
> +
> +		err = openat_parent_and_name(dstfd, child_path, child_name,
> +					     sizeof(child_name),
> +					     &new_parent_fd);
> +		free(child_path);
> +		if (err) {
> +			SAVE_ERRNO_AND_CLOSE(child_fd);
> +			break;
> +		}
> +
> +		err = btrfs_util_f2_create_snapshot(child_fd, new_parent_fd,
> +						    child_name, 0,
> +						    async_transid ? &tmp_transid : NULL,
> +						    NULL);
> +		SAVE_ERRNO_AND_CLOSE(child_fd);
> +		SAVE_ERRNO_AND_CLOSE(new_parent_fd);
> +		if (err)
> +			break;
> +		if (async_transid && tmp_transid > *async_transid)
> +			*async_transid = tmp_transid;
> +	}
> +
> +	btrfs_util_destroy_subvolume_iterator(iter);
> +out:
> +	SAVE_ERRNO_AND_CLOSE(dstfd);
> +	return err;
> +}
> +
> +__attribute__((visibility("default")))
> +enum btrfs_util_error btrfs_util_create_snapshot(const char *source,
> +						 const char *path, int flags,
> +						 uint64_t *async_transid,
> +						 struct btrfs_util_qgroup_inherit *qgroup_inherit)
> +{
> +	enum btrfs_util_error err;
> +	int fd;
> +
> +	fd = open(source, O_RDONLY);
> +	if (fd == -1)
> +		return BTRFS_UTIL_ERROR_OPEN_FAILED;
> +
> +	err = btrfs_util_f_create_snapshot(fd, path, flags, async_transid,
> +					   qgroup_inherit);
> +	SAVE_ERRNO_AND_CLOSE(fd);
> +	return err;
> +}
> +
> +__attribute__((visibility("default")))
> +enum btrfs_util_error btrfs_util_f_create_snapshot(int fd, const char *path,
> +						   int flags,
> +						   uint64_t *async_transid,
> +						   struct btrfs_util_qgroup_inherit *qgroup_inherit)
> +{
> +	char name[BTRFS_SUBVOL_NAME_MAX + 1];
> +	enum btrfs_util_error err;
> +	int parent_fd;
> +
> +	err = openat_parent_and_name(AT_FDCWD, path, name, sizeof(name),
> +				     &parent_fd);
> +	if (err)
> +		return err;
> +
> +	err = btrfs_util_f2_create_snapshot(fd, parent_fd, name, flags,
> +					    async_transid, qgroup_inherit);
> +	SAVE_ERRNO_AND_CLOSE(parent_fd);
> +	return err;
> +}
> +
> +__attribute__((visibility("default")))
> +enum btrfs_util_error btrfs_util_f2_create_snapshot(int fd, int parent_fd,
> +						    const char *name, int flags,
> +						    uint64_t *async_transid,
> +						    struct btrfs_util_qgroup_inherit *qgroup_inherit)
> +{
> +	struct btrfs_ioctl_vol_args_v2 args = {.fd = fd};
> +	enum btrfs_util_error err;
> +	size_t len;
> +	int ret;
> +
> +	if ((flags & ~BTRFS_UTIL_CREATE_SNAPSHOT_MASK) ||
> +	    ((flags & BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY) &&
> +	     (flags & BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE))) {
> +		errno = EINVAL;
> +		return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
> +	}
> +
> +	if (flags & BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY)
> +		args.flags |= BTRFS_SUBVOL_RDONLY;
> +	if (async_transid)
> +		args.flags |= BTRFS_SUBVOL_CREATE_ASYNC;
> +	if (qgroup_inherit) {
> +		args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
> +		args.qgroup_inherit = (struct btrfs_qgroup_inherit *)qgroup_inherit;
> +		args.size = (sizeof(*args.qgroup_inherit) +
> +			     args.qgroup_inherit->num_qgroups *
> +			     sizeof(args.qgroup_inherit->qgroups[0]));
> +	}
> +
> +	len = strlen(name);
> +	if (len >= sizeof(args.name)) {
> +		errno = ENAMETOOLONG;
> +		return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
> +	}
> +	memcpy(args.name, name, len);
> +	args.name[len] = '\0';
> +
> +	ret = ioctl(parent_fd, BTRFS_IOC_SNAP_CREATE_V2, &args);
> +	if (ret == -1)
> +		return BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED;
> +
> +	if (async_transid)
> +		*async_transid = args.transid;
> +
> +	if (flags & BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE) {
> +		err = snapshot_subvolume_children(fd, parent_fd, name,
> +						  async_transid);
> +		if (err)
> +			return err;
> +	}
> +
> +	return BTRFS_UTIL_OK;
> +}
> +
>  __attribute__((visibility("default")))
>  void btrfs_util_destroy_subvolume_iterator(struct btrfs_util_subvolume_iterator *iter)
>  {
> 


-- 
gpg @keyserver.linux.it: Goffredo Baroncelli <kreijackATinwind.it>
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

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

* Re: [PATCH 11/26] libbtrfsutil: add btrfs_util_create_snapshot()
  2018-01-26 19:31   ` Goffredo Baroncelli
@ 2018-01-26 19:46     ` Omar Sandoval
  2018-01-27  5:00       ` Qu Wenruo
  0 siblings, 1 reply; 47+ messages in thread
From: Omar Sandoval @ 2018-01-26 19:46 UTC (permalink / raw)
  To: kreijack; +Cc: linux-btrfs, kernel-team

On Fri, Jan 26, 2018 at 08:31:06PM +0100, Goffredo Baroncelli wrote:
> On 01/26/2018 07:40 PM, Omar Sandoval wrote:
> > From: Omar Sandoval <osandov@fb.com>
> 
> 
> Hi,
> 
> this is a great work; only few comments:
> 1) I found not intuitive the naming of the function: i.e. you have 
> 
> btrfs_util_create_snapshot()
> btrfs_util_f_create_snapshot()
> 
> To me it seems more clear to have
> 
> btrfs_util_create_snapshot()
> btrfs_util_create_snapshot_f()
> 
> I think that it is better move the 'f' at the end: at the begin you have the library "btrfs_util", in the middle you have the library function 'create_snapshot', at the end there is the function variant ('f', because it uses a file descriptor).
> 
> This is my opinion, even tough there are both examples like you (stat/fstat/lstat) and like my one (capt_get_fd/cap_get_file)...

Yup, I was going off of the fstat/fsync/etc. convention. I don't
particularly like, e.g., btrfs_create_snapshot_f(), but
btrfs_create_snapshot_fd() isn't so bad.

> 2) I find the prefix 'btrfs_util_' a bit verbose. Why not a simple 'btrfs_', even at the cost of a possible renaming of the conflicting function in the current btrfs code.

That's a reasonable idea, I mostly wanted to avoid naming conflicts but
if this is the "one true Btrfs library" it shouldn't be a concern.

I'll wait a bit for people to bikeshed on the naming before I go and
rename everything, but I'm leaning towards the shorter name and
appending _fd instead of prepending f_.

> 3) regarding the btrfs_util_create_snapshot() function, I think that it would be useful to add some more information:
> a) if used recursive is NOT atomic
> b) if used recursive, root capabilities are needed
> 
> The same for the other functions: mark with a 'root required' tag all the functions which require the root capabilities.

That's a great point, I'll document that.

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

* Re: [PATCH 01/26] btrfs-progs: get rid of undocumented qgroup inheritance options
  2018-01-26 18:40 ` [PATCH 01/26] btrfs-progs: get rid of undocumented qgroup inheritance options Omar Sandoval
@ 2018-01-27  4:35   ` Qu Wenruo
  0 siblings, 0 replies; 47+ messages in thread
From: Qu Wenruo @ 2018-01-27  4:35 UTC (permalink / raw)
  To: Omar Sandoval, linux-btrfs; +Cc: kernel-team


[-- Attachment #1.1: Type: text/plain, Size: 3966 bytes --]



On 2018年01月27日 02:40, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>
> 
> The -c option to subvol create and the -c and -x options to subvol
> snapshot have never been documented (and -x doesn't actually work
> because it's not in the getopt option string). The functionality is
> dubious and the kernel interface is being removed, so get rid of these.
> 

Similar patch is already submitted.

https://patchwork.kernel.org/patch/10125007/

Thanks,
Qu

> Signed-off-by: Omar Sandoval <osandov@fb.com>
> ---
>  cmds-subvolume.c | 25 ++-----------------------
>  qgroup.c         | 42 ------------------------------------------
>  qgroup.h         |  2 --
>  3 files changed, 2 insertions(+), 67 deletions(-)
> 
> diff --git a/cmds-subvolume.c b/cmds-subvolume.c
> index dc626a64..f8e34bc8 100644
> --- a/cmds-subvolume.c
> +++ b/cmds-subvolume.c
> @@ -129,18 +129,11 @@ static int cmd_subvol_create(int argc, char **argv)
>  	DIR	*dirstream = NULL;
>  
>  	while (1) {
> -		int c = getopt(argc, argv, "c:i:");
> +		int c = getopt(argc, argv, "i:");
>  		if (c < 0)
>  			break;
>  
>  		switch (c) {
> -		case 'c':
> -			res = qgroup_inherit_add_copy(&inherit, optarg, 0);
> -			if (res) {
> -				retval = res;
> -				goto out;
> -			}
> -			break;
>  		case 'i':
>  			res = qgroup_inherit_add_group(&inherit, optarg);
>  			if (res) {
> @@ -665,18 +658,11 @@ static int cmd_subvol_snapshot(int argc, char **argv)
>  
>  	memset(&args, 0, sizeof(args));
>  	while (1) {
> -		int c = getopt(argc, argv, "c:i:r");
> +		int c = getopt(argc, argv, "i:r");
>  		if (c < 0)
>  			break;
>  
>  		switch (c) {
> -		case 'c':
> -			res = qgroup_inherit_add_copy(&inherit, optarg, 0);
> -			if (res) {
> -				retval = res;
> -				goto out;
> -			}
> -			break;
>  		case 'i':
>  			res = qgroup_inherit_add_group(&inherit, optarg);
>  			if (res) {
> @@ -687,13 +673,6 @@ static int cmd_subvol_snapshot(int argc, char **argv)
>  		case 'r':
>  			readonly = 1;
>  			break;
> -		case 'x':
> -			res = qgroup_inherit_add_copy(&inherit, optarg, 1);
> -			if (res) {
> -				retval = res;
> -				goto out;
> -			}
> -			break;
>  		default:
>  			usage(cmd_subvol_snapshot_usage);
>  		}
> diff --git a/qgroup.c b/qgroup.c
> index 156825fd..e9158a26 100644
> --- a/qgroup.c
> +++ b/qgroup.c
> @@ -1310,45 +1310,3 @@ int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg)
>  
>  	return 0;
>  }
> -
> -int qgroup_inherit_add_copy(struct btrfs_qgroup_inherit **inherit, char *arg,
> -			    int type)
> -{
> -	int ret;
> -	u64 qgroup_src;
> -	u64 qgroup_dst;
> -	char *p;
> -	int pos = 0;
> -
> -	p = strchr(arg, ':');
> -	if (!p) {
> -bad:
> -		error("invalid copy specification, missing separator :");
> -		return -EINVAL;
> -	}
> -	*p = 0;
> -	qgroup_src = parse_qgroupid(arg);
> -	qgroup_dst = parse_qgroupid(p + 1);
> -	*p = ':';
> -
> -	if (!qgroup_src || !qgroup_dst)
> -		goto bad;
> -
> -	if (*inherit)
> -		pos = (*inherit)->num_qgroups +
> -		      (*inherit)->num_ref_copies * 2 * type;
> -
> -	ret = qgroup_inherit_realloc(inherit, 2, pos);
> -	if (ret)
> -		return ret;
> -
> -	(*inherit)->qgroups[pos++] = qgroup_src;
> -	(*inherit)->qgroups[pos++] = qgroup_dst;
> -
> -	if (!type)
> -		++(*inherit)->num_ref_copies;
> -	else
> -		++(*inherit)->num_excl_copies;
> -
> -	return 0;
> -}
> diff --git a/qgroup.h b/qgroup.h
> index 875fbdf3..bb6610d7 100644
> --- a/qgroup.h
> +++ b/qgroup.h
> @@ -92,7 +92,5 @@ int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set **comp_set,
>  				int is_descending);
>  int qgroup_inherit_size(struct btrfs_qgroup_inherit *p);
>  int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg);
> -int qgroup_inherit_add_copy(struct btrfs_qgroup_inherit **inherit, char *arg,
> -			    int type);
>  
>  #endif
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 520 bytes --]

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

* Re: [PATCH 11/26] libbtrfsutil: add btrfs_util_create_snapshot()
  2018-01-26 19:46     ` Omar Sandoval
@ 2018-01-27  5:00       ` Qu Wenruo
  2018-01-27  5:45         ` Omar Sandoval
  0 siblings, 1 reply; 47+ messages in thread
From: Qu Wenruo @ 2018-01-27  5:00 UTC (permalink / raw)
  To: Omar Sandoval, kreijack; +Cc: linux-btrfs, kernel-team


[-- Attachment #1.1: Type: text/plain, Size: 2716 bytes --]



On 2018年01月27日 03:46, Omar Sandoval wrote:
> On Fri, Jan 26, 2018 at 08:31:06PM +0100, Goffredo Baroncelli wrote:
>> On 01/26/2018 07:40 PM, Omar Sandoval wrote:
>>> From: Omar Sandoval <osandov@fb.com>
>>
>>
>> Hi,
>>
>> this is a great work; only few comments:
>> 1) I found not intuitive the naming of the function: i.e. you have 
>>
>> btrfs_util_create_snapshot()
>> btrfs_util_f_create_snapshot()
>>
>> To me it seems more clear to have
>>
>> btrfs_util_create_snapshot()
>> btrfs_util_create_snapshot_f()
>>
>> I think that it is better move the 'f' at the end: at the begin you have the library "btrfs_util", in the middle you have the library function 'create_snapshot', at the end there is the function variant ('f', because it uses a file descriptor).
>>
>> This is my opinion, even tough there are both examples like you (stat/fstat/lstat) and like my one (capt_get_fd/cap_get_file)...
> 
> Yup, I was going off of the fstat/fsync/etc. convention. I don't
> particularly like, e.g., btrfs_create_snapshot_f(), but
> btrfs_create_snapshot_fd() isn't so bad.

_fd() suffix sounds more reasonable to me too.

> 
>> 2) I find the prefix 'btrfs_util_' a bit verbose. Why not a simple 'btrfs_', even at the cost of a possible renaming of the conflicting function in the current btrfs code.
> 
> That's a reasonable idea, I mostly wanted to avoid naming conflicts but
> if this is the "one true Btrfs library" it shouldn't be a concern.

Unfortunately, at least there is also some planned work to bring a
shared code base between kernel and btrfs-progs, which is also named
libbtrfs, inspired by libxfs.

And depending on the respect of view, some developer may prefer the
short btrfs_ prefix for libbtrfs, while other developers/users will
definitely prefer btrfs_ prefix for libbtrfsutil.

What about shorted prefix like butil_ or btrutil_?

Thanks,
Qu

> 
> I'll wait a bit for people to bikeshed on the naming before I go and
> rename everything, but I'm leaning towards the shorter name and
> appending _fd instead of prepending f_.
> 
>> 3) regarding the btrfs_util_create_snapshot() function, I think that it would be useful to add some more information:
>> a) if used recursive is NOT atomic
>> b) if used recursive, root capabilities are needed
>>
>> The same for the other functions: mark with a 'root required' tag all the functions which require the root capabilities.
> 
> That's a great point, I'll document that.
> --
> 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
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 520 bytes --]

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

* Re: [PATCH 11/26] libbtrfsutil: add btrfs_util_create_snapshot()
  2018-01-27  5:00       ` Qu Wenruo
@ 2018-01-27  5:45         ` Omar Sandoval
  2018-01-27 14:19           ` Goffredo Baroncelli
  2018-01-27 16:31           ` Nikolay Borisov
  0 siblings, 2 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-27  5:45 UTC (permalink / raw)
  To: Qu Wenruo; +Cc: kreijack, linux-btrfs, kernel-team

On Sat, Jan 27, 2018 at 01:00:58PM +0800, Qu Wenruo wrote:
> 
> 
> On 2018年01月27日 03:46, Omar Sandoval wrote:
> > On Fri, Jan 26, 2018 at 08:31:06PM +0100, Goffredo Baroncelli wrote:
> >> On 01/26/2018 07:40 PM, Omar Sandoval wrote:
> >>> From: Omar Sandoval <osandov@fb.com>
> >>
> >>
> >> Hi,
> >>
> >> this is a great work; only few comments:
> >> 1) I found not intuitive the naming of the function: i.e. you have 
> >>
> >> btrfs_util_create_snapshot()
> >> btrfs_util_f_create_snapshot()
> >>
> >> To me it seems more clear to have
> >>
> >> btrfs_util_create_snapshot()
> >> btrfs_util_create_snapshot_f()
> >>
> >> I think that it is better move the 'f' at the end: at the begin you have the library "btrfs_util", in the middle you have the library function 'create_snapshot', at the end there is the function variant ('f', because it uses a file descriptor).
> >>
> >> This is my opinion, even tough there are both examples like you (stat/fstat/lstat) and like my one (capt_get_fd/cap_get_file)...
> > 
> > Yup, I was going off of the fstat/fsync/etc. convention. I don't
> > particularly like, e.g., btrfs_create_snapshot_f(), but
> > btrfs_create_snapshot_fd() isn't so bad.
> 
> _fd() suffix sounds more reasonable to me too.
> 
> > 
> >> 2) I find the prefix 'btrfs_util_' a bit verbose. Why not a simple 'btrfs_', even at the cost of a possible renaming of the conflicting function in the current btrfs code.
> > 
> > That's a reasonable idea, I mostly wanted to avoid naming conflicts but
> > if this is the "one true Btrfs library" it shouldn't be a concern.
> 
> Unfortunately, at least there is also some planned work to bring a
> shared code base between kernel and btrfs-progs, which is also named
> libbtrfs, inspired by libxfs.

That's right, I forgot about that. There's definitely value in having a
distinction between btrfs_util_ (userspace interfaces) and btrfs_
(filesystem disk format).

> And depending on the respect of view, some developer may prefer the
> short btrfs_ prefix for libbtrfs, while other developers/users will
> definitely prefer btrfs_ prefix for libbtrfsutil.
> 
> What about shorted prefix like butil_ or btrutil_?

Those aren't very informative, I think sticking with btrfs_util_ is
fine, it's not that bad to type out.

> Thanks,
> Qu
> 
> > 
> > I'll wait a bit for people to bikeshed on the naming before I go and
> > rename everything, but I'm leaning towards the shorter name and
> > appending _fd instead of prepending f_.
> > 
> >> 3) regarding the btrfs_util_create_snapshot() function, I think that it would be useful to add some more information:
> >> a) if used recursive is NOT atomic
> >> b) if used recursive, root capabilities are needed
> >>
> >> The same for the other functions: mark with a 'root required' tag all the functions which require the root capabilities.
> > 
> > That's a great point, I'll document that.
> > --
> > 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] 47+ messages in thread

* Re: [PATCH 11/26] libbtrfsutil: add btrfs_util_create_snapshot()
  2018-01-27  5:45         ` Omar Sandoval
@ 2018-01-27 14:19           ` Goffredo Baroncelli
  2018-01-27 16:31           ` Nikolay Borisov
  1 sibling, 0 replies; 47+ messages in thread
From: Goffredo Baroncelli @ 2018-01-27 14:19 UTC (permalink / raw)
  To: Omar Sandoval, Qu Wenruo; +Cc: linux-btrfs, kernel-team

On 01/27/2018 06:45 AM, Omar Sandoval wrote:
> On Sat, Jan 27, 2018 at 01:00:58PM +0800, Qu Wenruo wrote:
>>
>>
>> On 2018年01月27日 03:46, Omar Sandoval wrote:
>>> On Fri, Jan 26, 2018 at 08:31:06PM +0100, Goffredo Baroncelli wrote:
>>>> On 01/26/2018 07:40 PM, Omar Sandoval wrote:
>>>>> From: Omar Sandoval <osandov@fb.com>
[...]
>> Unfortunately, at least there is also some planned work to bring a
>> shared code base between kernel and btrfs-progs, which is also named
>> libbtrfs, inspired by libxfs.
> 
> That's right, I forgot about that. There's definitely value in having a
> distinction between btrfs_util_ (userspace interfaces) and btrfs_
> (filesystem disk format).
> 
>> And depending on the respect of view, some developer may prefer the
>> short btrfs_ prefix for libbtrfs, while other developers/users will
>> definitely prefer btrfs_ prefix for libbtrfsutil.
>>
>> What about shorted prefix like butil_ or btrutil_?
> 
> Those aren't very informative, I think sticking with btrfs_util_ is
> fine, it's not that bad to type out.

Let me another chance: what about 'ubtrfs_'...



> 
>> Thanks,
>> Qu
>>
>>>
>>> I'll wait a bit for people to bikeshed on the naming before I go and
>>> rename everything, but I'm leaning towards the shorter name and
>>> appending _fd instead of prepending f_.
>>>
>>>> 3) regarding the btrfs_util_create_snapshot() function, I think that it would be useful to add some more information:
>>>> a) if used recursive is NOT atomic
>>>> b) if used recursive, root capabilities are needed
>>>>
>>>> The same for the other functions: mark with a 'root required' tag all the functions which require the root capabilities.
>>>
>>> That's a great point, I'll document that.
>>> --
>>> 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
>>>
>>
> 
> 
> 
> 


-- 
gpg @keyserver.linux.it: Goffredo Baroncelli <kreijackATinwind.it>
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

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

* Re: [PATCH 11/26] libbtrfsutil: add btrfs_util_create_snapshot()
  2018-01-27  5:45         ` Omar Sandoval
  2018-01-27 14:19           ` Goffredo Baroncelli
@ 2018-01-27 16:31           ` Nikolay Borisov
  2018-01-27 18:54             ` Omar Sandoval
  1 sibling, 1 reply; 47+ messages in thread
From: Nikolay Borisov @ 2018-01-27 16:31 UTC (permalink / raw)
  To: Omar Sandoval, Qu Wenruo; +Cc: kreijack, linux-btrfs, kernel-team



On 27.01.2018 07:45, Omar Sandoval wrote:
> On Sat, Jan 27, 2018 at 01:00:58PM +0800, Qu Wenruo wrote:
>>
>>
>> On 2018年01月27日 03:46, Omar Sandoval wrote:
>>> On Fri, Jan 26, 2018 at 08:31:06PM +0100, Goffredo Baroncelli wrote:
>>>> On 01/26/2018 07:40 PM, Omar Sandoval wrote:
>>>>> From: Omar Sandoval <osandov@fb.com>
>>>>
>>>>
>>>> Hi,
>>>>
>>>> this is a great work; only few comments:
>>>> 1) I found not intuitive the naming of the function: i.e. you have 
>>>>
>>>> btrfs_util_create_snapshot()
>>>> btrfs_util_f_create_snapshot()
>>>>
>>>> To me it seems more clear to have
>>>>
>>>> btrfs_util_create_snapshot()
>>>> btrfs_util_create_snapshot_f()
>>>>
>>>> I think that it is better move the 'f' at the end: at the begin you have the library "btrfs_util", in the middle you have the library function 'create_snapshot', at the end there is the function variant ('f', because it uses a file descriptor).
>>>>
>>>> This is my opinion, even tough there are both examples like you (stat/fstat/lstat) and like my one (capt_get_fd/cap_get_file)...
>>>
>>> Yup, I was going off of the fstat/fsync/etc. convention. I don't
>>> particularly like, e.g., btrfs_create_snapshot_f(), but
>>> btrfs_create_snapshot_fd() isn't so bad.
>>
>> _fd() suffix sounds more reasonable to me too.
>>
>>>
>>>> 2) I find the prefix 'btrfs_util_' a bit verbose. Why not a simple 'btrfs_', even at the cost of a possible renaming of the conflicting function in the current btrfs code.
>>>
>>> That's a reasonable idea, I mostly wanted to avoid naming conflicts but
>>> if this is the "one true Btrfs library" it shouldn't be a concern.
>>
>> Unfortunately, at least there is also some planned work to bring a
>> shared code base between kernel and btrfs-progs, which is also named
>> libbtrfs, inspired by libxfs.

So why don't we implement the shared code as part of this library, won't
it result in just some more files + btrfs_* named function? This "core"
part will be built always when compiling the kernel/userspace progs. In
case we are compiling the userspace progs, additionally we can compile
in the libbtrfsutil essentially + the python modules? Why do we need to
keep the 2 libraries completely separate? For example we can have
something like:

libbtrfs/common/ - here lives the common Userspace/kernel code
libbtrfs/userspace/ - here lives the code of libbtrfsutil, which will
presumably consume certain function from the common dir?

> 
> That's right, I forgot about that. There's definitely value in having a
> distinction between btrfs_util_ (userspace interfaces) and btrfs_
> (filesystem disk format).
> 
>> And depending on the respect of view, some developer may prefer the
>> short btrfs_ prefix for libbtrfs, while other developers/users will
>> definitely prefer btrfs_ prefix for libbtrfsutil.
>>
>> What about shorted prefix like butil_ or btrutil_?
> 
> Those aren't very informative, I think sticking with btrfs_util_ is
> fine, it's not that bad to type out.
> 
>> Thanks,
>> Qu
>>
>>>
>>> I'll wait a bit for people to bikeshed on the naming before I go and
>>> rename everything, but I'm leaning towards the shorter name and
>>> appending _fd instead of prepending f_.
>>>
>>>> 3) regarding the btrfs_util_create_snapshot() function, I think that it would be useful to add some more information:
>>>> a) if used recursive is NOT atomic
>>>> b) if used recursive, root capabilities are needed
>>>>
>>>> The same for the other functions: mark with a 'root required' tag all the functions which require the root capabilities.
>>>
>>> That's a great point, I'll document that.
>>> --
>>> 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
>>>
>>
> 
> 
> 
> --
> 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] 47+ messages in thread

* Re: [PATCH 11/26] libbtrfsutil: add btrfs_util_create_snapshot()
  2018-01-27 16:31           ` Nikolay Borisov
@ 2018-01-27 18:54             ` Omar Sandoval
  0 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-27 18:54 UTC (permalink / raw)
  To: Nikolay Borisov; +Cc: Qu Wenruo, kreijack, linux-btrfs, kernel-team

On Sat, Jan 27, 2018 at 06:31:11PM +0200, Nikolay Borisov wrote:
> 
> 
> On 27.01.2018 07:45, Omar Sandoval wrote:
> > On Sat, Jan 27, 2018 at 01:00:58PM +0800, Qu Wenruo wrote:
> >>
> >>
> >> On 2018年01月27日 03:46, Omar Sandoval wrote:
> >>> On Fri, Jan 26, 2018 at 08:31:06PM +0100, Goffredo Baroncelli wrote:
> >>>> On 01/26/2018 07:40 PM, Omar Sandoval wrote:
> >>>>> From: Omar Sandoval <osandov@fb.com>
> >>>>
> >>>>
> >>>> Hi,
> >>>>
> >>>> this is a great work; only few comments:
> >>>> 1) I found not intuitive the naming of the function: i.e. you have 
> >>>>
> >>>> btrfs_util_create_snapshot()
> >>>> btrfs_util_f_create_snapshot()
> >>>>
> >>>> To me it seems more clear to have
> >>>>
> >>>> btrfs_util_create_snapshot()
> >>>> btrfs_util_create_snapshot_f()
> >>>>
> >>>> I think that it is better move the 'f' at the end: at the begin you have the library "btrfs_util", in the middle you have the library function 'create_snapshot', at the end there is the function variant ('f', because it uses a file descriptor).
> >>>>
> >>>> This is my opinion, even tough there are both examples like you (stat/fstat/lstat) and like my one (capt_get_fd/cap_get_file)...
> >>>
> >>> Yup, I was going off of the fstat/fsync/etc. convention. I don't
> >>> particularly like, e.g., btrfs_create_snapshot_f(), but
> >>> btrfs_create_snapshot_fd() isn't so bad.
> >>
> >> _fd() suffix sounds more reasonable to me too.
> >>
> >>>
> >>>> 2) I find the prefix 'btrfs_util_' a bit verbose. Why not a simple 'btrfs_', even at the cost of a possible renaming of the conflicting function in the current btrfs code.
> >>>
> >>> That's a reasonable idea, I mostly wanted to avoid naming conflicts but
> >>> if this is the "one true Btrfs library" it shouldn't be a concern.
> >>
> >> Unfortunately, at least there is also some planned work to bring a
> >> shared code base between kernel and btrfs-progs, which is also named
> >> libbtrfs, inspired by libxfs.
> 
> So why don't we implement the shared code as part of this library, won't
> it result in just some more files + btrfs_* named function? This "core"
> part will be built always when compiling the kernel/userspace progs. In
> case we are compiling the userspace progs, additionally we can compile
> in the libbtrfsutil essentially + the python modules? Why do we need to
> keep the 2 libraries completely separate? For example we can have
> something like:
> 
> libbtrfs/common/ - here lives the common Userspace/kernel code
> libbtrfs/userspace/ - here lives the code of libbtrfsutil, which will
> presumably consume certain function from the common dir?

The problem with this is that I really want libbtrfsutil to be LGPL, so
it has to be separate from the GPL filesystem code. Implementation-wise,
libbtrfsutil doesn't need to know anything about the filesystem format,
and I found it nice to avoid all of the kernel-isms, just writing
standard C. And the filesystem format code is fundamentally different
from the userspace interface code, so I think the split makes sense
regardless.

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

* Re: [PATCH 02/26] Add libbtrfsutil
  2018-01-26 18:40 ` [PATCH 02/26] Add libbtrfsutil Omar Sandoval
@ 2018-01-29  2:16   ` Qu Wenruo
  2018-01-29 23:45     ` Omar Sandoval
  0 siblings, 1 reply; 47+ messages in thread
From: Qu Wenruo @ 2018-01-29  2:16 UTC (permalink / raw)
  To: Omar Sandoval, linux-btrfs; +Cc: kernel-team


[-- Attachment #1.1: Type: text/plain, Size: 61487 bytes --]



On 2018年01月27日 02:40, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>
> 
> Currently, users wishing to manage Btrfs filesystems programatically
> have to shell out to btrfs-progs and parse the output. This isn't ideal.
> The goal of libbtrfsutil is to provide a library version of as many of
> the operations of btrfs-progs as possible and to migrate btrfs-progs to
> use it.
> 
> Rather than simply refactoring the existing btrfs-progs code, the code
> has to be written from scratch for a couple of reasons:
> 
> * A lot of the btrfs-progs code was not designed with a nice library API
>   in mind in terms of reusability, naming, and error reporting.
> * libbtrfsutil is licensed under the LGPL, whereas btrfs-progs is under
>   the GPL, which makes it dubious to directly copy or move the code.
> 
> Eventually, most of the low-level btrfs-progs code should either live in
> libbtrfsutil or the shared kernel/userspace filesystem code, and
> btrfs-progs will just be the CLI wrapper.
> 
> This first commit just includes the build system changes, license,
> README, and error reporting helper.
> 
> Signed-off-by: Omar Sandoval <osandov@fb.com>
> ---
>  .gitignore                  |   2 +
>  Makefile                    |  47 +--
>  libbtrfsutil/COPYING        | 674 ++++++++++++++++++++++++++++++++++++++++++++
>  libbtrfsutil/COPYING.LESSER | 165 +++++++++++
>  libbtrfsutil/README.md      |  32 +++
>  libbtrfsutil/btrfsutil.h    |  72 +++++
>  libbtrfsutil/errors.c       |  55 ++++
>  7 files changed, 1031 insertions(+), 16 deletions(-)
>  create mode 100644 libbtrfsutil/COPYING
>  create mode 100644 libbtrfsutil/COPYING.LESSER
>  create mode 100644 libbtrfsutil/README.md
>  create mode 100644 libbtrfsutil/btrfsutil.h
>  create mode 100644 libbtrfsutil/errors.c
> 
> diff --git a/.gitignore b/.gitignore
> index 8e607f6e..272d53e4 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -43,6 +43,8 @@ libbtrfs.so.0.1
>  library-test
>  library-test-static
>  /fssum
> +/libbtrfsutil.so*
> +/libbtrfsutil.a
>  
>  /tests/*-tests-results.txt
>  /tests/test-console.txt
> diff --git a/Makefile b/Makefile
> index 6369e8f4..062f7f3c 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -73,6 +73,7 @@ CFLAGS = $(SUBST_CFLAGS) \
>  	 -fPIC \
>  	 -I$(TOPDIR) \
>  	 -I$(TOPDIR)/kernel-lib \
> +	 -I$(TOPDIR)/libbtrfsutil \
>  	 $(EXTRAWARN_CFLAGS) \
>  	 $(DEBUG_CFLAGS_INTERNAL) \
>  	 $(EXTRA_CFLAGS)
> @@ -121,12 +122,14 @@ libbtrfs_headers = send-stream.h send-utils.h send.h kernel-lib/rbtree.h btrfs-l
>  	       kernel-lib/crc32c.h kernel-lib/list.h kerncompat.h \
>  	       kernel-lib/radix-tree.h kernel-lib/sizes.h kernel-lib/raid56.h \
>  	       extent-cache.h extent_io.h ioctl.h ctree.h btrfsck.h version.h
> +libbtrfsutil_version := 0.1
> +libbtrfsutil_objects = libbtrfsutil/errors.o
>  convert_objects = convert/main.o convert/common.o convert/source-fs.o \
>  		  convert/source-ext2.o convert/source-reiserfs.o
>  mkfs_objects = mkfs/main.o mkfs/common.o
>  image_objects = image/main.o image/sanitize.o
>  all_objects = $(objects) $(cmds_objects) $(libbtrfs_objects) $(convert_objects) \
> -	      $(mkfs_objects) $(image_objects)
> +	      $(mkfs_objects) $(image_objects) $(libbtrfsutil_objects)
>  
>  TESTS = fsck-tests.sh convert-tests.sh
>  
> @@ -248,10 +251,10 @@ static_convert_objects = $(patsubst %.o, %.static.o, $(convert_objects))
>  static_mkfs_objects = $(patsubst %.o, %.static.o, $(mkfs_objects))
>  static_image_objects = $(patsubst %.o, %.static.o, $(image_objects))
>  
> -libs_shared = libbtrfs.so.0.1
> -libs_static = libbtrfs.a
> +libs_shared = libbtrfs.so.0.1 libbtrfsutil.so.$(libbtrfsutil_version)
> +libs_static = libbtrfs.a libbtrfsutil.a
>  libs = $(libs_shared) $(libs_static)
> -lib_links = libbtrfs.so.0 libbtrfs.so
> +lib_links = libbtrfs.so.0 libbtrfs.so libbtrfsutil.so.0 libbtrfsutil.so
>  headers = $(libbtrfs_headers)
>  
>  # make C=1 to enable sparse
> @@ -289,7 +292,7 @@ endif
>  	$(Q)$(CC) $(STATIC_CFLAGS) -c $< -o $@ $($(subst -,_,$(@:%.static.o=%)-cflags)) \
>  		$($(subst -,_,btrfs-$(@:%/$(notdir $@)=%)-cflags))
>  
> -all: $(progs) libbtrfs $(BUILDDIRS)
> +all: $(progs) $(libs) $(lib_links) $(BUILDDIRS)
>  $(SUBDIRS): $(BUILDDIRS)
>  $(BUILDDIRS):
>  	@echo "Making all in $(patsubst build-%,%,$@)"
> @@ -353,20 +356,31 @@ kernel-lib/tables.c:
>  	@echo "    [TABLE]  $@"
>  	$(Q)./mktables > $@ || ($(RM) -f $@ && exit 1)
>  
> -libbtrfs: $(libs_shared) $(lib_links)
> +libbtrfs.so.0.1: $(libbtrfs_objects)
> +	@echo "    [LD]     $@"
> +	$(Q)$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LIBBTRFS_LIBS) \
> +		-shared -Wl,-soname,libbtrfs.so.0 -o $@
> +
> +libbtrfs.a: $(libbtrfs_objects)
> +	@echo "    [AR]     $@"
> +	$(Q)$(AR) cr $@ $^
> +
> +libbtrfs.so.0 libbtrfs.so: libbtrfs.so.0.1
> +	@echo "    [LN]     $@"
> +	$(Q)$(LN_S) -f $< $@
>  
> -$(libs_shared): $(libbtrfs_objects) $(lib_links) send.h
> +libbtrfsutil.so.$(libbtrfsutil_version): $(libbtrfsutil_objects)
>  	@echo "    [LD]     $@"
> -	$(Q)$(CC) $(CFLAGS) $(libbtrfs_objects) $(LDFLAGS) $(LIBBTRFS_LIBS) \
> -		-shared -Wl,-soname,libbtrfs.so.0 -o libbtrfs.so.0.1
> +	$(Q)$(CC) $(CFLAGS) $(libbtrfsutil_objects) \
> +		-shared -Wl,-soname,libbtrfsutil.so.0 -o $@
>  
> -$(libs_static): $(libbtrfs_objects)
> +libbtrfsutil.a: $(libbtrfsutil_objects)
>  	@echo "    [AR]     $@"
> -	$(Q)$(AR) cr libbtrfs.a $(libbtrfs_objects)
> +	$(Q)$(AR) cr $@ $^
>  
> -$(lib_links):
> +libbtrfsutil.so.0 libbtrfsutil.so: libbtrfsutil.so.$(libbtrfsutil_version)
>  	@echo "    [LN]     $@"
> -	$(Q)$(LN_S) -f libbtrfs.so.0.1 $@
> +	$(Q)$(LN_S) -f $< $@
>  
>  # keep intermediate files from the below implicit rules around
>  .PRECIOUS: $(addsuffix .o,$(progs))
> @@ -485,7 +499,7 @@ test-ioctl: ioctl-test ioctl-test-32 ioctl-test-64
>  	$(Q)./ioctl-test-32 > ioctl-test-32.log
>  	$(Q)./ioctl-test-64 > ioctl-test-64.log
>  
> -library-test: library-test.c $(libs_shared)
> +library-test: library-test.c libbtrfs.so
>  	@echo "    [TEST PREP]  $@"$(eval TMPD=$(shell mktemp -d))
>  	$(Q)mkdir -p $(TMPD)/include/btrfs && \
>  	cp $(libbtrfs_headers) $(TMPD)/include/btrfs && \
> @@ -549,7 +563,8 @@ clean: $(CLEANDIRS)
>                mktables btrfs.static mkfs.btrfs.static fssum \
>  	      $(check_defs) \
>  	      $(libs) $(lib_links) \
> -	      $(progs_static) $(progs_extra)
> +	      $(progs_static) $(progs_extra) \
> +	      libbtrfsutil/*.o libbtrfsutil/*.o.d
>  
>  clean-doc:
>  	@echo "Cleaning Documentation"
> @@ -568,7 +583,7 @@ $(CLEANDIRS):
>  	@echo "Cleaning $(patsubst clean-%,%,$@)"
>  	$(Q)$(MAKE) $(MAKEOPTS) -C $(patsubst clean-%,%,$@) clean
>  
> -install: $(libs) $(progs_install) $(INSTALLDIRS)
> +install: $(libs) $(libbtrfsutil_links) $(progs_install) $(INSTALLDIRS)
>  	$(INSTALL) -m755 -d $(DESTDIR)$(bindir)
>  	$(INSTALL) $(progs_install) $(DESTDIR)$(bindir)
>  	$(INSTALL) fsck.btrfs $(DESTDIR)$(bindir)
> diff --git a/libbtrfsutil/COPYING b/libbtrfsutil/COPYING
> new file mode 100644
> index 00000000..f288702d
> --- /dev/null
> +++ b/libbtrfsutil/COPYING
> @@ -0,0 +1,674 @@
> +                    GNU GENERAL PUBLIC LICENSE
> +                       Version 3, 29 June 2007
> +
> + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
> + Everyone is permitted to copy and distribute verbatim copies
> + of this license document, but changing it is not allowed.
> +
> +                            Preamble
> +
> +  The GNU General Public License is a free, copyleft license for
> +software and other kinds of works.
> +
> +  The licenses for most software and other practical works are designed
> +to take away your freedom to share and change the works.  By contrast,
> +the GNU General Public License is intended to guarantee your freedom to
> +share and change all versions of a program--to make sure it remains free
> +software for all its users.  We, the Free Software Foundation, use the
> +GNU General Public License for most of our software; it applies also to
> +any other work released this way by its authors.  You can apply it to
> +your programs, too.
> +
> +  When we speak of free software, we are referring to freedom, not
> +price.  Our General Public Licenses are designed to make sure that you
> +have the freedom to distribute copies of free software (and charge for
> +them if you wish), that you receive source code or can get it if you
> +want it, that you can change the software or use pieces of it in new
> +free programs, and that you know you can do these things.
> +
> +  To protect your rights, we need to prevent others from denying you
> +these rights or asking you to surrender the rights.  Therefore, you have
> +certain responsibilities if you distribute copies of the software, or if
> +you modify it: responsibilities to respect the freedom of others.
> +
> +  For example, if you distribute copies of such a program, whether
> +gratis or for a fee, you must pass on to the recipients the same
> +freedoms that you received.  You must make sure that they, too, receive
> +or can get the source code.  And you must show them these terms so they
> +know their rights.
> +
> +  Developers that use the GNU GPL protect your rights with two steps:
> +(1) assert copyright on the software, and (2) offer you this License
> +giving you legal permission to copy, distribute and/or modify it.
> +
> +  For the developers' and authors' protection, the GPL clearly explains
> +that there is no warranty for this free software.  For both users' and
> +authors' sake, the GPL requires that modified versions be marked as
> +changed, so that their problems will not be attributed erroneously to
> +authors of previous versions.
> +
> +  Some devices are designed to deny users access to install or run
> +modified versions of the software inside them, although the manufacturer
> +can do so.  This is fundamentally incompatible with the aim of
> +protecting users' freedom to change the software.  The systematic
> +pattern of such abuse occurs in the area of products for individuals to
> +use, which is precisely where it is most unacceptable.  Therefore, we
> +have designed this version of the GPL to prohibit the practice for those
> +products.  If such problems arise substantially in other domains, we
> +stand ready to extend this provision to those domains in future versions
> +of the GPL, as needed to protect the freedom of users.
> +
> +  Finally, every program is threatened constantly by software patents.
> +States should not allow patents to restrict development and use of
> +software on general-purpose computers, but in those that do, we wish to
> +avoid the special danger that patents applied to a free program could
> +make it effectively proprietary.  To prevent this, the GPL assures that
> +patents cannot be used to render the program non-free.
> +
> +  The precise terms and conditions for copying, distribution and
> +modification follow.
> +
> +                       TERMS AND CONDITIONS
> +
> +  0. Definitions.
> +
> +  "This License" refers to version 3 of the GNU General Public License.
> +
> +  "Copyright" also means copyright-like laws that apply to other kinds of
> +works, such as semiconductor masks.
> +
> +  "The Program" refers to any copyrightable work licensed under this
> +License.  Each licensee is addressed as "you".  "Licensees" and
> +"recipients" may be individuals or organizations.
> +
> +  To "modify" a work means to copy from or adapt all or part of the work
> +in a fashion requiring copyright permission, other than the making of an
> +exact copy.  The resulting work is called a "modified version" of the
> +earlier work or a work "based on" the earlier work.
> +
> +  A "covered work" means either the unmodified Program or a work based
> +on the Program.
> +
> +  To "propagate" a work means to do anything with it that, without
> +permission, would make you directly or secondarily liable for
> +infringement under applicable copyright law, except executing it on a
> +computer or modifying a private copy.  Propagation includes copying,
> +distribution (with or without modification), making available to the
> +public, and in some countries other activities as well.
> +
> +  To "convey" a work means any kind of propagation that enables other
> +parties to make or receive copies.  Mere interaction with a user through
> +a computer network, with no transfer of a copy, is not conveying.
> +
> +  An interactive user interface displays "Appropriate Legal Notices"
> +to the extent that it includes a convenient and prominently visible
> +feature that (1) displays an appropriate copyright notice, and (2)
> +tells the user that there is no warranty for the work (except to the
> +extent that warranties are provided), that licensees may convey the
> +work under this License, and how to view a copy of this License.  If
> +the interface presents a list of user commands or options, such as a
> +menu, a prominent item in the list meets this criterion.
> +
> +  1. Source Code.
> +
> +  The "source code" for a work means the preferred form of the work
> +for making modifications to it.  "Object code" means any non-source
> +form of a work.
> +
> +  A "Standard Interface" means an interface that either is an official
> +standard defined by a recognized standards body, or, in the case of
> +interfaces specified for a particular programming language, one that
> +is widely used among developers working in that language.
> +
> +  The "System Libraries" of an executable work include anything, other
> +than the work as a whole, that (a) is included in the normal form of
> +packaging a Major Component, but which is not part of that Major
> +Component, and (b) serves only to enable use of the work with that
> +Major Component, or to implement a Standard Interface for which an
> +implementation is available to the public in source code form.  A
> +"Major Component", in this context, means a major essential component
> +(kernel, window system, and so on) of the specific operating system
> +(if any) on which the executable work runs, or a compiler used to
> +produce the work, or an object code interpreter used to run it.
> +
> +  The "Corresponding Source" for a work in object code form means all
> +the source code needed to generate, install, and (for an executable
> +work) run the object code and to modify the work, including scripts to
> +control those activities.  However, it does not include the work's
> +System Libraries, or general-purpose tools or generally available free
> +programs which are used unmodified in performing those activities but
> +which are not part of the work.  For example, Corresponding Source
> +includes interface definition files associated with source files for
> +the work, and the source code for shared libraries and dynamically
> +linked subprograms that the work is specifically designed to require,
> +such as by intimate data communication or control flow between those
> +subprograms and other parts of the work.
> +
> +  The Corresponding Source need not include anything that users
> +can regenerate automatically from other parts of the Corresponding
> +Source.
> +
> +  The Corresponding Source for a work in source code form is that
> +same work.
> +
> +  2. Basic Permissions.
> +
> +  All rights granted under this License are granted for the term of
> +copyright on the Program, and are irrevocable provided the stated
> +conditions are met.  This License explicitly affirms your unlimited
> +permission to run the unmodified Program.  The output from running a
> +covered work is covered by this License only if the output, given its
> +content, constitutes a covered work.  This License acknowledges your
> +rights of fair use or other equivalent, as provided by copyright law.
> +
> +  You may make, run and propagate covered works that you do not
> +convey, without conditions so long as your license otherwise remains
> +in force.  You may convey covered works to others for the sole purpose
> +of having them make modifications exclusively for you, or provide you
> +with facilities for running those works, provided that you comply with
> +the terms of this License in conveying all material for which you do
> +not control copyright.  Those thus making or running the covered works
> +for you must do so exclusively on your behalf, under your direction
> +and control, on terms that prohibit them from making any copies of
> +your copyrighted material outside their relationship with you.
> +
> +  Conveying under any other circumstances is permitted solely under
> +the conditions stated below.  Sublicensing is not allowed; section 10
> +makes it unnecessary.
> +
> +  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
> +
> +  No covered work shall be deemed part of an effective technological
> +measure under any applicable law fulfilling obligations under article
> +11 of the WIPO copyright treaty adopted on 20 December 1996, or
> +similar laws prohibiting or restricting circumvention of such
> +measures.
> +
> +  When you convey a covered work, you waive any legal power to forbid
> +circumvention of technological measures to the extent such circumvention
> +is effected by exercising rights under this License with respect to
> +the covered work, and you disclaim any intention to limit operation or
> +modification of the work as a means of enforcing, against the work's
> +users, your or third parties' legal rights to forbid circumvention of
> +technological measures.
> +
> +  4. Conveying Verbatim Copies.
> +
> +  You may convey verbatim copies of the Program's source code as you
> +receive it, in any medium, provided that you conspicuously and
> +appropriately publish on each copy an appropriate copyright notice;
> +keep intact all notices stating that this License and any
> +non-permissive terms added in accord with section 7 apply to the code;
> +keep intact all notices of the absence of any warranty; and give all
> +recipients a copy of this License along with the Program.
> +
> +  You may charge any price or no price for each copy that you convey,
> +and you may offer support or warranty protection for a fee.
> +
> +  5. Conveying Modified Source Versions.
> +
> +  You may convey a work based on the Program, or the modifications to
> +produce it from the Program, in the form of source code under the
> +terms of section 4, provided that you also meet all of these conditions:
> +
> +    a) The work must carry prominent notices stating that you modified
> +    it, and giving a relevant date.
> +
> +    b) The work must carry prominent notices stating that it is
> +    released under this License and any conditions added under section
> +    7.  This requirement modifies the requirement in section 4 to
> +    "keep intact all notices".
> +
> +    c) You must license the entire work, as a whole, under this
> +    License to anyone who comes into possession of a copy.  This
> +    License will therefore apply, along with any applicable section 7
> +    additional terms, to the whole of the work, and all its parts,
> +    regardless of how they are packaged.  This License gives no
> +    permission to license the work in any other way, but it does not
> +    invalidate such permission if you have separately received it.
> +
> +    d) If the work has interactive user interfaces, each must display
> +    Appropriate Legal Notices; however, if the Program has interactive
> +    interfaces that do not display Appropriate Legal Notices, your
> +    work need not make them do so.
> +
> +  A compilation of a covered work with other separate and independent
> +works, which are not by their nature extensions of the covered work,
> +and which are not combined with it such as to form a larger program,
> +in or on a volume of a storage or distribution medium, is called an
> +"aggregate" if the compilation and its resulting copyright are not
> +used to limit the access or legal rights of the compilation's users
> +beyond what the individual works permit.  Inclusion of a covered work
> +in an aggregate does not cause this License to apply to the other
> +parts of the aggregate.
> +
> +  6. Conveying Non-Source Forms.
> +
> +  You may convey a covered work in object code form under the terms
> +of sections 4 and 5, provided that you also convey the
> +machine-readable Corresponding Source under the terms of this License,
> +in one of these ways:
> +
> +    a) Convey the object code in, or embodied in, a physical product
> +    (including a physical distribution medium), accompanied by the
> +    Corresponding Source fixed on a durable physical medium
> +    customarily used for software interchange.
> +
> +    b) Convey the object code in, or embodied in, a physical product
> +    (including a physical distribution medium), accompanied by a
> +    written offer, valid for at least three years and valid for as
> +    long as you offer spare parts or customer support for that product
> +    model, to give anyone who possesses the object code either (1) a
> +    copy of the Corresponding Source for all the software in the
> +    product that is covered by this License, on a durable physical
> +    medium customarily used for software interchange, for a price no
> +    more than your reasonable cost of physically performing this
> +    conveying of source, or (2) access to copy the
> +    Corresponding Source from a network server at no charge.
> +
> +    c) Convey individual copies of the object code with a copy of the
> +    written offer to provide the Corresponding Source.  This
> +    alternative is allowed only occasionally and noncommercially, and
> +    only if you received the object code with such an offer, in accord
> +    with subsection 6b.
> +
> +    d) Convey the object code by offering access from a designated
> +    place (gratis or for a charge), and offer equivalent access to the
> +    Corresponding Source in the same way through the same place at no
> +    further charge.  You need not require recipients to copy the
> +    Corresponding Source along with the object code.  If the place to
> +    copy the object code is a network server, the Corresponding Source
> +    may be on a different server (operated by you or a third party)
> +    that supports equivalent copying facilities, provided you maintain
> +    clear directions next to the object code saying where to find the
> +    Corresponding Source.  Regardless of what server hosts the
> +    Corresponding Source, you remain obligated to ensure that it is
> +    available for as long as needed to satisfy these requirements.
> +
> +    e) Convey the object code using peer-to-peer transmission, provided
> +    you inform other peers where the object code and Corresponding
> +    Source of the work are being offered to the general public at no
> +    charge under subsection 6d.
> +
> +  A separable portion of the object code, whose source code is excluded
> +from the Corresponding Source as a System Library, need not be
> +included in conveying the object code work.
> +
> +  A "User Product" is either (1) a "consumer product", which means any
> +tangible personal property which is normally used for personal, family,
> +or household purposes, or (2) anything designed or sold for incorporation
> +into a dwelling.  In determining whether a product is a consumer product,
> +doubtful cases shall be resolved in favor of coverage.  For a particular
> +product received by a particular user, "normally used" refers to a
> +typical or common use of that class of product, regardless of the status
> +of the particular user or of the way in which the particular user
> +actually uses, or expects or is expected to use, the product.  A product
> +is a consumer product regardless of whether the product has substantial
> +commercial, industrial or non-consumer uses, unless such uses represent
> +the only significant mode of use of the product.
> +
> +  "Installation Information" for a User Product means any methods,
> +procedures, authorization keys, or other information required to install
> +and execute modified versions of a covered work in that User Product from
> +a modified version of its Corresponding Source.  The information must
> +suffice to ensure that the continued functioning of the modified object
> +code is in no case prevented or interfered with solely because
> +modification has been made.
> +
> +  If you convey an object code work under this section in, or with, or
> +specifically for use in, a User Product, and the conveying occurs as
> +part of a transaction in which the right of possession and use of the
> +User Product is transferred to the recipient in perpetuity or for a
> +fixed term (regardless of how the transaction is characterized), the
> +Corresponding Source conveyed under this section must be accompanied
> +by the Installation Information.  But this requirement does not apply
> +if neither you nor any third party retains the ability to install
> +modified object code on the User Product (for example, the work has
> +been installed in ROM).
> +
> +  The requirement to provide Installation Information does not include a
> +requirement to continue to provide support service, warranty, or updates
> +for a work that has been modified or installed by the recipient, or for
> +the User Product in which it has been modified or installed.  Access to a
> +network may be denied when the modification itself materially and
> +adversely affects the operation of the network or violates the rules and
> +protocols for communication across the network.
> +
> +  Corresponding Source conveyed, and Installation Information provided,
> +in accord with this section must be in a format that is publicly
> +documented (and with an implementation available to the public in
> +source code form), and must require no special password or key for
> +unpacking, reading or copying.
> +
> +  7. Additional Terms.
> +
> +  "Additional permissions" are terms that supplement the terms of this
> +License by making exceptions from one or more of its conditions.
> +Additional permissions that are applicable to the entire Program shall
> +be treated as though they were included in this License, to the extent
> +that they are valid under applicable law.  If additional permissions
> +apply only to part of the Program, that part may be used separately
> +under those permissions, but the entire Program remains governed by
> +this License without regard to the additional permissions.
> +
> +  When you convey a copy of a covered work, you may at your option
> +remove any additional permissions from that copy, or from any part of
> +it.  (Additional permissions may be written to require their own
> +removal in certain cases when you modify the work.)  You may place
> +additional permissions on material, added by you to a covered work,
> +for which you have or can give appropriate copyright permission.
> +
> +  Notwithstanding any other provision of this License, for material you
> +add to a covered work, you may (if authorized by the copyright holders of
> +that material) supplement the terms of this License with terms:
> +
> +    a) Disclaiming warranty or limiting liability differently from the
> +    terms of sections 15 and 16 of this License; or
> +
> +    b) Requiring preservation of specified reasonable legal notices or
> +    author attributions in that material or in the Appropriate Legal
> +    Notices displayed by works containing it; or
> +
> +    c) Prohibiting misrepresentation of the origin of that material, or
> +    requiring that modified versions of such material be marked in
> +    reasonable ways as different from the original version; or
> +
> +    d) Limiting the use for publicity purposes of names of licensors or
> +    authors of the material; or
> +
> +    e) Declining to grant rights under trademark law for use of some
> +    trade names, trademarks, or service marks; or
> +
> +    f) Requiring indemnification of licensors and authors of that
> +    material by anyone who conveys the material (or modified versions of
> +    it) with contractual assumptions of liability to the recipient, for
> +    any liability that these contractual assumptions directly impose on
> +    those licensors and authors.
> +
> +  All other non-permissive additional terms are considered "further
> +restrictions" within the meaning of section 10.  If the Program as you
> +received it, or any part of it, contains a notice stating that it is
> +governed by this License along with a term that is a further
> +restriction, you may remove that term.  If a license document contains
> +a further restriction but permits relicensing or conveying under this
> +License, you may add to a covered work material governed by the terms
> +of that license document, provided that the further restriction does
> +not survive such relicensing or conveying.
> +
> +  If you add terms to a covered work in accord with this section, you
> +must place, in the relevant source files, a statement of the
> +additional terms that apply to those files, or a notice indicating
> +where to find the applicable terms.
> +
> +  Additional terms, permissive or non-permissive, may be stated in the
> +form of a separately written license, or stated as exceptions;
> +the above requirements apply either way.
> +
> +  8. Termination.
> +
> +  You may not propagate or modify a covered work except as expressly
> +provided under this License.  Any attempt otherwise to propagate or
> +modify it is void, and will automatically terminate your rights under
> +this License (including any patent licenses granted under the third
> +paragraph of section 11).
> +
> +  However, if you cease all violation of this License, then your
> +license from a particular copyright holder is reinstated (a)
> +provisionally, unless and until the copyright holder explicitly and
> +finally terminates your license, and (b) permanently, if the copyright
> +holder fails to notify you of the violation by some reasonable means
> +prior to 60 days after the cessation.
> +
> +  Moreover, your license from a particular copyright holder is
> +reinstated permanently if the copyright holder notifies you of the
> +violation by some reasonable means, this is the first time you have
> +received notice of violation of this License (for any work) from that
> +copyright holder, and you cure the violation prior to 30 days after
> +your receipt of the notice.
> +
> +  Termination of your rights under this section does not terminate the
> +licenses of parties who have received copies or rights from you under
> +this License.  If your rights have been terminated and not permanently
> +reinstated, you do not qualify to receive new licenses for the same
> +material under section 10.
> +
> +  9. Acceptance Not Required for Having Copies.
> +
> +  You are not required to accept this License in order to receive or
> +run a copy of the Program.  Ancillary propagation of a covered work
> +occurring solely as a consequence of using peer-to-peer transmission
> +to receive a copy likewise does not require acceptance.  However,
> +nothing other than this License grants you permission to propagate or
> +modify any covered work.  These actions infringe copyright if you do
> +not accept this License.  Therefore, by modifying or propagating a
> +covered work, you indicate your acceptance of this License to do so.
> +
> +  10. Automatic Licensing of Downstream Recipients.
> +
> +  Each time you convey a covered work, the recipient automatically
> +receives a license from the original licensors, to run, modify and
> +propagate that work, subject to this License.  You are not responsible
> +for enforcing compliance by third parties with this License.
> +
> +  An "entity transaction" is a transaction transferring control of an
> +organization, or substantially all assets of one, or subdividing an
> +organization, or merging organizations.  If propagation of a covered
> +work results from an entity transaction, each party to that
> +transaction who receives a copy of the work also receives whatever
> +licenses to the work the party's predecessor in interest had or could
> +give under the previous paragraph, plus a right to possession of the
> +Corresponding Source of the work from the predecessor in interest, if
> +the predecessor has it or can get it with reasonable efforts.
> +
> +  You may not impose any further restrictions on the exercise of the
> +rights granted or affirmed under this License.  For example, you may
> +not impose a license fee, royalty, or other charge for exercise of
> +rights granted under this License, and you may not initiate litigation
> +(including a cross-claim or counterclaim in a lawsuit) alleging that
> +any patent claim is infringed by making, using, selling, offering for
> +sale, or importing the Program or any portion of it.
> +
> +  11. Patents.
> +
> +  A "contributor" is a copyright holder who authorizes use under this
> +License of the Program or a work on which the Program is based.  The
> +work thus licensed is called the contributor's "contributor version".
> +
> +  A contributor's "essential patent claims" are all patent claims
> +owned or controlled by the contributor, whether already acquired or
> +hereafter acquired, that would be infringed by some manner, permitted
> +by this License, of making, using, or selling its contributor version,
> +but do not include claims that would be infringed only as a
> +consequence of further modification of the contributor version.  For
> +purposes of this definition, "control" includes the right to grant
> +patent sublicenses in a manner consistent with the requirements of
> +this License.
> +
> +  Each contributor grants you a non-exclusive, worldwide, royalty-free
> +patent license under the contributor's essential patent claims, to
> +make, use, sell, offer for sale, import and otherwise run, modify and
> +propagate the contents of its contributor version.
> +
> +  In the following three paragraphs, a "patent license" is any express
> +agreement or commitment, however denominated, not to enforce a patent
> +(such as an express permission to practice a patent or covenant not to
> +sue for patent infringement).  To "grant" such a patent license to a
> +party means to make such an agreement or commitment not to enforce a
> +patent against the party.
> +
> +  If you convey a covered work, knowingly relying on a patent license,
> +and the Corresponding Source of the work is not available for anyone
> +to copy, free of charge and under the terms of this License, through a
> +publicly available network server or other readily accessible means,
> +then you must either (1) cause the Corresponding Source to be so
> +available, or (2) arrange to deprive yourself of the benefit of the
> +patent license for this particular work, or (3) arrange, in a manner
> +consistent with the requirements of this License, to extend the patent
> +license to downstream recipients.  "Knowingly relying" means you have
> +actual knowledge that, but for the patent license, your conveying the
> +covered work in a country, or your recipient's use of the covered work
> +in a country, would infringe one or more identifiable patents in that
> +country that you have reason to believe are valid.
> +
> +  If, pursuant to or in connection with a single transaction or
> +arrangement, you convey, or propagate by procuring conveyance of, a
> +covered work, and grant a patent license to some of the parties
> +receiving the covered work authorizing them to use, propagate, modify
> +or convey a specific copy of the covered work, then the patent license
> +you grant is automatically extended to all recipients of the covered
> +work and works based on it.
> +
> +  A patent license is "discriminatory" if it does not include within
> +the scope of its coverage, prohibits the exercise of, or is
> +conditioned on the non-exercise of one or more of the rights that are
> +specifically granted under this License.  You may not convey a covered
> +work if you are a party to an arrangement with a third party that is
> +in the business of distributing software, under which you make payment
> +to the third party based on the extent of your activity of conveying
> +the work, and under which the third party grants, to any of the
> +parties who would receive the covered work from you, a discriminatory
> +patent license (a) in connection with copies of the covered work
> +conveyed by you (or copies made from those copies), or (b) primarily
> +for and in connection with specific products or compilations that
> +contain the covered work, unless you entered into that arrangement,
> +or that patent license was granted, prior to 28 March 2007.
> +
> +  Nothing in this License shall be construed as excluding or limiting
> +any implied license or other defenses to infringement that may
> +otherwise be available to you under applicable patent law.
> +
> +  12. No Surrender of Others' Freedom.
> +
> +  If conditions are imposed on you (whether by court order, agreement or
> +otherwise) that contradict the conditions of this License, they do not
> +excuse you from the conditions of this License.  If you cannot convey a
> +covered work so as to satisfy simultaneously your obligations under this
> +License and any other pertinent obligations, then as a consequence you may
> +not convey it at all.  For example, if you agree to terms that obligate you
> +to collect a royalty for further conveying from those to whom you convey
> +the Program, the only way you could satisfy both those terms and this
> +License would be to refrain entirely from conveying the Program.
> +
> +  13. Use with the GNU Affero General Public License.
> +
> +  Notwithstanding any other provision of this License, you have
> +permission to link or combine any covered work with a work licensed
> +under version 3 of the GNU Affero General Public License into a single
> +combined work, and to convey the resulting work.  The terms of this
> +License will continue to apply to the part which is the covered work,
> +but the special requirements of the GNU Affero General Public License,
> +section 13, concerning interaction through a network will apply to the
> +combination as such.
> +
> +  14. Revised Versions of this License.
> +
> +  The Free Software Foundation may publish revised and/or new versions of
> +the GNU General Public License from time to time.  Such new versions will
> +be similar in spirit to the present version, but may differ in detail to
> +address new problems or concerns.
> +
> +  Each version is given a distinguishing version number.  If the
> +Program specifies that a certain numbered version of the GNU General
> +Public License "or any later version" applies to it, you have the
> +option of following the terms and conditions either of that numbered
> +version or of any later version published by the Free Software
> +Foundation.  If the Program does not specify a version number of the
> +GNU General Public License, you may choose any version ever published
> +by the Free Software Foundation.
> +
> +  If the Program specifies that a proxy can decide which future
> +versions of the GNU General Public License can be used, that proxy's
> +public statement of acceptance of a version permanently authorizes you
> +to choose that version for the Program.
> +
> +  Later license versions may give you additional or different
> +permissions.  However, no additional obligations are imposed on any
> +author or copyright holder as a result of your choosing to follow a
> +later version.
> +
> +  15. Disclaimer of Warranty.
> +
> +  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
> +APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
> +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
> +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
> +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
> +PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
> +IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
> +ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
> +
> +  16. Limitation of Liability.
> +
> +  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
> +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
> +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
> +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
> +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
> +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
> +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
> +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
> +SUCH DAMAGES.
> +
> +  17. Interpretation of Sections 15 and 16.
> +
> +  If the disclaimer of warranty and limitation of liability provided
> +above cannot be given local legal effect according to their terms,
> +reviewing courts shall apply local law that most closely approximates
> +an absolute waiver of all civil liability in connection with the
> +Program, unless a warranty or assumption of liability accompanies a
> +copy of the Program in return for a fee.
> +
> +                     END OF TERMS AND CONDITIONS
> +
> +            How to Apply These Terms to Your New Programs
> +
> +  If you develop a new program, and you want it to be of the greatest
> +possible use to the public, the best way to achieve this is to make it
> +free software which everyone can redistribute and change under these terms.
> +
> +  To do so, attach the following notices to the program.  It is safest
> +to attach them to the start of each source file to most effectively
> +state the exclusion of warranty; and each file should have at least
> +the "copyright" line and a pointer to where the full notice is found.
> +
> +    <one line to give the program's name and a brief idea of what it does.>
> +    Copyright (C) <year>  <name of author>
> +
> +    This program is free software: you can redistribute it and/or modify
> +    it under the terms of the GNU General Public License as published by
> +    the Free Software Foundation, either version 3 of the License, or
> +    (at your option) any later version.
> +
> +    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, see <https://www.gnu.org/licenses/>.
> +
> +Also add information on how to contact you by electronic and paper mail.
> +
> +  If the program does terminal interaction, make it output a short
> +notice like this when it starts in an interactive mode:
> +
> +    <program>  Copyright (C) <year>  <name of author>
> +    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
> +    This is free software, and you are welcome to redistribute it
> +    under certain conditions; type `show c' for details.
> +
> +The hypothetical commands `show w' and `show c' should show the appropriate
> +parts of the General Public License.  Of course, your program's commands
> +might be different; for a GUI interface, you would use an "about box".
> +
> +  You should also get your employer (if you work as a programmer) or school,
> +if any, to sign a "copyright disclaimer" for the program, if necessary.
> +For more information on this, and how to apply and follow the GNU GPL, see
> +<https://www.gnu.org/licenses/>.
> +
> +  The GNU General Public License does not permit incorporating your program
> +into proprietary programs.  If your program is a subroutine library, you
> +may consider it more useful to permit linking proprietary applications with
> +the library.  If this is what you want to do, use the GNU Lesser General
> +Public License instead of this License.  But first, please read
> +<https://www.gnu.org/licenses/why-not-lgpl.html>.
> diff --git a/libbtrfsutil/COPYING.LESSER b/libbtrfsutil/COPYING.LESSER
> new file mode 100644
> index 00000000..0a041280
> --- /dev/null
> +++ b/libbtrfsutil/COPYING.LESSER
> @@ -0,0 +1,165 @@
> +                   GNU LESSER GENERAL PUBLIC LICENSE
> +                       Version 3, 29 June 2007
> +
> + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
> + Everyone is permitted to copy and distribute verbatim copies
> + of this license document, but changing it is not allowed.
> +
> +
> +  This version of the GNU Lesser General Public License incorporates
> +the terms and conditions of version 3 of the GNU General Public
> +License, supplemented by the additional permissions listed below.
> +
> +  0. Additional Definitions.
> +
> +  As used herein, "this License" refers to version 3 of the GNU Lesser
> +General Public License, and the "GNU GPL" refers to version 3 of the GNU
> +General Public License.
> +
> +  "The Library" refers to a covered work governed by this License,
> +other than an Application or a Combined Work as defined below.
> +
> +  An "Application" is any work that makes use of an interface provided
> +by the Library, but which is not otherwise based on the Library.
> +Defining a subclass of a class defined by the Library is deemed a mode
> +of using an interface provided by the Library.
> +
> +  A "Combined Work" is a work produced by combining or linking an
> +Application with the Library.  The particular version of the Library
> +with which the Combined Work was made is also called the "Linked
> +Version".
> +
> +  The "Minimal Corresponding Source" for a Combined Work means the
> +Corresponding Source for the Combined Work, excluding any source code
> +for portions of the Combined Work that, considered in isolation, are
> +based on the Application, and not on the Linked Version.
> +
> +  The "Corresponding Application Code" for a Combined Work means the
> +object code and/or source code for the Application, including any data
> +and utility programs needed for reproducing the Combined Work from the
> +Application, but excluding the System Libraries of the Combined Work.
> +
> +  1. Exception to Section 3 of the GNU GPL.
> +
> +  You may convey a covered work under sections 3 and 4 of this License
> +without being bound by section 3 of the GNU GPL.
> +
> +  2. Conveying Modified Versions.
> +
> +  If you modify a copy of the Library, and, in your modifications, a
> +facility refers to a function or data to be supplied by an Application
> +that uses the facility (other than as an argument passed when the
> +facility is invoked), then you may convey a copy of the modified
> +version:
> +
> +   a) under this License, provided that you make a good faith effort to
> +   ensure that, in the event an Application does not supply the
> +   function or data, the facility still operates, and performs
> +   whatever part of its purpose remains meaningful, or
> +
> +   b) under the GNU GPL, with none of the additional permissions of
> +   this License applicable to that copy.
> +
> +  3. Object Code Incorporating Material from Library Header Files.
> +
> +  The object code form of an Application may incorporate material from
> +a header file that is part of the Library.  You may convey such object
> +code under terms of your choice, provided that, if the incorporated
> +material is not limited to numerical parameters, data structure
> +layouts and accessors, or small macros, inline functions and templates
> +(ten or fewer lines in length), you do both of the following:
> +
> +   a) Give prominent notice with each copy of the object code that the
> +   Library is used in it and that the Library and its use are
> +   covered by this License.
> +
> +   b) Accompany the object code with a copy of the GNU GPL and this license
> +   document.
> +
> +  4. Combined Works.
> +
> +  You may convey a Combined Work under terms of your choice that,
> +taken together, effectively do not restrict modification of the
> +portions of the Library contained in the Combined Work and reverse
> +engineering for debugging such modifications, if you also do each of
> +the following:
> +
> +   a) Give prominent notice with each copy of the Combined Work that
> +   the Library is used in it and that the Library and its use are
> +   covered by this License.
> +
> +   b) Accompany the Combined Work with a copy of the GNU GPL and this license
> +   document.
> +
> +   c) For a Combined Work that displays copyright notices during
> +   execution, include the copyright notice for the Library among
> +   these notices, as well as a reference directing the user to the
> +   copies of the GNU GPL and this license document.
> +
> +   d) Do one of the following:
> +
> +       0) Convey the Minimal Corresponding Source under the terms of this
> +       License, and the Corresponding Application Code in a form
> +       suitable for, and under terms that permit, the user to
> +       recombine or relink the Application with a modified version of
> +       the Linked Version to produce a modified Combined Work, in the
> +       manner specified by section 6 of the GNU GPL for conveying
> +       Corresponding Source.
> +
> +       1) Use a suitable shared library mechanism for linking with the
> +       Library.  A suitable mechanism is one that (a) uses at run time
> +       a copy of the Library already present on the user's computer
> +       system, and (b) will operate properly with a modified version
> +       of the Library that is interface-compatible with the Linked
> +       Version.
> +
> +   e) Provide Installation Information, but only if you would otherwise
> +   be required to provide such information under section 6 of the
> +   GNU GPL, and only to the extent that such information is
> +   necessary to install and execute a modified version of the
> +   Combined Work produced by recombining or relinking the
> +   Application with a modified version of the Linked Version. (If
> +   you use option 4d0, the Installation Information must accompany
> +   the Minimal Corresponding Source and Corresponding Application
> +   Code. If you use option 4d1, you must provide the Installation
> +   Information in the manner specified by section 6 of the GNU GPL
> +   for conveying Corresponding Source.)
> +
> +  5. Combined Libraries.
> +
> +  You may place library facilities that are a work based on the
> +Library side by side in a single library together with other library
> +facilities that are not Applications and are not covered by this
> +License, and convey such a combined library under terms of your
> +choice, if you do both of the following:
> +
> +   a) Accompany the combined library with a copy of the same work based
> +   on the Library, uncombined with any other library facilities,
> +   conveyed under the terms of this License.
> +
> +   b) Give prominent notice with the combined library that part of it
> +   is a work based on the Library, and explaining where to find the
> +   accompanying uncombined form of the same work.
> +
> +  6. Revised Versions of the GNU Lesser General Public License.
> +
> +  The Free Software Foundation may publish revised and/or new versions
> +of the GNU Lesser General Public License from time to time. Such new
> +versions will be similar in spirit to the present version, but may
> +differ in detail to address new problems or concerns.
> +
> +  Each version is given a distinguishing version number. If the
> +Library as you received it specifies that a certain numbered version
> +of the GNU Lesser General Public License "or any later version"
> +applies to it, you have the option of following the terms and
> +conditions either of that published version or of any later version
> +published by the Free Software Foundation. If the Library as you
> +received it does not specify a version number of the GNU Lesser
> +General Public License, you may choose any version of the GNU Lesser
> +General Public License ever published by the Free Software Foundation.
> +
> +  If the Library as you received it specifies that a proxy can decide
> +whether future versions of the GNU Lesser General Public License shall
> +apply, that proxy's public statement of acceptance of any version is
> +permanent authorization for you to choose that version for the
> +Library.
> diff --git a/libbtrfsutil/README.md b/libbtrfsutil/README.md
> new file mode 100644
> index 00000000..2bc9a0a7
> --- /dev/null
> +++ b/libbtrfsutil/README.md
> @@ -0,0 +1,32 @@
> +libbtrfsutil
> +============
> +
> +libbtrfsutil is a library for managing Btrfs filesystems. It is licensed under
> +the LGPL. libbtrfsutil provides interfaces for a subset of the operations
> +offered by the `btrfs` command line utility.
> +
> +Development
> +-----------
> +
> +The [development process for btrfs-progs](../README.md#development) applies.
> +
> +libbtrfsutil only includes operations that are done through the filesystem and
> +ioctl interface, not operations that modify the filesystem directly (e.g., mkfs
> +or fsck). This is by design but also a legal necessity, as the filesystem
> +implementation is GPL but libbtrfsutil is LGPL. That is also why the
> +libbtrfsutil code is a reimplementation of the btrfs-progs code rather than a
> +refactoring. Be wary of this when porting functionality.
> +
> +A few guidelines:
> +
> +* All interfaces must be documented in `btrfsutil.h` using the kernel-doc style
> +* Error codes should be specific about what _exactly_ failed
> +* Functions should have a path and an fd variant whenever possible
> +* Spell out terms in function names, etc. rather than abbreviating whenever
> +  possible
> +* Don't require the Btrfs UAPI headers for any interfaces (e.g., instead of
> +  directly exposing a type from `linux/btrfs_tree.h`, abstract it away in a
> +  type specific to `libbtrfsutil`)
> +* Preserve API and ABI compatability at all times
> +* Bump the libbtrfsutil version once for every release where the library
> +  changes (this is mostly on the maintainers)
> diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
> new file mode 100644
> index 00000000..fe1091ca
> --- /dev/null
> +++ b/libbtrfsutil/btrfsutil.h
> @@ -0,0 +1,72 @@
> +/*
> + * Copyright (C) 2018 Facebook
> + *
> + * This file is part of libbtrfsutil.
> + *
> + * libbtrfsutil is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU Lesser General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * libbtrfsutil 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 Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef BTRFS_UTIL_H
> +#define BTRFS_UTIL_H
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/**
> + * enum btrfs_util_error - libbtrfsutil error codes.
> + *
> + * All functions in libbtrfsutil that can return an error return this type and
> + * set errno.
> + */

I totally understand that libbtrfsutils needs extra error numbers, but I
didn't see similar practice, would you mind to give some existing
example of such >0 error usage in other projects?
(Just curious)

Normally people would expect error values < 0 to indicate errors, just
like glibc system call wrappers, which always return -1 to indicate errors.

> +enum btrfs_util_error {
> +	BTRFS_UTIL_OK,
> +	BTRFS_UTIL_ERROR_STOP_ITERATION,
> +	BTRFS_UTIL_ERROR_NO_MEMORY,

Not sure if this is duplicated with -ENOMEM errno.

From my understanding, these extra numbers should be used to indicate
extra error not definied in generic errno.h.

For NOT_BTRFS and NOT_SUBVOLUME they makes sense, but for NO_MEMORY, I'm
really not sure.

Thanks,
Qu

> +	BTRFS_UTIL_ERROR_INVALID_ARGUMENT,
> +	BTRFS_UTIL_ERROR_NOT_BTRFS,
> +	BTRFS_UTIL_ERROR_NOT_SUBVOLUME,
> +	BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND,
> +	BTRFS_UTIL_ERROR_OPEN_FAILED,
> +	BTRFS_UTIL_ERROR_RMDIR_FAILED,
> +	BTRFS_UTIL_ERROR_UNLINK_FAILED,
> +	BTRFS_UTIL_ERROR_STAT_FAILED,
> +	BTRFS_UTIL_ERROR_STATFS_FAILED,
> +	BTRFS_UTIL_ERROR_SEARCH_FAILED,
> +	BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED,
> +	BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED,
> +	BTRFS_UTIL_ERROR_SUBVOL_SETFLAGS_FAILED,
> +	BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED,
> +	BTRFS_UTIL_ERROR_SNAP_CREATE_FAILED,
> +	BTRFS_UTIL_ERROR_SNAP_DESTROY_FAILED,
> +	BTRFS_UTIL_ERROR_DEFAULT_SUBVOL_FAILED,
> +	BTRFS_UTIL_ERROR_SYNC_FAILED,
> +	BTRFS_UTIL_ERROR_START_SYNC_FAILED,
> +	BTRFS_UTIL_ERROR_WAIT_SYNC_FAILED,
> +};
> +
> +/**
> + * btrfs_util_strerror() - Convert a libtrfsutil error code to a string
> + * description.
> + * @err: The error to convert.
> + *
> + * Return: Error description.
> + */
> +const char *btrfs_util_strerror(enum btrfs_util_error err);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* BTRFS_UTIL_H */
> diff --git a/libbtrfsutil/errors.c b/libbtrfsutil/errors.c
> new file mode 100644
> index 00000000..f695c541
> --- /dev/null
> +++ b/libbtrfsutil/errors.c
> @@ -0,0 +1,55 @@
> +/*
> + * Copyright (C) 2018 Facebook
> + *
> + * This file is part of libbtrfsutil.
> + *
> + * libbtrfsutil is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU Lesser General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * libbtrfsutil 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 Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <stddef.h>
> +#include "btrfsutil.h"
> +
> +static const char * const error_messages[] = {
> +	[BTRFS_UTIL_OK] = "Success",
> +	[BTRFS_UTIL_ERROR_STOP_ITERATION] = "Stop iteration",
> +	[BTRFS_UTIL_ERROR_NO_MEMORY] = "Cannot allocate memory",
> +	[BTRFS_UTIL_ERROR_INVALID_ARGUMENT] = "Invalid argument",
> +	[BTRFS_UTIL_ERROR_NOT_BTRFS] = "Not a Btrfs filesystem",
> +	[BTRFS_UTIL_ERROR_NOT_SUBVOLUME] = "Not a Btrfs subvolume",
> +	[BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND] = "Subvolume not found",
> +	[BTRFS_UTIL_ERROR_OPEN_FAILED] = "Could not open",
> +	[BTRFS_UTIL_ERROR_RMDIR_FAILED] = "Could not rmdir",
> +	[BTRFS_UTIL_ERROR_UNLINK_FAILED] = "Could not unlink",
> +	[BTRFS_UTIL_ERROR_STAT_FAILED] = "Could not stat",
> +	[BTRFS_UTIL_ERROR_STATFS_FAILED] = "Could not statfs",
> +	[BTRFS_UTIL_ERROR_SEARCH_FAILED] = "Could not search B-tree",
> +	[BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED] = "Could not lookup inode",
> +	[BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED] = "Could not get subvolume flags",
> +	[BTRFS_UTIL_ERROR_SUBVOL_SETFLAGS_FAILED] = "Could not set subvolume flags",
> +	[BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED] = "Could not create subvolume",
> +	[BTRFS_UTIL_ERROR_SNAP_CREATE_FAILED] = "Could not create snapshot",
> +	[BTRFS_UTIL_ERROR_SNAP_DESTROY_FAILED] = "Could not destroy subvolume/snapshot",
> +	[BTRFS_UTIL_ERROR_DEFAULT_SUBVOL_FAILED] = "Could not set default subvolume",
> +	[BTRFS_UTIL_ERROR_SYNC_FAILED] = "Could not sync filesystem",
> +	[BTRFS_UTIL_ERROR_START_SYNC_FAILED] = "Could not start filesystem sync",
> +	[BTRFS_UTIL_ERROR_WAIT_SYNC_FAILED] = "Could not wait for filesystem sync",
> +};
> +
> +__attribute__((visibility("default")))
> +const char *btrfs_util_strerror(enum btrfs_util_error err)
> +{
> +	if (err < 0 || err >= sizeof(error_messages) / sizeof(error_messages[0]))
> +		return NULL;
> +	return error_messages[err];
> +}
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 520 bytes --]

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

* Re: [PATCH 04/26] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id()
  2018-01-26 18:40 ` [PATCH 04/26] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id() Omar Sandoval
@ 2018-01-29 10:24   ` Nikolay Borisov
  2018-01-29 21:43     ` Omar Sandoval
  0 siblings, 1 reply; 47+ messages in thread
From: Nikolay Borisov @ 2018-01-29 10:24 UTC (permalink / raw)
  To: Omar Sandoval, linux-btrfs; +Cc: kernel-team



On 26.01.2018 20:40, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>
> 
> These are the most trivial helpers in the library and will be used to
> implement several of the more involved functions.
> 
> Signed-off-by: Omar Sandoval <osandov@fb.com>
> ---
>  Makefile                                    |   2 +-
>  libbtrfsutil/btrfsutil.h                    |  33 +++++++
>  libbtrfsutil/python/btrfsutilpy.h           |   3 +
>  libbtrfsutil/python/module.c                |  12 +++
>  libbtrfsutil/python/setup.py                |   1 +
>  libbtrfsutil/python/subvolume.c             |  73 +++++++++++++++
>  libbtrfsutil/python/tests/__init__.py       |  66 ++++++++++++++
>  libbtrfsutil/python/tests/test_subvolume.py |  57 ++++++++++++
>  libbtrfsutil/subvolume.c                    | 137 ++++++++++++++++++++++++++++
>  9 files changed, 383 insertions(+), 1 deletion(-)
>  create mode 100644 libbtrfsutil/python/subvolume.c
>  create mode 100644 libbtrfsutil/python/tests/test_subvolume.py
>  create mode 100644 libbtrfsutil/subvolume.c
> 
> diff --git a/Makefile b/Makefile
> index 02b03e81..48a558a9 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -123,7 +123,7 @@ libbtrfs_headers = send-stream.h send-utils.h send.h kernel-lib/rbtree.h btrfs-l
>  	       kernel-lib/radix-tree.h kernel-lib/sizes.h kernel-lib/raid56.h \
>  	       extent-cache.h extent_io.h ioctl.h ctree.h btrfsck.h version.h
>  libbtrfsutil_version := 0.1
> -libbtrfsutil_objects = libbtrfsutil/errors.o
> +libbtrfsutil_objects = libbtrfsutil/errors.o libbtrfsutil/subvolume.o
>  convert_objects = convert/main.o convert/common.o convert/source-fs.o \
>  		  convert/source-ext2.o convert/source-reiserfs.o
>  mkfs_objects = mkfs/main.o mkfs/common.o
> diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
> index fe1091ca..dff6599d 100644
> --- a/libbtrfsutil/btrfsutil.h
> +++ b/libbtrfsutil/btrfsutil.h
> @@ -20,6 +20,8 @@
>  #ifndef BTRFS_UTIL_H
>  #define BTRFS_UTIL_H
>  
> +#include <stdint.h>
> +
>  #ifdef __cplusplus
>  extern "C" {
>  #endif
> @@ -65,6 +67,37 @@ enum btrfs_util_error {
>   */
>  const char *btrfs_util_strerror(enum btrfs_util_error err);
>  
> +/**
> + * btrfs_util_is_subvolume() - Return whether a given path is a Btrfs subvolume.
> + * @path: Path to check.
> + *
> + * Return: %BTRFS_UTIL_OK if @path is a Btrfs subvolume,
> + * %BTRFS_UTIL_ERROR_NOT_BTRFS if @path is not on a Btrfs filesystem,
> + * %BTRFS_UTIL_ERROR_NOT_SUBVOLUME if @path is not a subvolume, non-zero error
> + * code on any other failure.
> + */
> +enum btrfs_util_error btrfs_util_is_subvolume(const char *path);
> +
> +/**
> + * btrfs_util_f_is_subvolume() - See btrfs_util_is_subvolume().
> + */
> +enum btrfs_util_error btrfs_util_f_is_subvolume(int fd);
> +
> +/**
> + * btrfs_util_subvolume_id() - Get the ID of the subvolume containing a path.
> + * @path: Path on a Btrfs filesystem.
> + * @id_ret: Returned subvolume ID.
> + *
> + * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
> + */
> +enum btrfs_util_error btrfs_util_subvolume_id(const char *path,
> +					      uint64_t *id_ret);
> +
> +/**
> + * btrfs_util_f_subvolume_id() - See btrfs_util_subvolume_id().
> + */
> +enum btrfs_util_error btrfs_util_f_subvolume_id(int fd, uint64_t *id_ret);
> +
>  #ifdef __cplusplus
>  }
>  #endif
> diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
> index 6d82f7e1..9a04fda7 100644
> --- a/libbtrfsutil/python/btrfsutilpy.h
> +++ b/libbtrfsutil/python/btrfsutilpy.h
> @@ -52,6 +52,9 @@ void SetFromBtrfsUtilErrorWithPaths(enum btrfs_util_error err,
>  				    struct path_arg *path1,
>  				    struct path_arg *path2);
>  
> +PyObject *is_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
> +PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds);
> +
>  void add_module_constants(PyObject *m);
>  
>  #endif /* BTRFSUTILPY_H */
> diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
> index d7398808..d492cbc7 100644
> --- a/libbtrfsutil/python/module.c
> +++ b/libbtrfsutil/python/module.c
> @@ -132,6 +132,18 @@ void path_cleanup(struct path_arg *path)
>  }
>  
>  static PyMethodDef btrfsutil_methods[] = {
> +	{"is_subvolume", (PyCFunction)is_subvolume,
> +	 METH_VARARGS | METH_KEYWORDS,
> +	 "is_subvolume(path) -> bool\n\n"
> +	 "Get whether a file is a subvolume.\n\n"
> +	 "Arguments:\n"
> +	 "path -- string, bytes, path-like object, or open file descriptor"},
> +	{"subvolume_id", (PyCFunction)subvolume_id,
> +	 METH_VARARGS | METH_KEYWORDS,
> +	 "subvolume_id(path) -> int\n\n"
> +	 "Get the ID of the subvolume containing a file.\n\n"
> +	 "Arguments:\n"
> +	 "path -- string, bytes, path-like object, or open file descriptor"},
>  	{},
>  };
>  
> diff --git a/libbtrfsutil/python/setup.py b/libbtrfsutil/python/setup.py
> index 3dc778ab..be973a34 100755
> --- a/libbtrfsutil/python/setup.py
> +++ b/libbtrfsutil/python/setup.py
> @@ -79,6 +79,7 @@ module = Extension(
>          'constants.c',
>          'error.c',
>          'module.c',
> +        'subvolume.c',
>      ],
>      include_dirs=['..'],
>      library_dirs=['../..'],
> diff --git a/libbtrfsutil/python/subvolume.c b/libbtrfsutil/python/subvolume.c
> new file mode 100644
> index 00000000..538bf324
> --- /dev/null
> +++ b/libbtrfsutil/python/subvolume.c
> @@ -0,0 +1,73 @@
> +/*
> + * Copyright (C) 2018 Facebook
> + *
> + * This file is part of libbtrfsutil.
> + *
> + * libbtrfsutil is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU Lesser General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * libbtrfsutil 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 Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "btrfsutilpy.h"
> +
> +PyObject *is_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
> +{
> +	static char *keywords[] = {"path", NULL};
> +	struct path_arg path = {.allow_fd = true};
> +	enum btrfs_util_error err;
> +
> +	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:is_subvolume",
> +					 keywords, &path_converter, &path))
> +		return NULL;
> +
> +	if (path.path)
> +		err = btrfs_util_is_subvolume(path.path);
> +	else
> +		err = btrfs_util_f_is_subvolume(path.fd);
> +	if (err == BTRFS_UTIL_OK) {
> +		path_cleanup(&path);
> +		Py_RETURN_TRUE;
> +	} else if (err == BTRFS_UTIL_ERROR_NOT_BTRFS ||
> +		   err == BTRFS_UTIL_ERROR_NOT_SUBVOLUME) {
> +		path_cleanup(&path);
> +		Py_RETURN_FALSE;
> +	} else {
> +		SetFromBtrfsUtilErrorWithPath(err, &path);
> +		path_cleanup(&path);
> +		return NULL;
> +	}
> +}
> +
> +PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds)
> +{
> +	static char *keywords[] = {"path", NULL};
> +	struct path_arg path = {.allow_fd = true};
> +	enum btrfs_util_error err;
> +	uint64_t id;
> +
> +	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:subvolume_id",
> +					 keywords, &path_converter, &path))
> +		return NULL;
> +
> +	if (path.path)
> +		err = btrfs_util_subvolume_id(path.path, &id);
> +	else
> +		err = btrfs_util_f_subvolume_id(path.fd, &id);
> +	if (err) {
> +		SetFromBtrfsUtilErrorWithPath(err, &path);
> +		path_cleanup(&path);
> +		return NULL;
> +	}
> +
> +	path_cleanup(&path);
> +	return PyLong_FromUnsignedLongLong(id);
> +}
> diff --git a/libbtrfsutil/python/tests/__init__.py b/libbtrfsutil/python/tests/__init__.py
> index e69de29b..d2c6ff28 100644
> --- a/libbtrfsutil/python/tests/__init__.py
> +++ b/libbtrfsutil/python/tests/__init__.py
> @@ -0,0 +1,66 @@
> +# Copyright (C) 2018 Facebook
> +#
> +# This file is part of libbtrfsutil.
> +#
> +# libbtrfsutil is free software: you can redistribute it and/or modify
> +# it under the terms of the GNU Lesser General Public License as published by
> +# the Free Software Foundation, either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# libbtrfsutil 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 Lesser General Public License for more details.
> +#
> +# You should have received a copy of the GNU Lesser General Public License
> +# along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
> +
> +import os
> +from pathlib import PurePath
> +import subprocess
> +import tempfile
> +import unittest
> +
> +
> +HAVE_PATH_LIKE = hasattr(PurePath, '__fspath__')
> +
> +
> +@unittest.skipIf(os.geteuid() != 0, 'must be run as root')
> +class BtrfsTestCase(unittest.TestCase):
> +    def setUp(self):
> +        self.mountpoint = tempfile.mkdtemp()
> +        try:
> +            with tempfile.NamedTemporaryFile(delete=False) as f:
> +                os.truncate(f.fileno(), 1024 * 1024 * 1024)
> +                self.image = f.name
> +        except Exception as e:
> +            os.rmdir(self.mountpoint)
> +            raise e
> +
> +        try:
> +            subprocess.check_call(['mkfs.btrfs', '-q', self.image])
> +            subprocess.check_call(['mount', '-o', 'loop', '--', self.image, self.mountpoint])
> +        except Exception as e:
> +            os.remove(self.image)
> +            os.rmdir(self.mountpoint)
> +            raise e
> +
> +    def tearDown(self):
> +        try:
> +            subprocess.check_call(['umount', self.mountpoint])
> +        finally:
> +            os.remove(self.image)
> +            os.rmdir(self.mountpoint)
> +
> +    @staticmethod
> +    def path_or_fd(path, open_flags=os.O_RDONLY):
> +        yield path
> +        yield path.encode()
> +        if HAVE_PATH_LIKE:
> +            yield PurePath(path)
> +        fd = os.open(path, open_flags)
> +        try:
> +            yield fd
> +        finally:
> +            os.close(fd)
> +
> diff --git a/libbtrfsutil/python/tests/test_subvolume.py b/libbtrfsutil/python/tests/test_subvolume.py
> new file mode 100644
> index 00000000..44b1d7f0
> --- /dev/null
> +++ b/libbtrfsutil/python/tests/test_subvolume.py
> @@ -0,0 +1,57 @@
> +# Copyright (C) 2018 Facebook
> +#
> +# This file is part of libbtrfsutil.
> +#
> +# libbtrfsutil is free software: you can redistribute it and/or modify
> +# it under the terms of the GNU Lesser General Public License as published by
> +# the Free Software Foundation, either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# libbtrfsutil 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 Lesser General Public License for more details.
> +#
> +# You should have received a copy of the GNU Lesser General Public License
> +# along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
> +
> +import fcntl
> +import errno
> +import os
> +import os.path
> +from pathlib import PurePath
> +import traceback
> +
> +import btrfsutil
> +from tests import BtrfsTestCase
> +
> +
> +class TestSubvolume(BtrfsTestCase):
> +    def test_is_subvolume(self):
> +        dir = os.path.join(self.mountpoint, 'foo')
> +        os.mkdir(dir)
> +
> +        for arg in self.path_or_fd(self.mountpoint):
> +            with self.subTest(type=type(arg)):
> +                self.assertTrue(btrfsutil.is_subvolume(arg))
> +        for arg in self.path_or_fd(dir):
> +            with self.subTest(type=type(arg)):
> +                self.assertFalse(btrfsutil.is_subvolume(arg))
> +
> +        with self.assertRaises(btrfsutil.BtrfsUtilError) as e:
> +            btrfsutil.is_subvolume(os.path.join(self.mountpoint, 'bar'))
> +        # This is a bit of an implementation detail, but really this is testing
> +        # that the exception is initialized correctly.
> +        self.assertEqual(e.exception.btrfsutilerror, btrfsutil.ERROR_STATFS_FAILED)
> +        self.assertEqual(e.exception.errno, errno.ENOENT)
> +
> +    def test_subvolume_id(self):
> +        dir = os.path.join(self.mountpoint, 'foo')
> +        os.mkdir(dir)
> +
> +        for arg in self.path_or_fd(self.mountpoint):
> +            with self.subTest(type=type(arg)):
> +                self.assertEqual(btrfsutil.subvolume_id(arg), 5)
> +        for arg in self.path_or_fd(dir):
> +            with self.subTest(type=type(arg)):
> +                self.assertEqual(btrfsutil.subvolume_id(arg), 5)
> diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c
> new file mode 100644
> index 00000000..37d5d388
> --- /dev/null
> +++ b/libbtrfsutil/subvolume.c
> @@ -0,0 +1,137 @@
> +/*
> + * Copyright (C) 2018 Facebook
> + *
> + * This file is part of libbtrfsutil.
> + *
> + * libbtrfsutil is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU Lesser General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * libbtrfsutil 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 Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <sys/ioctl.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <sys/vfs.h>
> +#include <linux/btrfs.h>
> +#include <linux/btrfs_tree.h>
> +#include <linux/magic.h>
> +
> +#include "btrfsutil.h"
> +
> +#define SAVE_ERRNO_AND_CLOSE(fd) {	\
> +	int saved_errno = errno;	\
> +					\
> +	close(fd);			\
> +	errno = saved_errno;		\
> +}
> +
> +/*
> + * This intentionally duplicates btrfs_util_f_is_subvolume() instead of opening
> + * a file descriptor and calling it, because fstat() and fstatfs() don't accept
> + * file descriptors opened with O_PATH on old kernels (before v3.6 and before
> + * v3.12, respectively), but stat() and statfs() can be called on a path that
> + * the user doesn't have read or write permissions to.
> + */
> +__attribute__((visibility("default")))

Why do we need to explicitly set the attribute visibility to default,
isn't it implicitly default already?
> +enum btrfs_util_error btrfs_util_is_subvolume(const char *path)
> +{
> +	struct statfs sfs;
> +	struct stat st;
> +	int ret;
> +
> +	ret = statfs(path, &sfs);
> +	if (ret == -1)
> +		return BTRFS_UTIL_ERROR_STATFS_FAILED;
> +
> +	if (sfs.f_type != BTRFS_SUPER_MAGIC) {
> +		errno = EINVAL;
> +		return BTRFS_UTIL_ERROR_NOT_BTRFS;
> +	}
> +
> +	ret = stat(path, &st);
> +	if (ret == -1)
> +		return BTRFS_UTIL_ERROR_STAT_FAILED;
> +
> +	if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) {
> +		errno = EINVAL;
> +		return BTRFS_UTIL_ERROR_NOT_SUBVOLUME;
> +	}
> +
> +	return BTRFS_UTIL_OK;
> +}
> +
> +__attribute__((visibility("default")))
> +enum btrfs_util_error btrfs_util_f_is_subvolume(int fd)
> +{
> +	struct statfs sfs;
> +	struct stat st;
> +	int ret;
> +
> +	ret = fstatfs(fd, &sfs);
> +	if (ret == -1)
> +		return BTRFS_UTIL_ERROR_STATFS_FAILED;
> +
> +	if (sfs.f_type != BTRFS_SUPER_MAGIC) {
> +		errno = EINVAL;
> +		return BTRFS_UTIL_ERROR_NOT_BTRFS;
> +	}
> +
> +	ret = fstat(fd, &st);
> +	if (ret == -1)
> +		return BTRFS_UTIL_ERROR_STAT_FAILED;
> +
> +	if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) {
> +		errno = EINVAL;
> +		return BTRFS_UTIL_ERROR_NOT_SUBVOLUME;
> +	}
> +
> +	return BTRFS_UTIL_OK;
> +}
> +
> +__attribute__((visibility("default")))
> +enum btrfs_util_error btrfs_util_subvolume_id(const char *path,
> +					      uint64_t *id_ret)
> +{
> +	enum btrfs_util_error err;
> +	int fd;
> +
> +	fd = open(path, O_RDONLY);
> +	if (fd == -1)
> +		return BTRFS_UTIL_ERROR_OPEN_FAILED;
> +
> +	err = btrfs_util_f_subvolume_id(fd, id_ret);
> +	SAVE_ERRNO_AND_CLOSE(fd);
> +	return err;
> +}
> +
> +__attribute__((visibility("default")))
> +enum btrfs_util_error btrfs_util_f_subvolume_id(int fd, uint64_t *id_ret)
> +{
> +	struct btrfs_ioctl_ino_lookup_args args = {
> +		.treeid = 0,
> +		.objectid = BTRFS_FIRST_FREE_OBJECTID,
> +	};
> +	int ret;
> +
> +	ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
> +	if (ret == -1) {
> +		close(fd);
> +		return BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED;
> +	}
> +
> +	*id_ret = args.treeid;
> +
> +	return BTRFS_UTIL_OK;
> +}
> 

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

* Re: [PATCH 04/26] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id()
  2018-01-29 10:24   ` Nikolay Borisov
@ 2018-01-29 21:43     ` Omar Sandoval
  2018-01-30  6:54       ` Nikolay Borisov
  0 siblings, 1 reply; 47+ messages in thread
From: Omar Sandoval @ 2018-01-29 21:43 UTC (permalink / raw)
  To: Nikolay Borisov; +Cc: linux-btrfs, kernel-team

On Mon, Jan 29, 2018 at 12:24:26PM +0200, Nikolay Borisov wrote:
> On 26.01.2018 20:40, Omar Sandoval wrote:
[snip]
> > +/*
> > + * This intentionally duplicates btrfs_util_f_is_subvolume() instead of opening
> > + * a file descriptor and calling it, because fstat() and fstatfs() don't accept
> > + * file descriptors opened with O_PATH on old kernels (before v3.6 and before
> > + * v3.12, respectively), but stat() and statfs() can be called on a path that
> > + * the user doesn't have read or write permissions to.
> > + */
> > +__attribute__((visibility("default")))
> 
> Why do we need to explicitly set the attribute visibility to default,
> isn't it implicitly default already?

Ah, I forgot to add -fvisibility=hidden to the build rule when I ported
this to the btrfs-progs Makefile, that's why that's there. I'll add
-fvisibility=hidden to the Makefile.

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

* Re: [PATCH 02/26] Add libbtrfsutil
  2018-01-29  2:16   ` Qu Wenruo
@ 2018-01-29 23:45     ` Omar Sandoval
  0 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-01-29 23:45 UTC (permalink / raw)
  To: Qu Wenruo; +Cc: linux-btrfs, kernel-team

On Mon, Jan 29, 2018 at 10:16:40AM +0800, Qu Wenruo wrote:
> 
> 
> On 2018年01月27日 02:40, Omar Sandoval wrote:
> > From: Omar Sandoval <osandov@fb.com>
> > 
> > Currently, users wishing to manage Btrfs filesystems programatically
> > have to shell out to btrfs-progs and parse the output. This isn't ideal.
> > The goal of libbtrfsutil is to provide a library version of as many of
> > the operations of btrfs-progs as possible and to migrate btrfs-progs to
> > use it.
> > 
> > Rather than simply refactoring the existing btrfs-progs code, the code
> > has to be written from scratch for a couple of reasons:
> > 
> > * A lot of the btrfs-progs code was not designed with a nice library API
> >   in mind in terms of reusability, naming, and error reporting.
> > * libbtrfsutil is licensed under the LGPL, whereas btrfs-progs is under
> >   the GPL, which makes it dubious to directly copy or move the code.
> > 
> > Eventually, most of the low-level btrfs-progs code should either live in
> > libbtrfsutil or the shared kernel/userspace filesystem code, and
> > btrfs-progs will just be the CLI wrapper.
> > 
> > This first commit just includes the build system changes, license,
> > README, and error reporting helper.
> > 
> > Signed-off-by: Omar Sandoval <osandov@fb.com>
> > ---
> >  .gitignore                  |   2 +
> >  Makefile                    |  47 +--
> >  libbtrfsutil/COPYING        | 674 ++++++++++++++++++++++++++++++++++++++++++++
> >  libbtrfsutil/COPYING.LESSER | 165 +++++++++++
> >  libbtrfsutil/README.md      |  32 +++
> >  libbtrfsutil/btrfsutil.h    |  72 +++++
> >  libbtrfsutil/errors.c       |  55 ++++
> >  7 files changed, 1031 insertions(+), 16 deletions(-)
> >  create mode 100644 libbtrfsutil/COPYING
> >  create mode 100644 libbtrfsutil/COPYING.LESSER
> >  create mode 100644 libbtrfsutil/README.md
> >  create mode 100644 libbtrfsutil/btrfsutil.h
> >  create mode 100644 libbtrfsutil/errors.c
> > 

[snip]

> > diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
> > new file mode 100644
> > index 00000000..fe1091ca
> > --- /dev/null
> > +++ b/libbtrfsutil/btrfsutil.h
> > @@ -0,0 +1,72 @@
> > +/*
> > + * Copyright (C) 2018 Facebook
> > + *
> > + * This file is part of libbtrfsutil.
> > + *
> > + * libbtrfsutil is free software: you can redistribute it and/or modify
> > + * it under the terms of the GNU Lesser General Public License as published by
> > + * the Free Software Foundation, either version 3 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * libbtrfsutil 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 Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public License
> > + * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#ifndef BTRFS_UTIL_H
> > +#define BTRFS_UTIL_H
> > +
> > +#ifdef __cplusplus
> > +extern "C" {
> > +#endif
> > +
> > +/**
> > + * enum btrfs_util_error - libbtrfsutil error codes.
> > + *
> > + * All functions in libbtrfsutil that can return an error return this type and
> > + * set errno.
> > + */
> 
> I totally understand that libbtrfsutils needs extra error numbers, but I
> didn't see similar practice, would you mind to give some existing
> example of such >0 error usage in other projects?
> (Just curious)

Sure, pthreads returns 0 on success, positive errnos on failure. libcurl
also uses positive error codes (https://curl.haxx.se/libcurl/c/libcurl-errors.html).
Those are just the first two I checked.

> Normally people would expect error values < 0 to indicate errors, just
> like glibc system call wrappers, which always return -1 to indicate errors.
> 
> > +enum btrfs_util_error {
> > +	BTRFS_UTIL_OK,
> > +	BTRFS_UTIL_ERROR_STOP_ITERATION,
> > +	BTRFS_UTIL_ERROR_NO_MEMORY,
> 
> Not sure if this is duplicated with -ENOMEM errno.
> 
> From my understanding, these extra numbers should be used to indicate
> extra error not definied in generic errno.h.
> 
> For NOT_BTRFS and NOT_SUBVOLUME they makes sense, but for NO_MEMORY, I'm
> really not sure.

So sometimes we return an errno, sometimes an enum btrfs_util_error? And
then we have to make sure to avoid collisions between the enum values
and errno values? That seems clunky.

Thanks for taking a look.

> > +	BTRFS_UTIL_ERROR_INVALID_ARGUMENT,
> > +	BTRFS_UTIL_ERROR_NOT_BTRFS,
> > +	BTRFS_UTIL_ERROR_NOT_SUBVOLUME,
> > +	BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND,
> > +	BTRFS_UTIL_ERROR_OPEN_FAILED,
> > +	BTRFS_UTIL_ERROR_RMDIR_FAILED,
> > +	BTRFS_UTIL_ERROR_UNLINK_FAILED,
> > +	BTRFS_UTIL_ERROR_STAT_FAILED,
> > +	BTRFS_UTIL_ERROR_STATFS_FAILED,
> > +	BTRFS_UTIL_ERROR_SEARCH_FAILED,
> > +	BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED,
> > +	BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED,
> > +	BTRFS_UTIL_ERROR_SUBVOL_SETFLAGS_FAILED,
> > +	BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED,
> > +	BTRFS_UTIL_ERROR_SNAP_CREATE_FAILED,
> > +	BTRFS_UTIL_ERROR_SNAP_DESTROY_FAILED,
> > +	BTRFS_UTIL_ERROR_DEFAULT_SUBVOL_FAILED,
> > +	BTRFS_UTIL_ERROR_SYNC_FAILED,
> > +	BTRFS_UTIL_ERROR_START_SYNC_FAILED,
> > +	BTRFS_UTIL_ERROR_WAIT_SYNC_FAILED,
> > +};

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

* Re: [PATCH 04/26] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id()
  2018-01-29 21:43     ` Omar Sandoval
@ 2018-01-30  6:54       ` Nikolay Borisov
  2018-02-01 16:28         ` David Sterba
  0 siblings, 1 reply; 47+ messages in thread
From: Nikolay Borisov @ 2018-01-30  6:54 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: linux-btrfs, kernel-team



On 29.01.2018 23:43, Omar Sandoval wrote:
> On Mon, Jan 29, 2018 at 12:24:26PM +0200, Nikolay Borisov wrote:
>> On 26.01.2018 20:40, Omar Sandoval wrote:
> [snip]
>>> +/*
>>> + * This intentionally duplicates btrfs_util_f_is_subvolume() instead of opening
>>> + * a file descriptor and calling it, because fstat() and fstatfs() don't accept
>>> + * file descriptors opened with O_PATH on old kernels (before v3.6 and before
>>> + * v3.12, respectively), but stat() and statfs() can be called on a path that
>>> + * the user doesn't have read or write permissions to.
>>> + */
>>> +__attribute__((visibility("default")))
>>
>> Why do we need to explicitly set the attribute visibility to default,
>> isn't it implicitly default already?
> 
> Ah, I forgot to add -fvisibility=hidden to the build rule when I ported
> this to the btrfs-progs Makefile, that's why that's there. I'll add
> -fvisibility=hidden to the Makefile.

Right, it could be a good idea to hide the visibility attribute behind
an eloquent macro i.e. (PUBLIC|LIBRARY)_FUNC or some such.

> 

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

* Re: [PATCH 04/26] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id()
  2018-01-30  6:54       ` Nikolay Borisov
@ 2018-02-01 16:28         ` David Sterba
  2018-02-02 19:14           ` Omar Sandoval
  0 siblings, 1 reply; 47+ messages in thread
From: David Sterba @ 2018-02-01 16:28 UTC (permalink / raw)
  To: Nikolay Borisov; +Cc: Omar Sandoval, linux-btrfs, kernel-team

On Tue, Jan 30, 2018 at 08:54:08AM +0200, Nikolay Borisov wrote:
> 
> 
> On 29.01.2018 23:43, Omar Sandoval wrote:
> > On Mon, Jan 29, 2018 at 12:24:26PM +0200, Nikolay Borisov wrote:
> >> On 26.01.2018 20:40, Omar Sandoval wrote:
> > [snip]
> >>> +/*
> >>> + * This intentionally duplicates btrfs_util_f_is_subvolume() instead of opening
> >>> + * a file descriptor and calling it, because fstat() and fstatfs() don't accept
> >>> + * file descriptors opened with O_PATH on old kernels (before v3.6 and before
> >>> + * v3.12, respectively), but stat() and statfs() can be called on a path that
> >>> + * the user doesn't have read or write permissions to.
> >>> + */
> >>> +__attribute__((visibility("default")))
> >>
> >> Why do we need to explicitly set the attribute visibility to default,
> >> isn't it implicitly default already?
> > 
> > Ah, I forgot to add -fvisibility=hidden to the build rule when I ported
> > this to the btrfs-progs Makefile, that's why that's there. I'll add
> > -fvisibility=hidden to the Makefile.
> 
> Right, it could be a good idea to hide the visibility attribute behind
> an eloquent macro i.e. (PUBLIC|LIBRARY)_FUNC or some such.

Macro would be better (but is not needed for the initial version).

Alternatively the library .sym file can externally track the exported
symbols and also track versioning.

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

* Re: [PATCH 25/26] btrfs-progs: deprecate libbtrfs helpers with libbtrfsutil equivalents
  2018-01-26 18:41 ` [PATCH 25/26] btrfs-progs: deprecate libbtrfs helpers with libbtrfsutil equivalents Omar Sandoval
@ 2018-02-02 14:49   ` David Sterba
  0 siblings, 0 replies; 47+ messages in thread
From: David Sterba @ 2018-02-02 14:49 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: linux-btrfs, kernel-team

On Fri, Jan 26, 2018 at 10:41:13AM -0800, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>
> 
> The old libbtrfs defines some helpers which do the same thing as some
> libbtrfsutil helpers. Reimplement the libbtrfs helpers in terms of the
> libbtrfsutil APIs and mark the libbtrfs versions as deprecated, which we
> could ideally get rid of eventually.

Can we keep both libraries separate, ie. no runtime dependency? The
libbtrfs still has some use internally, the deprecation should happen on
the application level.

If there are some exported functions in libbtrfs that are (and should)
be provided by the util library, then we can use the same .o for
building as a temporary solution.

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

* Re: [PATCH 04/26] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id()
  2018-02-01 16:28         ` David Sterba
@ 2018-02-02 19:14           ` Omar Sandoval
  0 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-02-02 19:14 UTC (permalink / raw)
  To: David Sterba; +Cc: Nikolay Borisov, linux-btrfs, kernel-team

On Thu, Feb 01, 2018 at 05:28:28PM +0100, David Sterba wrote:
> On Tue, Jan 30, 2018 at 08:54:08AM +0200, Nikolay Borisov wrote:
> > 
> > 
> > On 29.01.2018 23:43, Omar Sandoval wrote:
> > > On Mon, Jan 29, 2018 at 12:24:26PM +0200, Nikolay Borisov wrote:
> > >> On 26.01.2018 20:40, Omar Sandoval wrote:
> > > [snip]
> > >>> +/*
> > >>> + * This intentionally duplicates btrfs_util_f_is_subvolume() instead of opening
> > >>> + * a file descriptor and calling it, because fstat() and fstatfs() don't accept
> > >>> + * file descriptors opened with O_PATH on old kernels (before v3.6 and before
> > >>> + * v3.12, respectively), but stat() and statfs() can be called on a path that
> > >>> + * the user doesn't have read or write permissions to.
> > >>> + */
> > >>> +__attribute__((visibility("default")))
> > >>
> > >> Why do we need to explicitly set the attribute visibility to default,
> > >> isn't it implicitly default already?
> > > 
> > > Ah, I forgot to add -fvisibility=hidden to the build rule when I ported
> > > this to the btrfs-progs Makefile, that's why that's there. I'll add
> > > -fvisibility=hidden to the Makefile.
> > 
> > Right, it could be a good idea to hide the visibility attribute behind
> > an eloquent macro i.e. (PUBLIC|LIBRARY)_FUNC or some such.
> 
> Macro would be better (but is not needed for the initial version).
> 
> Alternatively the library .sym file can externally track the exported
> symbols and also track versioning.

I'll add a macro for this, thanks.

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

* Re: [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library"
  2018-01-26 18:51 ` [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Hugo Mills
@ 2018-02-02 23:04   ` Hans van Kranenburg
  0 siblings, 0 replies; 47+ messages in thread
From: Hans van Kranenburg @ 2018-02-02 23:04 UTC (permalink / raw)
  To: Hugo Mills, Omar Sandoval, linux-btrfs, kernel-team

Hi,

On 01/26/2018 07:51 PM, Hugo Mills wrote:
> On Fri, Jan 26, 2018 at 10:40:48AM -0800, Omar Sandoval wrote:
>> From: Omar Sandoval <osandov@fb.com>
>>
>> One of the features requests I get most often is a library to do the
>> sorts of operations that we do with btrfs-progs. We can shell out to
>> btrfs-progs, but the output format isn't always easily parsasble, and
>> shelling out isn't always ideal. There's libbtrfs, but it's very
>> incomplete, doesn't have a well thought out API, and is licensed under
>> the GPL, making it hard to use for many projects.
>>
>> libbtrfsutil is a new library written from scratch to address these
>> issues. The implementation is completely independent of the existing
>> btrfs-progs code, including kerncompat.h, and has a clean API and naming
>> scheme. It is licensed under the LGPL. It also includes Python bindings
>> by default. I will maintain the library code.

Insert mandatory picture here:

https://memegenerator.net/img/instances/12076366/python-all-the-things.jpg

>    *speechless*
> 
>    That's awesome, Omar (although with the python bindings, you've
> probably just ruined Hans' day ;) ).

Not quite, or should I say, quite the opposite, actually.

I can think of a few different python libs related to btrfs right now...

1. This one, which works on the same level as the btrfs-progs command
line utils and provides the same set of functionality, with the same
input and output, only way easier to handle when automating things. The
audience is the same audience as who would else try to 'shell out' to
btrfs-progs and then do painful parsing of the output.

2. A lib for advanced btrfs users (more of a niche audience) who want to
get a peek behind the curtains of what is going on in their online
filesystem. Directly do searches in metadata, directly call an ioctl
etc, either for educational purposes, or to e.g. do things like write a
small special purpose dedupe program, visualize things etc. This is the
python3-btrfs lib that I've been working on for some time now.

3. A lib for btrfs developers who want to script working on offline
fileystems. This one does not exist yet. Well, it's a bit of an unborn
baby now, my first brain dump:
  https://github.com/knorrie/btrfs-progs/blob/python/python3/README.md
This would be the lib that exposes the (to be revised and improved)
internal libbtrfs (I guess).

All of these are not competitors. They serve different purpose and a
different audience. So I'm happy to see number 1 also happening. :-)

>> Patch 1 is a preparation cleanup which can go in independently. Patch 2
>> adds the build system stuff for the library, and patch 3 does the same
>> for the Python bindings. Patches 4-14 implement the library helpers,
>> currently subvolume helpers and the sync ioctls. Patches 15-26 replace
>> the btrfs-progs and libbtrfs code to use libbtrfsutil instead. I took
>> care to preserve backwards-compatibility. `btrfs subvol list` in
>> particular had some buggy behaviors for -o and -a that I emulated in the
>> new code, see the comments in the code.

+1 for starting to clean up that code. Especially the whole subvolume
list part looks like a great improvement.

>> These patches are also available on my GitHub:
>> https://github.com/osandov/btrfs-progs/tree/libbtrfsutil. That branch
>> will rebase as I update this series.
>>
>> Please share feedback regarding the API, implementation, or anything
>> else.

Great work, and I'm of course also interested in what you think about
the lib nr.3 idea, since I didn't throw it around on the list before
yet. :-)

-- 
Hans van Kranenburg

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

* Re: [PATCH 21/26] btrfs-progs: use libbtrfsutil for subvol show
  2018-01-26 18:41 ` [PATCH 21/26] btrfs-progs: use libbtrfsutil for subvol show Omar Sandoval
@ 2018-02-02 23:18   ` Hans van Kranenburg
  2018-02-02 23:29     ` Omar Sandoval
  0 siblings, 1 reply; 47+ messages in thread
From: Hans van Kranenburg @ 2018-02-02 23:18 UTC (permalink / raw)
  To: Omar Sandoval, linux-btrfs

On 01/26/2018 07:41 PM, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>
> 
> Now implemented with btrfs_util_subvolume_path(),
> btrfs_util_subvolume_info(), and subvolume iterators.
> 
> Signed-off-by: Omar Sandoval <osandov@fb.com>
> ---
>  cmds-subvolume.c | 150 ++++++++++++++++++++++++++++++++++++-------------------
>  utils.c          | 118 -------------------------------------------
>  utils.h          |   5 --
>  3 files changed, 99 insertions(+), 174 deletions(-)
> 
> diff --git a/cmds-subvolume.c b/cmds-subvolume.c
> index c5e03011..b969fc88 100644
> --- a/cmds-subvolume.c
> +++ b/cmds-subvolume.c
>
>
> [...]
>  	} else
>  		strcpy(tstr, "-");
>  	printf("\tCreation time: \t\t%s\n", tstr);
>  
> -	printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
> -	printf("\tGeneration: \t\t%llu\n", get_ri.gen);
> -	printf("\tGen at creation: \t%llu\n", get_ri.ogen);
> -	printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
> -	printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
> +	printf("\tSubvolume ID: \t\t%" PRIu64 "\n", subvol.id);
> +	printf("\tGeneration: \t\t%" PRIu64 "\n", subvol.generation);
> +	printf("\tGen at creation: \t%" PRIu64 "\n", subvol.otransid);
> +	printf("\tParent ID: \t\t%" PRIu64 "\n", subvol.parent_id);
> +	printf("\tTop level ID: \t\t%" PRIu64 "\n", subvol.parent_id);
>  
> -	if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
> +	if (subvol.flags & BTRFS_ROOT_SUBVOL_RDONLY)
>  		printf("\tFlags: \t\t\treadonly\n");
>  	else
>  		printf("\tFlags: \t\t\t-\n");
>  
>  	/* print the snapshots of the given subvol if any*/
>  	printf("\tSnapshot(s):\n");
> -	filter_set = btrfs_list_alloc_filter_set();
> -	btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
> -				(u64)(unsigned long)get_ri.uuid);
> -	btrfs_list_setup_print_column(BTRFS_LIST_PATH);
>  
> -	fd = open_file_or_dir(fullpath, &dirstream1);
> -	if (fd < 0) {
> -		fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
> -		goto out;
> +	err = btrfs_util_f_create_subvolume_iterator(fd,
> +						     BTRFS_FS_TREE_OBJECTID,
> +						     0, &iter);
> +
> [...]
When you have enough subvolumes in a filesystem, let's say 100000 (yes,
that sometimes happens), the current btrfs sub list is quite unusable,
which is kind of expected. But, currently, sub show is also unusable
because it still starts loading a list of all subvolumes to be able to
print the 'snapshots, if any'.

So I guess that this new sub show will at least print the info first and
then use the iterator and start crawling through the list, which can be
interrupted? At least you get the relevant info first then. :-)

-- 
Hans van Kranenburg

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

* Re: [PATCH 21/26] btrfs-progs: use libbtrfsutil for subvol show
  2018-02-02 23:18   ` Hans van Kranenburg
@ 2018-02-02 23:29     ` Omar Sandoval
  0 siblings, 0 replies; 47+ messages in thread
From: Omar Sandoval @ 2018-02-02 23:29 UTC (permalink / raw)
  To: Hans van Kranenburg; +Cc: linux-btrfs

On Sat, Feb 03, 2018 at 12:18:02AM +0100, Hans van Kranenburg wrote:
> On 01/26/2018 07:41 PM, Omar Sandoval wrote:
> > From: Omar Sandoval <osandov@fb.com>
> > 
> > Now implemented with btrfs_util_subvolume_path(),
> > btrfs_util_subvolume_info(), and subvolume iterators.
> > 
> > Signed-off-by: Omar Sandoval <osandov@fb.com>
> > ---
> >  cmds-subvolume.c | 150 ++++++++++++++++++++++++++++++++++++-------------------
> >  utils.c          | 118 -------------------------------------------
> >  utils.h          |   5 --
> >  3 files changed, 99 insertions(+), 174 deletions(-)
> > 
> > diff --git a/cmds-subvolume.c b/cmds-subvolume.c
> > index c5e03011..b969fc88 100644
> > --- a/cmds-subvolume.c
> > +++ b/cmds-subvolume.c
> >
> >
> > [...]
> >  	} else
> >  		strcpy(tstr, "-");
> >  	printf("\tCreation time: \t\t%s\n", tstr);
> >  
> > -	printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
> > -	printf("\tGeneration: \t\t%llu\n", get_ri.gen);
> > -	printf("\tGen at creation: \t%llu\n", get_ri.ogen);
> > -	printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
> > -	printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
> > +	printf("\tSubvolume ID: \t\t%" PRIu64 "\n", subvol.id);
> > +	printf("\tGeneration: \t\t%" PRIu64 "\n", subvol.generation);
> > +	printf("\tGen at creation: \t%" PRIu64 "\n", subvol.otransid);
> > +	printf("\tParent ID: \t\t%" PRIu64 "\n", subvol.parent_id);
> > +	printf("\tTop level ID: \t\t%" PRIu64 "\n", subvol.parent_id);
> >  
> > -	if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
> > +	if (subvol.flags & BTRFS_ROOT_SUBVOL_RDONLY)
> >  		printf("\tFlags: \t\t\treadonly\n");
> >  	else
> >  		printf("\tFlags: \t\t\t-\n");
> >  
> >  	/* print the snapshots of the given subvol if any*/
> >  	printf("\tSnapshot(s):\n");
> > -	filter_set = btrfs_list_alloc_filter_set();
> > -	btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
> > -				(u64)(unsigned long)get_ri.uuid);
> > -	btrfs_list_setup_print_column(BTRFS_LIST_PATH);
> >  
> > -	fd = open_file_or_dir(fullpath, &dirstream1);
> > -	if (fd < 0) {
> > -		fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
> > -		goto out;
> > +	err = btrfs_util_f_create_subvolume_iterator(fd,
> > +						     BTRFS_FS_TREE_OBJECTID,
> > +						     0, &iter);
> > +
> > [...]
> When you have enough subvolumes in a filesystem, let's say 100000 (yes,
> that sometimes happens), the current btrfs sub list is quite unusable,
> which is kind of expected. But, currently, sub show is also unusable
> because it still starts loading a list of all subvolumes to be able to
> print the 'snapshots, if any'.
> 
> So I guess that this new sub show will at least print the info first and
> then use the iterator and start crawling through the list, which can be
> interrupted? At least you get the relevant info first then. :-)

Right, and since we don't load everything into memory all at once, both
show and list will be able to output subvolumes one by one.

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

end of thread, other threads:[~2018-02-02 23:29 UTC | newest]

Thread overview: 47+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-26 18:40 [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
2018-01-26 18:40 ` [PATCH 01/26] btrfs-progs: get rid of undocumented qgroup inheritance options Omar Sandoval
2018-01-27  4:35   ` Qu Wenruo
2018-01-26 18:40 ` [PATCH 02/26] Add libbtrfsutil Omar Sandoval
2018-01-29  2:16   ` Qu Wenruo
2018-01-29 23:45     ` Omar Sandoval
2018-01-26 18:40 ` [PATCH 03/26] libbtrfsutil: add Python bindings Omar Sandoval
2018-01-26 18:40 ` [PATCH 04/26] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id() Omar Sandoval
2018-01-29 10:24   ` Nikolay Borisov
2018-01-29 21:43     ` Omar Sandoval
2018-01-30  6:54       ` Nikolay Borisov
2018-02-01 16:28         ` David Sterba
2018-02-02 19:14           ` Omar Sandoval
2018-01-26 18:40 ` [PATCH 05/26] libbtrfsutil: add qgroup inheritance helpers Omar Sandoval
2018-01-26 18:40 ` [PATCH 06/26] libbtrfsutil: add btrfs_util_create_subvolume() Omar Sandoval
2018-01-26 18:40 ` [PATCH 07/26] libbtrfsutil: add btrfs_util_subvolume_info() Omar Sandoval
2018-01-26 18:40 ` [PATCH 08/26] libbtrfsutil: add btrfs_util_[gs]et_read_only() Omar Sandoval
2018-01-26 18:40 ` [PATCH 09/26] libbtrfsutil: add btrfs_util_[gs]et_default_subvolume() Omar Sandoval
2018-01-26 18:40 ` [PATCH 10/26] libbtrfsutil: add subvolume iterator helpers Omar Sandoval
2018-01-26 18:40 ` [PATCH 11/26] libbtrfsutil: add btrfs_util_create_snapshot() Omar Sandoval
2018-01-26 19:31   ` Goffredo Baroncelli
2018-01-26 19:46     ` Omar Sandoval
2018-01-27  5:00       ` Qu Wenruo
2018-01-27  5:45         ` Omar Sandoval
2018-01-27 14:19           ` Goffredo Baroncelli
2018-01-27 16:31           ` Nikolay Borisov
2018-01-27 18:54             ` Omar Sandoval
2018-01-26 18:41 ` [PATCH 12/26] libbtrfsutil: add btrfs_util_delete_subvolume() Omar Sandoval
2018-01-26 18:41 ` [PATCH 13/26] libbtrfsutil: add btrfs_util_deleted_subvolumes() Omar Sandoval
2018-01-26 18:41 ` [PATCH 14/26] libbtrfsutil: add filesystem sync helpers Omar Sandoval
2018-01-26 18:41 ` [PATCH 15/26] btrfs-progs: use libbtrfsutil for read-only property Omar Sandoval
2018-01-26 18:41 ` [PATCH 16/26] btrfs-progs: use libbtrfsutil for sync ioctls Omar Sandoval
2018-01-26 18:41 ` [PATCH 17/26] btrfs-progs: use libbtrfsutil for set-default Omar Sandoval
2018-01-26 18:41 ` [PATCH 18/26] btrfs-progs: use libbtrfsutil for get-default Omar Sandoval
2018-01-26 18:41 ` [PATCH 19/26] btrfs-progs: use libbtrfsutil for subvol create and snapshot Omar Sandoval
2018-01-26 18:41 ` [PATCH 20/26] btrfs-progs: use libbtrfsutil for subvol delete Omar Sandoval
2018-01-26 18:41 ` [PATCH 21/26] btrfs-progs: use libbtrfsutil for subvol show Omar Sandoval
2018-02-02 23:18   ` Hans van Kranenburg
2018-02-02 23:29     ` Omar Sandoval
2018-01-26 18:41 ` [PATCH 22/26] btrfs-progs: use libbtrfsutil for subvol sync Omar Sandoval
2018-01-26 18:41 ` [PATCH 23/26] btrfs-progs: replace test_issubvolume() with btrfs_util_is_subvolume() Omar Sandoval
2018-01-26 18:41 ` [PATCH 24/26] btrfs-progs: add recursive snapshot/delete using libbtrfsutil Omar Sandoval
2018-01-26 18:41 ` [PATCH 25/26] btrfs-progs: deprecate libbtrfs helpers with libbtrfsutil equivalents Omar Sandoval
2018-02-02 14:49   ` David Sterba
2018-01-26 18:41 ` [PATCH 26/26] btrfs-progs: use libbtrfsutil for subvolume list Omar Sandoval
2018-01-26 18:51 ` [PATCH 00/26] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Hugo Mills
2018-02-02 23:04   ` Hans van Kranenburg

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.