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

From: Omar Sandoval <osandov@fb.com>

Hi,

This is v2 of "btrfs-progs as a library".

Most of the changes since v1 are small:

- Rebased onto v4.15
- Split up btrfs_util_subvolume_path() which was accidentally squashed together
  with the commit adding btrfs_util_create_subvolume()
- Renamed btrfs_util_f_* functions to btrfs_util_*_fd for clarity
- Added -fvisibility=hidden and a macro for
  __attribute__((visibility("default")))
- Changed to use semantic versioning
- Fixed missing install of btrfsutil.h
- Documented functions which require root or are non-atomic
- Added a missing license to setup.py

The bigger change is in the last two patches. Dave requested that I get
rid of the runtime dependency of libbtrfsutil from libbtrfs. The easiest
way to do this was to remove the btrfs_list_subvols_print()
implementation from libbtrfs and put it in cmds-subvolume.c (details in
patch 26). I'm open to alternatives.

Please share feedback, thanks!

Cover letter from v1:

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.

Omar Sandoval (27):
  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_path()
  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: use libbtrfsutil for subvolume list
  btrfs-progs: deprecate libbtrfs helpers

 .gitignore                                   |    2 +
 Documentation/btrfs-subvolume.asciidoc       |   14 +-
 INSTALL                                      |    4 +
 Makefile                                     |  109 +-
 Makefile.inc.in                              |    4 +-
 btrfs-list.c                                 |  810 +----------
 btrfs-list.h                                 |  104 +-
 cmds-filesystem.c                            |   19 +-
 cmds-inspect.c                               |   10 +-
 cmds-qgroup.c                                |   21 +-
 cmds-receive.c                               |   13 +-
 cmds-subvolume.c                             | 1877 ++++++++++++++++++--------
 configure.ac                                 |   15 +
 libbtrfsutil/COPYING                         |  674 +++++++++
 libbtrfsutil/COPYING.LESSER                  |  165 +++
 libbtrfsutil/README.md                       |   38 +
 libbtrfsutil/btrfsutil.h                     |  648 +++++++++
 libbtrfsutil/btrfsutil_internal.h            |   40 +
 libbtrfsutil/errors.c                        |   55 +
 libbtrfsutil/filesystem.c                    |  103 ++
 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                 |  111 ++
 libbtrfsutil/python/subvolume.c              |  667 +++++++++
 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                        |   83 ++
 libbtrfsutil/subvolume.c                     | 1368 +++++++++++++++++++
 messages.h                                   |   13 +
 props.c                                      |   69 +-
 qgroup.c                                     |  106 --
 qgroup.h                                     |    4 -
 send-utils.c                                 |   25 +-
 utils.c                                      |  152 +--
 utils.h                                      |    7 -
 41 files changed, 6899 insertions(+), 1859 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/btrfsutil_internal.h
 create mode 100644 libbtrfsutil/errors.c
 create mode 100644 libbtrfsutil/filesystem.c
 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] 64+ messages in thread

* [PATCH v2 01/27] btrfs-progs: get rid of undocumented qgroup inheritance options
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
@ 2018-02-15 19:04 ` Omar Sandoval
  2018-02-15 19:04 ` [PATCH v2 02/27] Add libbtrfsutil Omar Sandoval
                   ` (26 subsequent siblings)
  27 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:04 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 8a473f7a..edcb4f11 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) {
@@ -662,18 +655,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) {
@@ -684,13 +670,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 b5b893f4..b107a683 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -1324,45 +1324,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] 64+ messages in thread

* [PATCH v2 02/27] Add libbtrfsutil
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
  2018-02-15 19:04 ` [PATCH v2 01/27] btrfs-progs: get rid of undocumented qgroup inheritance options Omar Sandoval
@ 2018-02-15 19:04 ` Omar Sandoval
  2018-02-20 17:32   ` Liu Bo
  2018-02-15 19:04 ` [PATCH v2 03/27] libbtrfsutil: add Python bindings Omar Sandoval
                   ` (25 subsequent siblings)
  27 siblings, 1 reply; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:04 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                          |  72 ++--
 Makefile.inc.in                   |   2 +-
 libbtrfsutil/COPYING              | 674 ++++++++++++++++++++++++++++++++++++++
 libbtrfsutil/COPYING.LESSER       | 165 ++++++++++
 libbtrfsutil/README.md            |  35 ++
 libbtrfsutil/btrfsutil.h          |  76 +++++
 libbtrfsutil/btrfsutil_internal.h |  40 +++
 libbtrfsutil/errors.c             |  55 ++++
 9 files changed, 1100 insertions(+), 21 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/btrfsutil_internal.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 00e21379..7fb70d06 100644
--- a/Makefile
+++ b/Makefile
@@ -73,10 +73,20 @@ CFLAGS = $(SUBST_CFLAGS) \
 	 -fPIC \
 	 -I$(TOPDIR) \
 	 -I$(TOPDIR)/kernel-lib \
+	 -I$(TOPDIR)/libbtrfsutil \
 	 $(EXTRAWARN_CFLAGS) \
 	 $(DEBUG_CFLAGS_INTERNAL) \
 	 $(EXTRA_CFLAGS)
 
+LIBBTRFSUTIL_CFLAGS = $(SUBST_CFLAGS) \
+		      $(CSTD) \
+		      -D_GNU_SOURCE \
+		      -fPIC \
+		      -fvisibility=hidden \
+		      -I$(TOPDIR)/libbtrfsutil \
+		      $(EXTRAWARN_CFLAGS) \
+		      $(EXTRA_CFLAGS)
+
 LDFLAGS = $(SUBST_LDFLAGS) \
 	  -rdynamic -L$(TOPDIR) \
 	  $(DEBUG_LDFLAGS_INTERNAL) \
@@ -121,12 +131,17 @@ 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_major := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_MAJOR ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
+libbtrfsutil_minor := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_MINOR ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
+libbtrfsutil_patch := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_PATCH ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
+libbtrfsutil_version := $(libbtrfsutil_major).$(libbtrfsutil_minor).$(libbtrfsutil_patch)
+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 mkfs/rootdir.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)
 
 udev_rules = 64-btrfs-dm.rules
 
@@ -246,11 +261,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
-headers = $(libbtrfs_headers)
+lib_links = libbtrfs.so.0 libbtrfs.so libbtrfsutil.so.$(libbtrfsutil_major) libbtrfsutil.so
 
 # make C=1 to enable sparse
 ifdef C
@@ -287,7 +301,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-%,%,$@)"
@@ -351,20 +365,35 @@ 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 $< $@
+
+libbtrfsutil/%.o: libbtrfsutil/%.c
+	@echo "    [CC]     $@"
+	$(Q)$(CC) $(LIBBTRFSUTIL_CFLAGS) -o $@ -c $< -o $@
 
-$(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) $(LIBBTRFSUTIL_CFLAGS) $(libbtrfsutil_objects) \
+		-shared -Wl,-soname,libbtrfsutil.so.$(libbtrfsutil_major) -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.$(libbtrfsutil_major) 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))
@@ -483,7 +512,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 && \
@@ -547,7 +576,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"
@@ -575,8 +605,9 @@ install: $(libs) $(progs_install) $(INSTALLDIRS)
 	$(INSTALL) -m755 -d $(DESTDIR)$(libdir)
 	$(INSTALL) $(libs) $(DESTDIR)$(libdir)
 	cp -a $(lib_links) $(DESTDIR)$(libdir)
-	$(INSTALL) -m755 -d $(DESTDIR)$(incdir)
-	$(INSTALL) -m644 $(headers) $(DESTDIR)$(incdir)
+	$(INSTALL) -m755 -d $(DESTDIR)$(incdir)/btrfs
+	$(INSTALL) -m644 $(libbtrfs_headers) $(DESTDIR)$(incdir)/btrfs
+	$(INSTALL) -m644 libbtrfsutil/btrfsutil.h $(DESTDIR)$(incdir)
 ifneq ($(udevdir),)
 	$(INSTALL) -m755 -d $(DESTDIR)$(udevruledir)
 	$(INSTALL) -m644 $(udev_rules) $(DESTDIR)$(udevruledir)
@@ -594,8 +625,9 @@ $(INSTALLDIRS):
 
 uninstall:
 	$(Q)$(MAKE) $(MAKEOPTS) -C Documentation uninstall
-	cd $(DESTDIR)$(incdir); $(RM) -f -- $(headers)
-	$(RMDIR) -p --ignore-fail-on-non-empty -- $(DESTDIR)$(incdir)
+	cd $(DESTDIR)$(incdir)/btrfs; $(RM) -f -- $(libbtrfs_headers)
+	$(RMDIR) -p --ignore-fail-on-non-empty -- $(DESTDIR)$(incdir)/btrfs
+	cd $(DESTDIR)$(incdir); $(RM) -f -- btrfsutil.h
 	cd $(DESTDIR)$(libdir); $(RM) -f -- $(lib_links) $(libs)
 	cd $(DESTDIR)$(bindir); $(RM) -f -- btrfsck fsck.btrfs $(progs_install)
 
diff --git a/Makefile.inc.in b/Makefile.inc.in
index 56271903..b53bef80 100644
--- a/Makefile.inc.in
+++ b/Makefile.inc.in
@@ -27,7 +27,7 @@ prefix ?= @prefix@
 exec_prefix = @exec_prefix@
 bindir = @bindir@
 libdir ?= @libdir@
-incdir = @includedir@/btrfs
+incdir = @includedir@
 udevdir = @UDEVDIR@
 udevruledir = ${udevdir}/rules.d
 
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..ee4c6a1d
--- /dev/null
+++ b/libbtrfsutil/README.md
@@ -0,0 +1,35 @@
+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.
+
+libbtrfsutil is semantically versioned separately from btrfs-progs. It is the
+maintainers' responsibility to bump the version as needed (at most once per
+release of btrfs-progs).
+
+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 (i.e., we don't want to bump
+  the library major version if we don't have to)
diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
new file mode 100644
index 00000000..867418f2
--- /dev/null
+++ b/libbtrfsutil/btrfsutil.h
@@ -0,0 +1,76 @@
+/*
+ * 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
+
+#define BTRFS_UTIL_VERSION_MAJOR 1
+#define BTRFS_UTIL_VERSION_MINOR 0
+#define BTRFS_UTIL_VERSION_PATCH 0
+
+#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/btrfsutil_internal.h b/libbtrfsutil/btrfsutil_internal.h
new file mode 100644
index 00000000..bdf8840a
--- /dev/null
+++ b/libbtrfsutil/btrfsutil_internal.h
@@ -0,0 +1,40 @@
+/*
+ * 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>
+
+#include "btrfsutil.h"
+
+#define PUBLIC __attribute__((visibility("default")))
+
+#define le16_to_cpu __le16_to_cpu
+#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/errors.c b/libbtrfsutil/errors.c
new file mode 100644
index 00000000..634edc65
--- /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_internal.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",
+};
+
+PUBLIC 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] 64+ messages in thread

* [PATCH v2 03/27] libbtrfsutil: add Python bindings
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
  2018-02-15 19:04 ` [PATCH v2 01/27] btrfs-progs: get rid of undocumented qgroup inheritance options Omar Sandoval
  2018-02-15 19:04 ` [PATCH v2 02/27] Add libbtrfsutil Omar Sandoval
@ 2018-02-15 19:04 ` Omar Sandoval
  2018-02-21 13:47   ` David Sterba
  2018-02-22  1:44   ` Misono, Tomohiro
  2018-02-15 19:04 ` [PATCH v2 04/27] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id() Omar Sandoval
                   ` (24 subsequent siblings)
  27 siblings, 2 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:04 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          | 108 ++++++++++++++++++
 libbtrfsutil/python/tests/__init__.py |   0
 11 files changed, 601 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 7fb70d06..95ee9678 100644
--- a/Makefile
+++ b/Makefile
@@ -154,8 +154,10 @@ endif
 
 ifeq ($(BUILD_VERBOSE),1)
   Q =
+  SETUP_PY_Q =
 else
   Q = @
+  SETUP_PY_Q = -q
 endif
 
 ifeq ("$(origin D)", "command line")
@@ -302,6 +304,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-%,%,$@)"
@@ -345,6 +350,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
@@ -395,6 +410,15 @@ libbtrfsutil.so.$(libbtrfsutil_major) libbtrfsutil.so: libbtrfsutil.so.$(libbtrf
 	@echo "    [LN]     $@"
 	$(Q)$(LN_S) -f $< $@
 
+ifeq ($(PYTHON_BINDINGS),1)
+libbtrfsutil_python: libbtrfsutil.so libbtrfsutil/btrfsutil.h
+	@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))
 
@@ -578,6 +602,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"
@@ -613,6 +641,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 b53bef80..159d38ed 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 3afcdb47..50d36e4f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -195,6 +195,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.
@@ -249,6 +262,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 ee4c6a1d..0c8eba44 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
 -----------
@@ -33,3 +34,5 @@ A few guidelines:
   type specific to `libbtrfsutil`)
 * Preserve API and ABI compatability at all times (i.e., we don't want to bump
   the library major version if we don't have to)
+* Include Python bindings for all interfaces
+* Write tests for all interfaces
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..cd8a6048
--- /dev/null
+++ b/libbtrfsutil/python/setup.py
@@ -0,0 +1,108 @@
+#!/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 get_version():
+    with open('../btrfsutil.h', 'r') as f:
+        btrfsutil_h = f.read()
+    major = re.search(r'^#define BTRFS_UTIL_VERSION_MAJOR ([0-9]+)$',
+                      btrfsutil_h, flags=re.MULTILINE).group(1)
+    minor = re.search(r'^#define BTRFS_UTIL_VERSION_MINOR ([0-9]+)$',
+                      btrfsutil_h, flags=re.MULTILINE).group(1)
+    patch = re.search(r'^#define BTRFS_UTIL_VERSION_PATCH ([0-9]+)$',
+                      btrfsutil_h, flags=re.MULTILINE).group(1)
+    return major + '.' + minor + '.' + patch
+
+
+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'],
+)
+
+setup(
+    name='btrfsutil',
+    version=get_version(),
+    description='Library for managing Btrfs filesystems',
+    url='https://github.com/kdave/btrfs-progs',
+    license='GPLv3',
+    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] 64+ messages in thread

* [PATCH v2 04/27] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id()
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (2 preceding siblings ...)
  2018-02-15 19:04 ` [PATCH v2 03/27] libbtrfsutil: add Python bindings Omar Sandoval
@ 2018-02-15 19:04 ` Omar Sandoval
  2018-02-21 11:43   ` David Sterba
  2018-02-15 19:04 ` [PATCH v2 05/27] libbtrfsutil: add qgroup inheritance helpers Omar Sandoval
                   ` (23 subsequent siblings)
  27 siblings, 1 reply; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:04 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                    | 127 ++++++++++++++++++++++++++++
 9 files changed, 373 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 95ee9678..58b3eca4 100644
--- a/Makefile
+++ b/Makefile
@@ -135,7 +135,7 @@ libbtrfsutil_major := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_MAJOR ([0-
 libbtrfsutil_minor := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_MINOR ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
 libbtrfsutil_patch := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_PATCH ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
 libbtrfsutil_version := $(libbtrfsutil_major).$(libbtrfsutil_minor).$(libbtrfsutil_patch)
-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 mkfs/rootdir.o
diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 867418f2..ca36f695 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -20,6 +20,8 @@
 #ifndef BTRFS_UTIL_H
 #define BTRFS_UTIL_H
 
+#include <stdint.h>
+
 #define BTRFS_UTIL_VERSION_MAJOR 1
 #define BTRFS_UTIL_VERSION_MINOR 0
 #define BTRFS_UTIL_VERSION_PATCH 0
@@ -69,6 +71,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_is_subvolume_fd() - See btrfs_util_is_subvolume().
+ */
+enum btrfs_util_error btrfs_util_is_subvolume_fd(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_subvolume_id_fd() - See btrfs_util_subvolume_id().
+ */
+enum btrfs_util_error btrfs_util_subvolume_id_fd(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 cd8a6048..57249829 100755
--- a/libbtrfsutil/python/setup.py
+++ b/libbtrfsutil/python/setup.py
@@ -91,6 +91,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..4aab06a5
--- /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_is_subvolume_fd(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_subvolume_id_fd(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..04988c14
--- /dev/null
+++ b/libbtrfsutil/subvolume.c
@@ -0,0 +1,127 @@
+/*
+ * 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_internal.h"
+
+/*
+ * This intentionally duplicates btrfs_util_is_subvolume_fd() 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.
+ */
+PUBLIC 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;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_is_subvolume_fd(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;
+}
+
+PUBLIC 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_subvolume_id_fd(fd, id_ret);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_subvolume_id_fd(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] 64+ messages in thread

* [PATCH v2 05/27] libbtrfsutil: add qgroup inheritance helpers
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (3 preceding siblings ...)
  2018-02-15 19:04 ` [PATCH v2 04/27] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id() Omar Sandoval
@ 2018-02-15 19:04 ` Omar Sandoval
  2018-02-15 19:04 ` [PATCH v2 06/27] libbtrfsutil: add btrfs_util_create_subvolume() Omar Sandoval
                   ` (22 subsequent siblings)
  27 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:04 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                    |  83 +++++++++++++++++
 8 files changed, 335 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 58b3eca4..fa32c50d 100644
--- a/Makefile
+++ b/Makefile
@@ -135,7 +135,8 @@ libbtrfsutil_major := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_MAJOR ([0-
 libbtrfsutil_minor := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_MINOR ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
 libbtrfsutil_patch := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_PATCH ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
 libbtrfsutil_version := $(libbtrfsutil_major).$(libbtrfsutil_minor).$(libbtrfsutil_patch)
-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 mkfs/rootdir.o
diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index ca36f695..76bf0b60 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>
 
 #define BTRFS_UTIL_VERSION_MAJOR 1
@@ -102,6 +103,50 @@ enum btrfs_util_error btrfs_util_subvolume_id(const char *path,
  */
 enum btrfs_util_error btrfs_util_subvolume_id_fd(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 57249829..06857084 100755
--- a/libbtrfsutil/python/setup.py
+++ b/libbtrfsutil/python/setup.py
@@ -91,6 +91,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..d2916184
--- /dev/null
+++ b/libbtrfsutil/qgroup.c
@@ -0,0 +1,83 @@
+/*
+ * 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_internal.h"
+
+PUBLIC 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;
+}
+
+PUBLIC void btrfs_util_destroy_qgroup_inherit(struct btrfs_util_qgroup_inherit *inherit)
+{
+	free(inherit);
+}
+
+PUBLIC 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;
+}
+
+PUBLIC 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] 64+ messages in thread

* [PATCH v2 06/27] libbtrfsutil: add btrfs_util_create_subvolume()
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (4 preceding siblings ...)
  2018-02-15 19:04 ` [PATCH v2 05/27] libbtrfsutil: add qgroup inheritance helpers Omar Sandoval
@ 2018-02-15 19:04 ` Omar Sandoval
  2018-02-23  8:24   ` Misono, Tomohiro
  2018-02-15 19:04 ` [PATCH v2 07/27] libbtrfsutil: add btrfs_util_subvolume_path() Omar Sandoval
                   ` (21 subsequent siblings)
  27 siblings, 1 reply; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:04 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                    |  34 +++++++++
 libbtrfsutil/python/btrfsutilpy.h           |   1 +
 libbtrfsutil/python/module.c                |   8 ++
 libbtrfsutil/python/subvolume.c             |  29 +++++++
 libbtrfsutil/python/tests/test_qgroup.py    |  11 +++
 libbtrfsutil/python/tests/test_subvolume.py |  49 +++++++++++-
 libbtrfsutil/subvolume.c                    | 114 ++++++++++++++++++++++++++++
 7 files changed, 245 insertions(+), 1 deletion(-)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 76bf0b60..5d67f111 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -105,6 +105,40 @@ enum btrfs_util_error btrfs_util_subvolume_id_fd(int fd, uint64_t *id_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_create_subvolume_fd() - 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_create_subvolume_fd(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/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
index a36f2671..87d47ae0 100644
--- a/libbtrfsutil/python/btrfsutilpy.h
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -60,6 +60,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 *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..69bba704 100644
--- a/libbtrfsutil/python/module.c
+++ b/libbtrfsutil/python/module.c
@@ -144,6 +144,14 @@ 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"},
+	{"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 4aab06a5..6f2080ee 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -71,3 +71,32 @@ PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds)
 	path_cleanup(&path);
 	return PyLong_FromUnsignedLongLong(id);
 }
+
+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..f6c5958d 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,50 @@ 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_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 04988c14..8efbdd43 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>
@@ -125,3 +127,115 @@ PUBLIC enum btrfs_util_error btrfs_util_subvolume_id_fd(int fd,
 
 	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;
+}
+
+PUBLIC 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_create_subvolume_fd(parent_fd, name, flags,
+					    async_transid, qgroup_inherit);
+	SAVE_ERRNO_AND_CLOSE(parent_fd);
+	return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_create_subvolume_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 = {};
+	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] 64+ messages in thread

* [PATCH v2 07/27] libbtrfsutil: add btrfs_util_subvolume_path()
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (5 preceding siblings ...)
  2018-02-15 19:04 ` [PATCH v2 06/27] libbtrfsutil: add btrfs_util_create_subvolume() Omar Sandoval
@ 2018-02-15 19:04 ` Omar Sandoval
  2018-02-23  6:27   ` Misono, Tomohiro
  2018-02-15 19:04 ` [PATCH v2 08/27] libbtrfsutil: add btrfs_util_subvolume_info() Omar Sandoval
                   ` (20 subsequent siblings)
  27 siblings, 1 reply; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:04 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

We can just walk up root backrefs with BTRFS_IOC_TREE_SEARCH and inode
paths with BTRFS_IOC_INO_LOOKUP.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 libbtrfsutil/btrfsutil.h                    |  21 +++++
 libbtrfsutil/python/btrfsutilpy.h           |   2 +-
 libbtrfsutil/python/module.c                |   8 ++
 libbtrfsutil/python/subvolume.c             |  30 +++++++
 libbtrfsutil/python/tests/test_subvolume.py |  31 +++++++
 libbtrfsutil/subvolume.c                    | 125 ++++++++++++++++++++++++++++
 6 files changed, 216 insertions(+), 1 deletion(-)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 5d67f111..f96c9c4e 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -103,6 +103,27 @@ enum btrfs_util_error btrfs_util_subvolume_id(const char *path,
  */
 enum btrfs_util_error btrfs_util_subvolume_id_fd(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.
+ *
+ * This requires appropriate privilege (CAP_SYS_ADMIN).
+ *
+ * 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_subvolume_path_fd() - See btrfs_util_subvolume_path().
+ */
+enum btrfs_util_error btrfs_util_subvolume_path_fd(int fd, uint64_t id,
+						   char **path_ret);
+
 struct btrfs_util_qgroup_inherit;
 
 /**
diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
index 87d47ae0..ffd62ba7 100644
--- a/libbtrfsutil/python/btrfsutilpy.h
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -60,7 +60,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 *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *subvolume_path(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 69bba704..444516b1 100644
--- a/libbtrfsutil/python/module.c
+++ b/libbtrfsutil/python/module.c
@@ -144,6 +144,14 @@ 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"
diff --git a/libbtrfsutil/python/subvolume.c b/libbtrfsutil/python/subvolume.c
index 6f2080ee..6382d290 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -72,6 +72,36 @@ PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds)
 	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_subvolume_path_fd(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};
diff --git a/libbtrfsutil/python/tests/test_subvolume.py b/libbtrfsutil/python/tests/test_subvolume.py
index f6c5958d..57ba27bf 100644
--- a/libbtrfsutil/python/tests/test_subvolume.py
+++ b/libbtrfsutil/python/tests/test_subvolume.py
@@ -56,6 +56,37 @@ class TestSubvolume(BtrfsTestCase):
             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')
 
diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c
index 8efbdd43..54a63b52 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -128,6 +128,131 @@ PUBLIC enum btrfs_util_error btrfs_util_subvolume_id_fd(int fd,
 	return BTRFS_UTIL_OK;
 }
 
+PUBLIC 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_subvolume_path_fd(fd, id, path_ret);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_subvolume_path_fd(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_is_subvolume_fd(fd);
+		if (err)
+			return err;
+
+		err = btrfs_util_subvolume_id_fd(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)
-- 
2.16.1


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

* [PATCH v2 08/27] libbtrfsutil: add btrfs_util_subvolume_info()
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (6 preceding siblings ...)
  2018-02-15 19:04 ` [PATCH v2 07/27] libbtrfsutil: add btrfs_util_subvolume_path() Omar Sandoval
@ 2018-02-15 19:04 ` Omar Sandoval
  2018-02-15 19:04 ` [PATCH v2 09/27] libbtrfsutil: add btrfs_util_[gs]et_read_only() Omar Sandoval
                   ` (19 subsequent siblings)
  27 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:04 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                    | 111 +++++++++++++++++++++
 libbtrfsutil/python/btrfsutilpy.h           |   4 +
 libbtrfsutil/python/module.c                |  14 +++
 libbtrfsutil/python/subvolume.c             | 113 +++++++++++++++++++++
 libbtrfsutil/python/tests/test_subvolume.py |  50 ++++++++++
 libbtrfsutil/subvolume.c                    | 148 ++++++++++++++++++++++++++++
 6 files changed, 440 insertions(+)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index f96c9c4e..0d83dea9 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>
 
 #define BTRFS_UTIL_VERSION_MAJOR 1
 #define BTRFS_UTIL_VERSION_MINOR 0
@@ -124,6 +126,115 @@ enum btrfs_util_error btrfs_util_subvolume_path(const char *path, uint64_t id,
 enum btrfs_util_error btrfs_util_subvolume_path_fd(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.
+ *
+ * This requires appropriate privilege (CAP_SYS_ADMIN).
+ *
+ * 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_subvolume_info_fd() - See btrfs_util_subvolume_info().
+ */
+enum btrfs_util_error btrfs_util_subvolume_info_fd(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 ffd62ba7..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,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 *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 6382d290..31b6ca2e 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_subvolume_info_fd(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 54a63b52..69bc790a 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -253,6 +253,154 @@ PUBLIC enum btrfs_util_error btrfs_util_subvolume_path_fd(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);
+}
+
+PUBLIC 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_subvolume_info_fd(fd, id, subvol);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_subvolume_info_fd(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_is_subvolume_fd(fd);
+		if (err)
+			return err;
+
+		err = btrfs_util_subvolume_id_fd(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] 64+ messages in thread

* [PATCH v2 09/27] libbtrfsutil: add btrfs_util_[gs]et_read_only()
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (7 preceding siblings ...)
  2018-02-15 19:04 ` [PATCH v2 08/27] libbtrfsutil: add btrfs_util_subvolume_info() Omar Sandoval
@ 2018-02-15 19:04 ` Omar Sandoval
  2018-02-15 19:04 ` [PATCH v2 10/27] libbtrfsutil: add btrfs_util_[gs]et_default_subvolume() Omar Sandoval
                   ` (18 subsequent siblings)
  27 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:04 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             | 55 ++++++++++++++++++++++++
 libbtrfsutil/python/tests/test_subvolume.py | 17 ++++++++
 libbtrfsutil/subvolume.c                    | 66 +++++++++++++++++++++++++++++
 6 files changed, 186 insertions(+)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 0d83dea9..8bd2b847 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -235,6 +235,39 @@ enum btrfs_util_error btrfs_util_subvolume_info(const char *path, uint64_t id,
 enum btrfs_util_error btrfs_util_subvolume_info_fd(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_get_subvolume_read_only_fd() - See
+ * btrfs_util_get_subvolume_read_only().
+ */
+enum btrfs_util_error btrfs_util_get_subvolume_read_only_fd(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_set_subvolume_read_only_fd() - See
+ * btrfs_util_set_subvolume_read_only().
+ */
+enum btrfs_util_error btrfs_util_set_subvolume_read_only_fd(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 31b6ca2e..76487865 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -215,6 +215,61 @@ 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_get_subvolume_read_only_fd(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_set_subvolume_read_only_fd(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 69bc790a..bc5b309e 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -401,6 +401,72 @@ PUBLIC enum btrfs_util_error btrfs_util_subvolume_info_fd(int fd, uint64_t id,
 	return BTRFS_UTIL_OK;
 }
 
+PUBLIC enum btrfs_util_error btrfs_util_get_subvolume_read_only_fd(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;
+}
+
+PUBLIC 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_get_subvolume_read_only_fd(fd, ret);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+PUBLIC 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_set_subvolume_read_only_fd(fd, read_only);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_set_subvolume_read_only_fd(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] 64+ messages in thread

* [PATCH v2 10/27] libbtrfsutil: add btrfs_util_[gs]et_default_subvolume()
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (8 preceding siblings ...)
  2018-02-15 19:04 ` [PATCH v2 09/27] libbtrfsutil: add btrfs_util_[gs]et_read_only() Omar Sandoval
@ 2018-02-15 19:04 ` Omar Sandoval
  2018-02-22  1:55   ` Misono, Tomohiro
  2018-02-15 19:04 ` [PATCH v2 11/27] libbtrfsutil: add subvolume iterator helpers Omar Sandoval
                   ` (17 subsequent siblings)
  27 siblings, 1 reply; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:04 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                    |  41 ++++++++++
 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                    | 113 ++++++++++++++++++++++++++++
 6 files changed, 234 insertions(+)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 8bd2b847..54777f1d 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -256,6 +256,8 @@ enum btrfs_util_error btrfs_util_get_subvolume_read_only_fd(int fd, bool *ret);
  * @path: Subvolume path.
  * @read_only: New value of read-only flag.
  *
+ * This requires appropriate privilege (CAP_SYS_ADMIN).
+ *
  * 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,
@@ -268,6 +270,45 @@ enum btrfs_util_error btrfs_util_set_subvolume_read_only(const char *path,
 enum btrfs_util_error btrfs_util_set_subvolume_read_only_fd(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.
+ *
+ * This requires appropriate privilege (CAP_SYS_ADMIN).
+ *
+ * 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_get_default_subvolume_fd() - See
+ * btrfs_util_get_default_subvolume().
+ */
+enum btrfs_util_error btrfs_util_get_default_subvolume_fd(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_set_default_subvolume_fd() - See
+ * btrfs_util_set_default_subvolume().
+ */
+enum btrfs_util_error btrfs_util_set_default_subvolume_fd(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 76487865..fa3ec4a7 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -270,6 +270,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_get_default_subvolume_fd(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_set_default_subvolume_fd(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 bc5b309e..b3f768ed 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -467,6 +467,119 @@ PUBLIC enum btrfs_util_error btrfs_util_set_subvolume_read_only_fd(int fd,
 	return BTRFS_UTIL_OK;
 }
 
+PUBLIC 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_get_default_subvolume_fd(fd, id_ret);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_get_default_subvolume_fd(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;
+}
+
+PUBLIC 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_set_default_subvolume_fd(fd, id);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_set_default_subvolume_fd(int fd,
+								 uint64_t id)
+{
+	enum btrfs_util_error err;
+	int ret;
+
+	if (id == 0) {
+		err = btrfs_util_is_subvolume_fd(fd);
+		if (err)
+			return err;
+
+		err = btrfs_util_subvolume_id_fd(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] 64+ messages in thread

* [PATCH v2 11/27] libbtrfsutil: add subvolume iterator helpers
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (9 preceding siblings ...)
  2018-02-15 19:04 ` [PATCH v2 10/27] libbtrfsutil: add btrfs_util_[gs]et_default_subvolume() Omar Sandoval
@ 2018-02-15 19:04 ` Omar Sandoval
  2018-02-23  7:40   ` Misono, Tomohiro
  2018-02-15 19:04 ` [PATCH v2 12/27] libbtrfsutil: add btrfs_util_create_snapshot() Omar Sandoval
                   ` (16 subsequent siblings)
  27 siblings, 1 reply; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:04 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                    |  97 +++++++++
 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                    | 317 ++++++++++++++++++++++++++++
 6 files changed, 690 insertions(+)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 54777f1d..7d3a87f6 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -345,6 +345,103 @@ enum btrfs_util_error btrfs_util_create_subvolume_fd(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_create_subvolume_iterator_fd() - See
+ * btrfs_util_create_subvolume_iterator().
+ */
+enum btrfs_util_error btrfs_util_create_subvolume_iterator_fd(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.
+ *
+ * This requires appropriate privilege (CAP_SYS_ADMIN).
+ *
+ * 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().
+ *
+ * This requires appropriate privilege (CAP_SYS_ADMIN).
+ *
+ * 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 fa3ec4a7..6c384583 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -348,3 +348,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_create_subvolume_iterator_fd(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 b3f768ed..5744f89f 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -691,3 +691,320 @@ PUBLIC enum btrfs_util_error btrfs_util_create_subvolume_fd(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;
+}
+
+PUBLIC 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_create_subvolume_iterator_fd(fd, top, flags, ret);
+	if (err == BTRFS_UTIL_OK)
+		(*ret)->flags |= BTRFS_UTIL_SUBVOLUME_ITERATOR_CLOSE_FD;
+
+	return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_create_subvolume_iterator_fd(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_is_subvolume_fd(fd);
+		if (err)
+			return err;
+
+		err = btrfs_util_subvolume_id_fd(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;
+}
+
+PUBLIC 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);
+	}
+}
+
+PUBLIC 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;
+}
+
+PUBLIC 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;
+}
+
+PUBLIC 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_subvolume_info_fd(iter->fd, id, subvol);
+}
-- 
2.16.1


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

* [PATCH v2 12/27] libbtrfsutil: add btrfs_util_create_snapshot()
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (10 preceding siblings ...)
  2018-02-15 19:04 ` [PATCH v2 11/27] libbtrfsutil: add subvolume iterator helpers Omar Sandoval
@ 2018-02-15 19:04 ` Omar Sandoval
  2018-02-15 19:04 ` [PATCH v2 13/27] libbtrfsutil: add btrfs_util_delete_subvolume() Omar Sandoval
                   ` (15 subsequent siblings)
  27 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:04 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                    |  59 ++++++++++
 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                    | 167 ++++++++++++++++++++++++++++
 7 files changed, 352 insertions(+)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 7d3a87f6..04fe6666 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -345,6 +345,65 @@ enum btrfs_util_error btrfs_util_create_subvolume_fd(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 is currently implemented in userspace non-atomically. Because
+ * it modifies the newly-created snapshot, it cannot be combined with
+ * %BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY. It requires appropriate privilege
+ * (CAP_SYS_ADMIN).
+ */
+#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_create_snapshot_fd() - See btrfs_util_create_snapshot().
+ */
+enum btrfs_util_error btrfs_util_create_snapshot_fd(int fd, const char *path,
+						    int flags,
+						    uint64_t *async_transid,
+						    struct btrfs_util_qgroup_inherit *qgroup_inherit);
+
+/**
+ * btrfs_util_create_snapshot_fd2() - 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_create_snapshot_fd2(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 6c384583..a158ade7 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -349,6 +349,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_create_snapshot_fd(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 5744f89f..ca3d43c8 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -836,6 +836,173 @@ 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_create_subvolume_iterator_fd(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_create_snapshot_fd2(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;
+}
+
+PUBLIC 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_create_snapshot_fd(fd, path, flags, async_transid,
+					    qgroup_inherit);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_create_snapshot_fd(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_create_snapshot_fd2(fd, parent_fd, name, flags,
+					     async_transid, qgroup_inherit);
+	SAVE_ERRNO_AND_CLOSE(parent_fd);
+	return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_create_snapshot_fd2(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;
+}
+
 PUBLIC void btrfs_util_destroy_subvolume_iterator(struct btrfs_util_subvolume_iterator *iter)
 {
 	if (iter) {
-- 
2.16.1


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

* [PATCH v2 13/27] libbtrfsutil: add btrfs_util_delete_subvolume()
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (11 preceding siblings ...)
  2018-02-15 19:04 ` [PATCH v2 12/27] libbtrfsutil: add btrfs_util_create_snapshot() Omar Sandoval
@ 2018-02-15 19:04 ` Omar Sandoval
  2018-02-15 19:04 ` [PATCH v2 14/27] libbtrfsutil: add btrfs_util_deleted_subvolumes() Omar Sandoval
                   ` (14 subsequent siblings)
  27 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:04 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                    |  33 +++++++++
 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                    | 102 ++++++++++++++++++++++++++++
 6 files changed, 219 insertions(+)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 04fe6666..00c86174 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -404,6 +404,39 @@ enum btrfs_util_error btrfs_util_create_snapshot_fd2(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.
+ *
+ * If this flag is not used, deleting a subvolume with child subvolumes is an
+ * error. Note that this is currently implemented in userspace non-atomically.
+ * It requires appropriate privilege (CAP_SYS_ADMIN).
+ */
+#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_delete_subvolume_fd() - 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_delete_subvolume_fd(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 a158ade7..eb3f6e27 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -398,6 +398,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 ca3d43c8..908e71db 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -1003,6 +1003,108 @@ PUBLIC enum btrfs_util_error btrfs_util_create_snapshot_fd2(int 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_create_subvolume_iterator_fd(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_delete_subvolume_fd(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;
+}
+
+PUBLIC 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_delete_subvolume_fd(parent_fd, name, flags);
+	SAVE_ERRNO_AND_CLOSE(parent_fd);
+	return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_delete_subvolume_fd(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;
+}
+
 PUBLIC void btrfs_util_destroy_subvolume_iterator(struct btrfs_util_subvolume_iterator *iter)
 {
 	if (iter) {
-- 
2.16.1


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

* [PATCH v2 14/27] libbtrfsutil: add btrfs_util_deleted_subvolumes()
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (12 preceding siblings ...)
  2018-02-15 19:04 ` [PATCH v2 13/27] libbtrfsutil: add btrfs_util_delete_subvolume() Omar Sandoval
@ 2018-02-15 19:04 ` Omar Sandoval
  2018-02-23  2:12   ` Misono, Tomohiro
  2018-02-15 19:05 ` [PATCH v2 15/27] libbtrfsutil: add filesystem sync helpers Omar Sandoval
                   ` (13 subsequent siblings)
  27 siblings, 1 reply; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:04 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                    | 21 +++++++
 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                    | 89 +++++++++++++++++++++++++++++
 7 files changed, 183 insertions(+), 15 deletions(-)

diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 00c86174..677ab3c1 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -534,6 +534,27 @@ 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.
+ *
+ * This requires appropriate privilege (CAP_SYS_ADMIN).
+ *
+ * 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_deleted_subvolumes_fd() - See btrfs_util_deleted_subvolumes().
+ */
+enum btrfs_util_error btrfs_util_deleted_subvolumes_fd(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 eb3f6e27..069e606b 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -425,6 +425,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_deleted_subvolumes_fd(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 908e71db..4ae581b2 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -1277,3 +1277,92 @@ PUBLIC enum btrfs_util_error btrfs_util_subvolume_iterator_next_info(struct btrf
 
 	return btrfs_util_subvolume_info_fd(iter->fd, id, subvol);
 }
+
+PUBLIC 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_deleted_subvolumes_fd(fd, ids, n);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_deleted_subvolumes_fd(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] 64+ messages in thread

* [PATCH v2 15/27] libbtrfsutil: add filesystem sync helpers
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (13 preceding siblings ...)
  2018-02-15 19:04 ` [PATCH v2 14/27] libbtrfsutil: add btrfs_util_deleted_subvolumes() Omar Sandoval
@ 2018-02-15 19:05 ` Omar Sandoval
  2018-02-15 19:05 ` [PATCH v2 16/27] btrfs-progs: use libbtrfsutil for read-only property Omar Sandoval
                   ` (12 subsequent siblings)
  27 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:05 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/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 +++++++++++++++++++
 8 files changed, 341 insertions(+), 2 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 fa32c50d..37bd6e5d 100644
--- a/Makefile
+++ b/Makefile
@@ -135,8 +135,8 @@ libbtrfsutil_major := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_MAJOR ([0-
 libbtrfsutil_minor := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_MINOR ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
 libbtrfsutil_patch := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_PATCH ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
 libbtrfsutil_version := $(libbtrfsutil_major).$(libbtrfsutil_minor).$(libbtrfsutil_patch)
-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 mkfs/rootdir.o
diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 677ab3c1..34a2fc4a 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -74,6 +74,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_sync_fd() - See btrfs_util_sync().
+ */
+enum btrfs_util_error btrfs_util_sync_fd(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_start_sync_fd() - See btrfs_util_start_sync().
+ */
+enum btrfs_util_error btrfs_util_start_sync_fd(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_wait_sync_fd() - See btrfs_util_wait_sync().
+ */
+enum btrfs_util_error btrfs_util_wait_sync_fd(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..dfd171bf
--- /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_internal.h"
+
+PUBLIC 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_sync_fd(fd);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_sync_fd(int fd)
+{
+	int ret;
+
+	ret = ioctl(fd, BTRFS_IOC_SYNC, NULL);
+	if (ret == -1)
+		return BTRFS_UTIL_ERROR_SYNC_FAILED;
+
+	return BTRFS_UTIL_OK;
+}
+
+PUBLIC 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_start_sync_fd(fd, transid);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_start_sync_fd(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;
+}
+
+PUBLIC 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_wait_sync_fd(fd, transid);
+	SAVE_ERRNO_AND_CLOSE(fd);
+	return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_wait_sync_fd(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/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..627ed193
--- /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_sync_fd(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_start_sync_fd(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_wait_sync_fd(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 06857084..131f1755 100755
--- a/libbtrfsutil/python/setup.py
+++ b/libbtrfsutil/python/setup.py
@@ -90,6 +90,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
-- 
2.16.1


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

* [PATCH v2 16/27] btrfs-progs: use libbtrfsutil for read-only property
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (14 preceding siblings ...)
  2018-02-15 19:05 ` [PATCH v2 15/27] libbtrfsutil: add filesystem sync helpers Omar Sandoval
@ 2018-02-15 19:05 ` Omar Sandoval
  2018-02-22  4:23   ` Misono, Tomohiro
  2018-02-15 19:05 ` [PATCH v2 17/27] btrfs-progs: use libbtrfsutil for sync ioctls Omar Sandoval
                   ` (11 subsequent siblings)
  27 siblings, 1 reply; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:05 UTC (permalink / raw)
  To: linux-btrfs; +Cc: kernel-team

From: Omar Sandoval <osandov@fb.com>

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 messages.h | 13 ++++++++++++
 props.c    | 69 +++++++++++++++++++++++---------------------------------------
 2 files changed, 38 insertions(+), 44 deletions(-)

diff --git a/messages.h b/messages.h
index 4999c7b9..004d5167 100644
--- a/messages.h
+++ b/messages.h
@@ -54,6 +54,19 @@
 			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;					\
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] 64+ messages in thread

* [PATCH v2 17/27] btrfs-progs: use libbtrfsutil for sync ioctls
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (15 preceding siblings ...)
  2018-02-15 19:05 ` [PATCH v2 16/27] btrfs-progs: use libbtrfsutil for read-only property Omar Sandoval
@ 2018-02-15 19:05 ` Omar Sandoval
  2018-02-15 19:05 ` [PATCH v2 18/27] btrfs-progs: use libbtrfsutil for set-default Omar Sandoval
                   ` (10 subsequent siblings)
  27 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:05 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 | 19 ++++++-------------
 cmds-qgroup.c     | 10 +++++++---
 cmds-subvolume.c  | 25 +++++++++++++++++--------
 3 files changed, 30 insertions(+), 24 deletions(-)

diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index 467aff11..225df421 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"
@@ -813,25 +815,16 @@ static const char * const cmd_filesystem_sync_usage[] = {
 
 static int cmd_filesystem_sync(int argc, char **argv)
 {
-	int 	fd, res;
-	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);
-	close_file_or_dir(fd, dirstream);
-	if( res < 0 ){
-		error("sync ioctl failed on '%s': %m", path);
+	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 4f99e419..f9a52fa8 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"
 
@@ -299,6 +301,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;
@@ -372,9 +375,10 @@ static int cmd_qgroup_show(int argc, char **argv)
 	}
 
 	if (sync) {
-		ret = ioctl(fd, BTRFS_IOC_SYNC);
-		if (ret < 0)
-			warning("sync ioctl failed on '%s': %m", path);
+		err = btrfs_util_sync_fd(fd);
+		if (err)
+			warning("sync ioctl failed on '%s': %s", path,
+				strerror(errno));
 	}
 
 	if (filter_flag) {
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index edcb4f11..6006a278 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"
@@ -219,12 +221,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_start_sync_fd(fd, &transid);
+	if (err)
+		return -1;
+
+	err = btrfs_util_wait_sync_fd(fd, transid);
+	if (err)
+		return -1;
+
+	return 0;
 }
 
 static const char * const cmd_subvol_delete_usage[] = {
@@ -911,6 +919,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);
 
@@ -934,9 +943,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': %m", subvol);
+	err = btrfs_util_sync_fd(fd);
+	if (err) {
+		error_btrfs_util(err);
 		close_file_or_dir(fd, dirstream);
 		return 1;
 	}
-- 
2.16.1


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

* [PATCH v2 18/27] btrfs-progs: use libbtrfsutil for set-default
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (16 preceding siblings ...)
  2018-02-15 19:05 ` [PATCH v2 17/27] btrfs-progs: use libbtrfsutil for sync ioctls Omar Sandoval
@ 2018-02-15 19:05 ` Omar Sandoval
  2018-02-15 19:05 ` [PATCH v2 19/27] btrfs-progs: use libbtrfsutil for get-default Omar Sandoval
                   ` (9 subsequent siblings)
  27 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:05 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 | 43 ++++++++-----------------------------------
 1 file changed, 8 insertions(+), 35 deletions(-)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 6006a278..700e822c 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -851,11 +851,9 @@ static const char * const cmd_subvol_set_default_usage[] = {
 
 static int cmd_subvol_set_default(int argc, char **argv)
 {
-	int	ret=0, fd;
-	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);
 
@@ -865,42 +863,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);
-	close_file_or_dir(fd, dirstream);
-	if (ret < 0) {
-		error("unable to set a new default subvolume: %m");
+	err = btrfs_util_set_default_subvolume(path, objectid);
+	if (err) {
+		error_btrfs_util(err);
 		return 1;
 	}
 	return 0;
-- 
2.16.1


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

* [PATCH v2 19/27] btrfs-progs: use libbtrfsutil for get-default
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (17 preceding siblings ...)
  2018-02-15 19:05 ` [PATCH v2 18/27] btrfs-progs: use libbtrfsutil for set-default Omar Sandoval
@ 2018-02-15 19:05 ` Omar Sandoval
  2018-02-15 19:05 ` [PATCH v2 20/27] btrfs-progs: use libbtrfsutil for subvol create and snapshot Omar Sandoval
                   ` (8 subsequent siblings)
  27 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:05 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 | 54 ++++++++++++++++++++++++++----------------------------
 1 file changed, 26 insertions(+), 28 deletions(-)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 700e822c..42cc30ce 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>
@@ -785,31 +786,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: %m");
-		goto out;
-	}
-
-	ret = 1;
-	if (default_id == 0) {
-		error("'default' dir item not found");
+	err = btrfs_util_get_default_subvolume_fd(fd, &default_id);
+	if (err) {
+		error_btrfs_util(err);
 		goto out;
 	}
 
@@ -820,24 +815,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_subvolume_info_fd(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_subvolume_path_fd(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] 64+ messages in thread

* [PATCH v2 20/27] btrfs-progs: use libbtrfsutil for subvol create and snapshot
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (18 preceding siblings ...)
  2018-02-15 19:05 ` [PATCH v2 19/27] btrfs-progs: use libbtrfsutil for get-default Omar Sandoval
@ 2018-02-15 19:05 ` Omar Sandoval
  2018-02-15 19:05 ` [PATCH v2 21/27] btrfs-progs: use libbtrfsutil for subvol delete Omar Sandoval
                   ` (7 subsequent siblings)
  27 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:05 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 42cc30ce..ea436ba0 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: %m");
+	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;
 }
@@ -650,19 +618,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)
@@ -670,14 +630,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);
@@ -687,92 +644,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': %m", subvol);
+	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 b107a683..69395939 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -1260,67 +1260,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] 64+ messages in thread

* [PATCH v2 21/27] btrfs-progs: use libbtrfsutil for subvol delete
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (19 preceding siblings ...)
  2018-02-15 19:05 ` [PATCH v2 20/27] btrfs-progs: use libbtrfsutil for subvol create and snapshot Omar Sandoval
@ 2018-02-15 19:05 ` Omar Sandoval
  2018-02-15 19:05 ` [PATCH v2 22/27] btrfs-progs: use libbtrfsutil for subvol show Omar Sandoval
                   ` (6 subsequent siblings)
  27 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:05 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 | 22 ++++++++--------------
 1 file changed, 8 insertions(+), 14 deletions(-)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index ea436ba0..68768914 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;
 	}
@@ -313,11 +308,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': %m", dname, vname);
+
+	err = btrfs_util_delete_subvolume_fd(fd, vname, 0);
+	if (err) {
+		error_btrfs_util(err);
 		ret = 1;
 		goto out;
 	}
-- 
2.16.1


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

* [PATCH v2 22/27] btrfs-progs: use libbtrfsutil for subvol show
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (20 preceding siblings ...)
  2018-02-15 19:05 ` [PATCH v2 21/27] btrfs-progs: use libbtrfsutil for subvol delete Omar Sandoval
@ 2018-02-15 19:05 ` Omar Sandoval
  2018-02-15 19:05 ` [PATCH v2 23/27] btrfs-progs: use libbtrfsutil for subvol sync Omar Sandoval
                   ` (5 subsequent siblings)
  27 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:05 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 | 149 ++++++++++++++++++++++++++++++++++++-------------------
 utils.c          | 118 -------------------------------------------
 utils.h          |   5 --
 3 files changed, 98 insertions(+), 174 deletions(-)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 68768914..49c9c8cf 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -824,19 +824,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;
@@ -873,96 +874,142 @@ 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': %m", argv[optind]);
 		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_create_subvolume_iterator_fd(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_subvolume_info_fd(fd, rootid_arg, &subvol);
+		if (err) {
+			error_btrfs_util(err);
+			goto out;
+		}
+
+		err = btrfs_util_subvolume_path_fd(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_create_subvolume_iterator_fd(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 e9cb3a82..6e6f295f 100644
--- a/utils.c
+++ b/utils.c
@@ -2490,124 +2490,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 b871c9ff..eb460e9b 100644
--- a/utils.h
+++ b/utils.h
@@ -153,11 +153,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] 64+ messages in thread

* [PATCH v2 23/27] btrfs-progs: use libbtrfsutil for subvol sync
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (21 preceding siblings ...)
  2018-02-15 19:05 ` [PATCH v2 22/27] btrfs-progs: use libbtrfsutil for subvol show Omar Sandoval
@ 2018-02-15 19:05 ` Omar Sandoval
  2018-02-22  2:03   ` Misono, Tomohiro
  2018-02-22  2:09   ` Misono, Tomohiro
  2018-02-15 19:05 ` [PATCH v2 24/27] btrfs-progs: replace test_issubvolume() with btrfs_util_is_subvolume() Omar Sandoval
                   ` (4 subsequent siblings)
  27 siblings, 2 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:05 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 49c9c8cf..9bab9312 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_subvolume_info_fd(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;
 			}
@@ -1028,160 +999,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:");
@@ -1215,10 +1041,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_deleted_subvolumes_fd(fd, &ids, &id_count);
+		if (err) {
+			error_btrfs_util(err);
 			ret = 1;
 			goto out;
 		}
@@ -1227,7 +1052,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;
@@ -1241,13 +1066,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] 64+ messages in thread

* [PATCH v2 24/27] btrfs-progs: replace test_issubvolume() with btrfs_util_is_subvolume()
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (22 preceding siblings ...)
  2018-02-15 19:05 ` [PATCH v2 23/27] btrfs-progs: use libbtrfsutil for subvol sync Omar Sandoval
@ 2018-02-15 19:05 ` Omar Sandoval
  2018-02-15 19:05 ` [PATCH v2 25/27] btrfs-progs: add recursive snapshot/delete using libbtrfsutil Omar Sandoval
                   ` (3 subsequent siblings)
  27 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:05 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    | 11 ++++-------
 cmds-subvolume.c | 10 +++-------
 utils.c          | 34 +++++-----------------------------
 utils.h          |  1 -
 4 files changed, 12 insertions(+), 44 deletions(-)

diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index f9a52fa8..f733565e 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -425,6 +425,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");
@@ -465,13 +466,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 9bab9312..43875c96 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -756,13 +756,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 6e6f295f..9be91437 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"
@@ -1453,6 +1455,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;
@@ -1480,8 +1483,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)
@@ -2451,33 +2454,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 eb460e9b..403de481 100644
--- a/utils.h
+++ b/utils.h
@@ -149,7 +149,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] 64+ messages in thread

* [PATCH v2 25/27] btrfs-progs: add recursive snapshot/delete using libbtrfsutil
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (23 preceding siblings ...)
  2018-02-15 19:05 ` [PATCH v2 24/27] btrfs-progs: replace test_issubvolume() with btrfs_util_is_subvolume() Omar Sandoval
@ 2018-02-15 19:05 ` Omar Sandoval
  2018-02-15 19:05 ` [PATCH v2 26/27] btrfs-progs: use libbtrfsutil for subvolume list Omar Sandoval
                   ` (2 subsequent siblings)
  27 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:05 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 43875c96..101ba4ca 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;
@@ -280,7 +286,7 @@ again:
 		commit_mode == COMMIT_EACH || (commit_mode == COMMIT_AFTER && cnt + 1 == argc)
 		? "commit" : "no-commit", dname, vname);
 
-	err = btrfs_util_delete_subvolume_fd(fd, vname, 0);
+	err = btrfs_util_delete_subvolume_fd(fd, vname, flags);
 	if (err) {
 		error_btrfs_util(err);
 		ret = 1;
@@ -569,13 +575,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
@@ -589,7 +597,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;
 
@@ -601,11 +609,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] 64+ messages in thread

* [PATCH v2 26/27] btrfs-progs: use libbtrfsutil for subvolume list
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (24 preceding siblings ...)
  2018-02-15 19:05 ` [PATCH v2 25/27] btrfs-progs: add recursive snapshot/delete using libbtrfsutil Omar Sandoval
@ 2018-02-15 19:05 ` Omar Sandoval
  2018-02-23  2:26   ` Misono, Tomohiro
  2018-02-15 19:05 ` [PATCH v2 27/27] btrfs-progs: deprecate libbtrfs helpers Omar Sandoval
  2018-02-20 18:50 ` [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" David Sterba
  27 siblings, 1 reply; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:05 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.

This also moves the list printing code from libbtrfs to
cmds-subvolume.c. This is an ABI break, but it avoids adding a
libbtrfsutil dependency to libbtrfs. A search online didn't turn up any
projects which use this interface.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 btrfs-list.c     | 806 +---------------------------------------------
 btrfs-list.h     |  89 ------
 cmds-subvolume.c | 948 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 956 insertions(+), 887 deletions(-)

diff --git a/btrfs-list.c b/btrfs-list.c
index e01c5899..a2fdb3f9 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -34,9 +34,6 @@
 #include "btrfs-list.h"
 #include "rbtree-utils.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.
  */
@@ -44,285 +41,14 @@ struct root_lookup {
 	struct rb_root root;
 };
 
-static struct {
-	char	*name;
-	char	*column_name;
-	int	need_print;
-} btrfs_list_columns[] = {
-	{
-		.name		= "ID",
-		.column_name	= "ID",
-		.need_print	= 0,
-	},
-	{
-		.name		= "gen",
-		.column_name	= "Gen",
-		.need_print	= 0,
-	},
-	{
-		.name		= "cgen",
-		.column_name	= "CGen",
-		.need_print	= 0,
-	},
-	{
-		.name		= "parent",
-		.column_name	= "Parent",
-		.need_print	= 0,
-	},
-	{
-		.name		= "top level",
-		.column_name	= "Top Level",
-		.need_print	= 0,
-	},
-	{
-		.name		= "otime",
-		.column_name	= "OTime",
-		.need_print	= 0,
-	},
-	{
-		.name		= "parent_uuid",
-		.column_name	= "Parent UUID",
-		.need_print	= 0,
-	},
-	{
-		.name		= "received_uuid",
-		.column_name	= "Received UUID",
-		.need_print	= 0,
-	},
-	{
-		.name		= "uuid",
-		.column_name	= "UUID",
-		.need_print	= 0,
-	},
-	{
-		.name		= "path",
-		.column_name	= "Path",
-		.need_print	= 0,
-	},
-	{
-		.name		= NULL,
-		.column_name	= NULL,
-		.need_print	= 0,
-	},
-};
-
-static btrfs_list_filter_func all_filter_funcs[];
-static btrfs_list_comp_func all_comp_funcs[];
-
-void btrfs_list_setup_print_column(enum btrfs_list_column_enum column)
+static int comp_rootid(struct root_info *entry1, struct root_info *entry2)
 {
-	int i;
-
-	ASSERT(0 <= column && column <= BTRFS_LIST_ALL);
-
-	if (column < BTRFS_LIST_ALL) {
-		btrfs_list_columns[column].need_print = 1;
-		return;
-	}
-
-	for (i = 0; i < BTRFS_LIST_ALL; i++)
-		btrfs_list_columns[i].need_print = 1;
-}
-
-static int comp_entry_with_rootid(struct root_info *entry1,
-				  struct root_info *entry2,
-				  int is_descending)
-{
-	int ret;
-
 	if (entry1->root_id > entry2->root_id)
-		ret = 1;
+		return 1;
 	else if (entry1->root_id < entry2->root_id)
-		ret = -1;
-	else
-		ret = 0;
-
-	return is_descending ? -ret : ret;
-}
-
-static int comp_entry_with_gen(struct root_info *entry1,
-			       struct root_info *entry2,
-			       int is_descending)
-{
-	int ret;
-
-	if (entry1->gen > entry2->gen)
-		ret = 1;
-	else if (entry1->gen < entry2->gen)
-		ret = -1;
-	else
-		ret = 0;
-
-	return is_descending ? -ret : ret;
-}
-
-static int comp_entry_with_ogen(struct root_info *entry1,
-				struct root_info *entry2,
-				int is_descending)
-{
-	int ret;
-
-	if (entry1->ogen > entry2->ogen)
-		ret = 1;
-	else if (entry1->ogen < entry2->ogen)
-		ret = -1;
+		return -1;
 	else
-		ret = 0;
-
-	return is_descending ? -ret : ret;
-}
-
-static int comp_entry_with_path(struct root_info *entry1,
-				struct root_info *entry2,
-				int is_descending)
-{
-	int ret;
-
-	if (strcmp(entry1->full_path, entry2->full_path) > 0)
-		ret = 1;
-	else if (strcmp(entry1->full_path, entry2->full_path) < 0)
-		ret = -1;
-	else
-		ret = 0;
-
-	return is_descending ? -ret : ret;
-}
-
-static btrfs_list_comp_func all_comp_funcs[] = {
-	[BTRFS_LIST_COMP_ROOTID]	= comp_entry_with_rootid,
-	[BTRFS_LIST_COMP_OGEN]		= comp_entry_with_ogen,
-	[BTRFS_LIST_COMP_GEN]		= comp_entry_with_gen,
-	[BTRFS_LIST_COMP_PATH]		= comp_entry_with_path,
-};
-
-static char *all_sort_items[] = {
-	[BTRFS_LIST_COMP_ROOTID]	= "rootid",
-	[BTRFS_LIST_COMP_OGEN]		= "ogen",
-	[BTRFS_LIST_COMP_GEN]		= "gen",
-	[BTRFS_LIST_COMP_PATH]		= "path",
-	[BTRFS_LIST_COMP_MAX]		= NULL,
-};
-
-static int  btrfs_list_get_sort_item(char *sort_name)
-{
-	int i;
-
-	for (i = 0; i < BTRFS_LIST_COMP_MAX; i++) {
-		if (strcmp(sort_name, all_sort_items[i]) == 0)
-			return i;
-	}
-	return -1;
-}
-
-struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void)
-{
-	struct btrfs_list_comparer_set *set;
-	int size;
-
-	size = sizeof(struct btrfs_list_comparer_set) +
-	       BTRFS_LIST_NCOMPS_INCREASE * sizeof(struct btrfs_list_comparer);
-	set = calloc(1, size);
-	if (!set) {
-		fprintf(stderr, "memory allocation failed\n");
-		exit(1);
-	}
-
-	set->total = BTRFS_LIST_NCOMPS_INCREASE;
-
-	return set;
-}
-
-static int btrfs_list_setup_comparer(struct btrfs_list_comparer_set **comp_set,
-		enum btrfs_list_comp_enum comparer, int is_descending)
-{
-	struct btrfs_list_comparer_set *set = *comp_set;
-	int size;
-
-	ASSERT(set != NULL);
-	ASSERT(comparer < BTRFS_LIST_COMP_MAX);
-	ASSERT(set->ncomps <= set->total);
-
-	if (set->ncomps == set->total) {
-		void *tmp;
-
-		size = set->total + BTRFS_LIST_NCOMPS_INCREASE;
-		size = sizeof(*set) + size * sizeof(struct btrfs_list_comparer);
-		tmp = set;
-		set = realloc(set, size);
-		if (!set) {
-			fprintf(stderr, "memory allocation failed\n");
-			free(tmp);
-			exit(1);
-		}
-
-		memset(&set->comps[set->total], 0,
-		       BTRFS_LIST_NCOMPS_INCREASE *
-		       sizeof(struct btrfs_list_comparer));
-		set->total += BTRFS_LIST_NCOMPS_INCREASE;
-		*comp_set = set;
-	}
-
-	ASSERT(set->comps[set->ncomps].comp_func == NULL);
-
-	set->comps[set->ncomps].comp_func = all_comp_funcs[comparer];
-	set->comps[set->ncomps].is_descending = is_descending;
-	set->ncomps++;
-	return 0;
-}
-
-static int sort_comp(struct root_info *entry1, struct root_info *entry2,
-		     struct btrfs_list_comparer_set *set)
-{
-	int rootid_compared = 0;
-	int i, ret = 0;
-
-	if (!set || !set->ncomps)
-		return comp_entry_with_rootid(entry1, entry2, 0);
-
-	for (i = 0; i < set->ncomps; i++) {
-		if (!set->comps[i].comp_func)
-			break;
-
-		ret = set->comps[i].comp_func(entry1, entry2,
-					      set->comps[i].is_descending);
-		if (ret)
-			return ret;
-
-		if (set->comps[i].comp_func == comp_entry_with_rootid)
-			rootid_compared = 1;
-	}
-
-	if (!rootid_compared)
-		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;
+		return 0;
 }
 
 /*
@@ -342,7 +68,7 @@ static int root_tree_insert(struct root_lookup *root_tree,
 		parent = *p;
 		curr = rb_entry(parent, struct root_info, rb_node);
 
-		ret = comp_entry_with_rootid(ins, curr, 0);
+		ret = comp_rootid(ins, curr);
 		if (ret < 0)
 			p = &(*p)->rb_left;
 		else if (ret > 0)
@@ -373,7 +99,7 @@ static struct root_info *root_tree_search(struct root_lookup *root_tree,
 	while(n) {
 		entry = rb_entry(n, struct root_info, rb_node);
 
-		ret = comp_entry_with_rootid(&tmp, entry, 0);
+		ret = comp_rootid(&tmp, entry);
 		if (ret < 0)
 			n = n->rb_left;
 		else if (ret > 0)
@@ -1069,229 +795,6 @@ static int list_subvol_search(int fd, struct root_lookup *root_lookup)
 	return 0;
 }
 
-static int filter_by_rootid(struct root_info *ri, u64 data)
-{
-	return ri->root_id == data;
-}
-
-static int filter_snapshot(struct root_info *ri, u64 data)
-{
-	return !!ri->root_offset;
-}
-
-static int filter_flags(struct root_info *ri, u64 flags)
-{
-	return ri->flags & flags;
-}
-
-static int filter_gen_more(struct root_info *ri, u64 data)
-{
-	return ri->gen >= data;
-}
-
-static int filter_gen_less(struct root_info *ri, u64 data)
-{
-	return ri->gen <= data;
-}
-
-static int filter_gen_equal(struct root_info  *ri, u64 data)
-{
-	return ri->gen == data;
-}
-
-static int filter_cgen_more(struct root_info *ri, u64 data)
-{
-	return ri->ogen >= data;
-}
-
-static int filter_cgen_less(struct root_info *ri, u64 data)
-{
-	return ri->ogen <= data;
-}
-
-static int filter_cgen_equal(struct root_info *ri, u64 data)
-{
-	return ri->ogen == data;
-}
-
-static int filter_topid_equal(struct root_info *ri, u64 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) {
-		char *tmp;
-		char p[] = "<FS_TREE>";
-		int add_len = strlen(p);
-		int len = strlen(ri->full_path);
-
-		tmp = malloc(len + add_len + 2);
-		if (!tmp) {
-			fprintf(stderr, "memory allocation failed\n");
-			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;
-	}
-	return 1;
-}
-
-static int filter_by_parent(struct root_info *ri, u64 data)
-{
-	return !uuid_compare(ri->puuid, (u8 *)(unsigned long)data);
-}
-
-static int filter_deleted(struct root_info *ri, u64 data)
-{
-	return ri->deleted;
-}
-
-static btrfs_list_filter_func all_filter_funcs[] = {
-	[BTRFS_LIST_FILTER_ROOTID]		= filter_by_rootid,
-	[BTRFS_LIST_FILTER_SNAPSHOT_ONLY]	= filter_snapshot,
-	[BTRFS_LIST_FILTER_FLAGS]		= filter_flags,
-	[BTRFS_LIST_FILTER_GEN_MORE]		= filter_gen_more,
-	[BTRFS_LIST_FILTER_GEN_LESS]		= filter_gen_less,
-	[BTRFS_LIST_FILTER_GEN_EQUAL]           = filter_gen_equal,
-	[BTRFS_LIST_FILTER_CGEN_MORE]		= filter_cgen_more,
-	[BTRFS_LIST_FILTER_CGEN_LESS]		= filter_cgen_less,
-	[BTRFS_LIST_FILTER_CGEN_EQUAL]          = filter_cgen_equal,
-	[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)
-{
-	struct btrfs_list_filter_set *set;
-	int size;
-
-	size = sizeof(struct btrfs_list_filter_set) +
-	       BTRFS_LIST_NFILTERS_INCREASE * sizeof(struct btrfs_list_filter);
-	set = calloc(1, size);
-	if (!set) {
-		fprintf(stderr, "memory allocation failed\n");
-		exit(1);
-	}
-
-	set->total = BTRFS_LIST_NFILTERS_INCREASE;
-
-	return set;
-}
-
-/*
- * Setup list filters. Exit if there's not enough memory, as we can't continue
- * without the structures set up properly.
- */
-void btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
-			    enum btrfs_list_filter_enum filter, u64 data)
-{
-	struct btrfs_list_filter_set *set = *filter_set;
-	int size;
-
-	ASSERT(set != NULL);
-	ASSERT(filter < BTRFS_LIST_FILTER_MAX);
-	ASSERT(set->nfilters <= set->total);
-
-	if (set->nfilters == set->total) {
-		void *tmp;
-
-		size = set->total + BTRFS_LIST_NFILTERS_INCREASE;
-		size = sizeof(*set) + size * sizeof(struct btrfs_list_filter);
-		tmp = set;
-		set = realloc(set, size);
-		if (!set) {
-			fprintf(stderr, "memory allocation failed\n");
-			free(tmp);
-			exit(1);
-		}
-
-		memset(&set->filters[set->total], 0,
-		       BTRFS_LIST_NFILTERS_INCREASE *
-		       sizeof(struct btrfs_list_filter));
-		set->total += BTRFS_LIST_NFILTERS_INCREASE;
-		*filter_set = set;
-	}
-
-	ASSERT(set->filters[set->nfilters].filter_func == NULL);
-
-	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++;
-}
-
-static int filter_root(struct root_info *ri,
-		       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);
-		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;
@@ -1310,185 +813,6 @@ static int list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
 	return 0;
 }
 
-static void print_subvolume_column(struct root_info *subv,
-				   enum btrfs_list_column_enum column)
-{
-	char tstr[256];
-	char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
-
-	ASSERT(0 <= column && column < BTRFS_LIST_ALL);
-
-	switch (column) {
-	case BTRFS_LIST_OBJECTID:
-		printf("%llu", subv->root_id);
-		break;
-	case BTRFS_LIST_GENERATION:
-		printf("%llu", subv->gen);
-		break;
-	case BTRFS_LIST_OGENERATION:
-		printf("%llu", subv->ogen);
-		break;
-	case BTRFS_LIST_PARENT:
-		printf("%llu", subv->ref_tree);
-		break;
-	case BTRFS_LIST_TOP_LEVEL:
-		printf("%llu", subv->top_id);
-		break;
-	case BTRFS_LIST_OTIME:
-		if (subv->otime) {
-			struct tm tm;
-
-			localtime_r(&subv->otime, &tm);
-			strftime(tstr, 256, "%Y-%m-%d %X", &tm);
-		} else
-			strcpy(tstr, "-");
-		printf("%s", tstr);
-		break;
-	case BTRFS_LIST_UUID:
-		if (uuid_is_null(subv->uuid))
-			strcpy(uuidparse, "-");
-		else
-			uuid_unparse(subv->uuid, uuidparse);
-		printf("%-36s", uuidparse);
-		break;
-	case BTRFS_LIST_PUUID:
-		if (uuid_is_null(subv->puuid))
-			strcpy(uuidparse, "-");
-		else
-			uuid_unparse(subv->puuid, uuidparse);
-		printf("%-36s", uuidparse);
-		break;
-	case BTRFS_LIST_RUUID:
-		if (uuid_is_null(subv->ruuid))
-			strcpy(uuidparse, "-");
-		else
-			uuid_unparse(subv->ruuid, uuidparse);
-		printf("%-36s", uuidparse);
-		break;
-	case BTRFS_LIST_PATH:
-		BUG_ON(!subv->full_path);
-		printf("%s", subv->full_path);
-		break;
-	default:
-		break;
-	}
-}
-
-static void print_one_subvol_info_raw(struct root_info *subv,
-		const char *raw_prefix)
-{
-	int i;
-
-	for (i = 0; i < BTRFS_LIST_ALL; i++) {
-		if (!btrfs_list_columns[i].need_print)
-			continue;
-
-		if (raw_prefix)
-			printf("%s",raw_prefix);
-
-		print_subvolume_column(subv, i);
-	}
-	printf("\n");
-}
-
-static void print_one_subvol_info_table(struct root_info *subv)
-{
-	int i;
-
-	for (i = 0; i < BTRFS_LIST_ALL; i++) {
-		if (!btrfs_list_columns[i].need_print)
-			continue;
-
-		print_subvolume_column(subv, i);
-
-		if (i != BTRFS_LIST_PATH)
-			printf("\t");
-
-		if (i == BTRFS_LIST_TOP_LEVEL)
-			printf("\t");
-	}
-	printf("\n");
-}
-
-static void print_one_subvol_info_default(struct root_info *subv)
-{
-	int i;
-
-	for (i = 0; i < BTRFS_LIST_ALL; i++) {
-		if (!btrfs_list_columns[i].need_print)
-			continue;
-
-		printf("%s ", btrfs_list_columns[i].name);
-		print_subvolume_column(subv, i);
-
-		if (i != BTRFS_LIST_PATH)
-			printf(" ");
-	}
-	printf("\n");
-}
-
-static void print_all_subvol_info_tab_head(void)
-{
-	int i;
-	int len;
-	char barrier[20];
-
-	for (i = 0; i < BTRFS_LIST_ALL; i++) {
-		if (btrfs_list_columns[i].need_print)
-			printf("%s\t", btrfs_list_columns[i].name);
-
-		if (i == BTRFS_LIST_ALL-1)
-			printf("\n");
-	}
-
-	for (i = 0; i < BTRFS_LIST_ALL; i++) {
-		memset(barrier, 0, sizeof(barrier));
-
-		if (btrfs_list_columns[i].need_print) {
-			len = strlen(btrfs_list_columns[i].name);
-			while (len--)
-				strcat(barrier, "-");
-
-			printf("%s\t", barrier);
-		}
-		if (i == BTRFS_LIST_ALL-1)
-			printf("\n");
-	}
-}
-
-static void print_all_subvol_info(struct root_lookup *sorted_tree,
-		  enum btrfs_list_layout layout, const char *raw_prefix)
-{
-	struct rb_node *n;
-	struct root_info *entry;
-
-	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;
-
-		switch (layout) {
-		case BTRFS_LIST_LAYOUT_DEFAULT:
-			print_one_subvol_info_default(entry);
-			break;
-		case BTRFS_LIST_LAYOUT_TABLE:
-			print_one_subvol_info_table(entry);
-			break;
-		case BTRFS_LIST_LAYOUT_RAW:
-			print_one_subvol_info_raw(entry, raw_prefix);
-			break;
-		}
-next:
-		n = rb_next(n);
-	}
-}
-
 static int btrfs_list_subvols(int fd, struct root_lookup *root_lookup)
 {
 	int ret;
@@ -1507,33 +831,6 @@ static int btrfs_list_subvols(int fd, struct root_lookup *root_lookup)
 	return ret;
 }
 
-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 root_lookup root_lookup;
-	struct root_lookup root_sort;
-	int ret = 0;
-	u64 top_id = 0;
-
-	if (full_path)
-		ret = btrfs_list_get_path_rootid(fd, &top_id);
-	if (ret)
-		return ret;
-
-	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);
-
-	print_all_subvol_info(&root_sort, layout, raw_prefix);
-	rb_free_nodes(&root_lookup.root, free_root_info);
-
-	return 0;
-}
-
 static char *strdup_or_null(const char *s)
 {
 	if (!s)
@@ -1598,8 +895,8 @@ int btrfs_get_subvol(int fd, struct root_info *the_ri)
 			continue;
 		}
 
-		if (!comp_entry_with_rootid(the_ri, ri, 0) ||
-			!uuid_compare(the_ri->uuid, ri->uuid)) {
+		if (!comp_rootid(the_ri, ri) ||
+		    !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);
@@ -1832,93 +1129,6 @@ char *btrfs_list_path_for_root(int fd, u64 root)
 	return ret_path;
 }
 
-int btrfs_list_parse_sort_string(char *opt_arg,
-				 struct btrfs_list_comparer_set **comps)
-{
-	int order;
-	int flag;
-	char *p;
-	char **ptr_argv;
-	int what_to_sort;
-
-	while ((p = strtok(opt_arg, ",")) != NULL) {
-		flag = 0;
-		ptr_argv = all_sort_items;
-
-		while (*ptr_argv) {
-			if (strcmp(*ptr_argv, p) == 0) {
-				flag = 1;
-				break;
-			} else {
-				p++;
-				if (strcmp(*ptr_argv, p) == 0) {
-					flag = 1;
-					p--;
-					break;
-				}
-				p--;
-			}
-			ptr_argv++;
-		}
-
-		if (flag == 0)
-			return -1;
-
-		else {
-			if (*p == '+') {
-				order = 0;
-				p++;
-			} else if (*p == '-') {
-				order = 1;
-				p++;
-			} else
-				order = 0;
-
-			what_to_sort = btrfs_list_get_sort_item(p);
-			btrfs_list_setup_comparer(comps, what_to_sort, order);
-		}
-		opt_arg = NULL;
-	}
-
-	return 0;
-}
-
-/*
- * This function is used to parse the argument of filter condition.
- *
- * type is the filter object.
- */
-int btrfs_list_parse_filter_string(char *opt_arg,
-				   struct btrfs_list_filter_set **filters,
-				   enum btrfs_list_filter_enum type)
-{
-
-	u64 arg;
-
-	switch (*(opt_arg++)) {
-	case '+':
-		arg = arg_strtou64(opt_arg);
-		type += 2;
-
-		btrfs_list_setup_filter(filters, type, arg);
-		break;
-	case '-':
-		arg = arg_strtou64(opt_arg);
-		type += 1;
-
-		btrfs_list_setup_filter(filters, type, arg);
-		break;
-	default:
-		opt_arg--;
-		arg = arg_strtou64(opt_arg);
-
-		btrfs_list_setup_filter(filters, type, arg);
-		break;
-	}
-
-	return 0;
-}
-
 int btrfs_list_get_path_rootid(int fd, u64 *treeid)
 {
 	int ret;
diff --git a/btrfs-list.h b/btrfs-list.h
index 6e5fc778..54e1888f 100644
--- a/btrfs-list.h
+++ b/btrfs-list.h
@@ -31,12 +31,6 @@
 
 #include <time.h>
 
-enum btrfs_list_layout {
-	BTRFS_LIST_LAYOUT_DEFAULT = 0,
-	BTRFS_LIST_LAYOUT_TABLE,
-	BTRFS_LIST_LAYOUT_RAW
-};
-
 /*
  * one of these for each root we find.
  */
@@ -87,89 +81,6 @@ 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 *,
-				    int);
-
-struct btrfs_list_filter {
-	btrfs_list_filter_func filter_func;
-	u64 data;
-};
-
-struct btrfs_list_comparer {
-	btrfs_list_comp_func comp_func;
-	int is_descending;
-};
-
-struct btrfs_list_filter_set {
-	int total;
-	int nfilters;
-	int only_deleted;
-	struct btrfs_list_filter filters[0];
-};
-
-struct btrfs_list_comparer_set {
-	int total;
-	int ncomps;
-	struct btrfs_list_comparer comps[0];
-};
-
-enum btrfs_list_column_enum {
-	BTRFS_LIST_OBJECTID,
-	BTRFS_LIST_GENERATION,
-	BTRFS_LIST_OGENERATION,
-	BTRFS_LIST_PARENT,
-	BTRFS_LIST_TOP_LEVEL,
-	BTRFS_LIST_OTIME,
-	BTRFS_LIST_PUUID,
-	BTRFS_LIST_RUUID,
-	BTRFS_LIST_UUID,
-	BTRFS_LIST_PATH,
-	BTRFS_LIST_ALL,
-};
-
-enum btrfs_list_filter_enum {
-	BTRFS_LIST_FILTER_ROOTID,
-	BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
-	BTRFS_LIST_FILTER_FLAGS,
-	BTRFS_LIST_FILTER_GEN,
-	BTRFS_LIST_FILTER_GEN_EQUAL	=	BTRFS_LIST_FILTER_GEN,
-	BTRFS_LIST_FILTER_GEN_LESS,
-	BTRFS_LIST_FILTER_GEN_MORE,
-	BTRFS_LIST_FILTER_CGEN,
-	BTRFS_LIST_FILTER_CGEN_EQUAL	=	BTRFS_LIST_FILTER_CGEN,
-	BTRFS_LIST_FILTER_CGEN_LESS,
-	BTRFS_LIST_FILTER_CGEN_MORE,
-	BTRFS_LIST_FILTER_TOPID_EQUAL,
-	BTRFS_LIST_FILTER_FULL_PATH,
-	BTRFS_LIST_FILTER_BY_PARENT,
-	BTRFS_LIST_FILTER_DELETED,
-	BTRFS_LIST_FILTER_MAX,
-};
-
-enum btrfs_list_comp_enum {
-	BTRFS_LIST_COMP_ROOTID,
-	BTRFS_LIST_COMP_OGEN,
-	BTRFS_LIST_COMP_GEN,
-	BTRFS_LIST_COMP_PATH,
-	BTRFS_LIST_COMP_MAX,
-};
-
-int btrfs_list_parse_sort_string(char *optarg,
-				 struct btrfs_list_comparer_set **comps);
-int btrfs_list_parse_filter_string(char *optarg,
-				   struct btrfs_list_filter_set **filters,
-				   enum btrfs_list_filter_enum type);
-void btrfs_list_setup_print_column(enum btrfs_list_column_enum column);
-struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void);
-void btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
-			    enum btrfs_list_filter_enum filter, u64 data);
-struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void);
-
-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 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);
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 101ba4ca..392e4660 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -371,6 +371,954 @@ keep_fd:
 	return ret;
 }
 
+enum btrfs_list_layout {
+	BTRFS_LIST_LAYOUT_DEFAULT = 0,
+	BTRFS_LIST_LAYOUT_TABLE,
+	BTRFS_LIST_LAYOUT_RAW
+};
+
+enum btrfs_list_column_enum {
+	BTRFS_LIST_OBJECTID,
+	BTRFS_LIST_GENERATION,
+	BTRFS_LIST_OGENERATION,
+	BTRFS_LIST_PARENT,
+	BTRFS_LIST_TOP_LEVEL,
+	BTRFS_LIST_OTIME,
+	BTRFS_LIST_PUUID,
+	BTRFS_LIST_RUUID,
+	BTRFS_LIST_UUID,
+	BTRFS_LIST_PATH,
+	BTRFS_LIST_ALL,
+};
+
+enum btrfs_list_filter_enum {
+	BTRFS_LIST_FILTER_ROOTID,
+	BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
+	BTRFS_LIST_FILTER_FLAGS,
+	BTRFS_LIST_FILTER_GEN,
+	BTRFS_LIST_FILTER_GEN_EQUAL	=	BTRFS_LIST_FILTER_GEN,
+	BTRFS_LIST_FILTER_GEN_LESS,
+	BTRFS_LIST_FILTER_GEN_MORE,
+	BTRFS_LIST_FILTER_CGEN,
+	BTRFS_LIST_FILTER_CGEN_EQUAL	=	BTRFS_LIST_FILTER_CGEN,
+	BTRFS_LIST_FILTER_CGEN_LESS,
+	BTRFS_LIST_FILTER_CGEN_MORE,
+	BTRFS_LIST_FILTER_TOPID_EQUAL,
+	BTRFS_LIST_FILTER_FULL_PATH,
+	BTRFS_LIST_FILTER_BY_PARENT,
+	BTRFS_LIST_FILTER_DELETED,
+	BTRFS_LIST_FILTER_MAX,
+};
+
+enum btrfs_list_comp_enum {
+	BTRFS_LIST_COMP_ROOTID,
+	BTRFS_LIST_COMP_OGEN,
+	BTRFS_LIST_COMP_GEN,
+	BTRFS_LIST_COMP_PATH,
+	BTRFS_LIST_COMP_MAX,
+};
+
+#define BTRFS_LIST_NFILTERS_INCREASE	(2 * BTRFS_LIST_FILTER_MAX)
+#define BTRFS_LIST_NCOMPS_INCREASE	(2 * BTRFS_LIST_COMP_MAX)
+
+struct listed_subvol {
+	struct btrfs_util_subvolume_info info;
+	char *path;
+};
+
+struct subvol_list {
+	size_t num;
+	struct listed_subvol subvols[];
+};
+
+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 {
+	btrfs_list_filter_func filter_func;
+	u64 data;
+};
+
+struct btrfs_list_comparer {
+	btrfs_list_comp_func comp_func;
+	int is_descending;
+};
+
+struct btrfs_list_filter_set {
+	int total;
+	int nfilters;
+	int only_deleted;
+	struct btrfs_list_filter filters[0];
+};
+
+struct btrfs_list_comparer_set {
+	int total;
+	int ncomps;
+	struct btrfs_list_comparer comps[0];
+};
+
+static struct {
+	char	*name;
+	char	*column_name;
+	int	need_print;
+} btrfs_list_columns[] = {
+	{
+		.name		= "ID",
+		.column_name	= "ID",
+		.need_print	= 0,
+	},
+	{
+		.name		= "gen",
+		.column_name	= "Gen",
+		.need_print	= 0,
+	},
+	{
+		.name		= "cgen",
+		.column_name	= "CGen",
+		.need_print	= 0,
+	},
+	{
+		.name		= "parent",
+		.column_name	= "Parent",
+		.need_print	= 0,
+	},
+	{
+		.name		= "top level",
+		.column_name	= "Top Level",
+		.need_print	= 0,
+	},
+	{
+		.name		= "otime",
+		.column_name	= "OTime",
+		.need_print	= 0,
+	},
+	{
+		.name		= "parent_uuid",
+		.column_name	= "Parent UUID",
+		.need_print	= 0,
+	},
+	{
+		.name		= "received_uuid",
+		.column_name	= "Received UUID",
+		.need_print	= 0,
+	},
+	{
+		.name		= "uuid",
+		.column_name	= "UUID",
+		.need_print	= 0,
+	},
+	{
+		.name		= "path",
+		.column_name	= "Path",
+		.need_print	= 0,
+	},
+	{
+		.name		= NULL,
+		.column_name	= NULL,
+		.need_print	= 0,
+	},
+};
+
+static btrfs_list_filter_func all_filter_funcs[];
+static btrfs_list_comp_func all_comp_funcs[];
+
+static void btrfs_list_setup_print_column(enum btrfs_list_column_enum column)
+{
+	int i;
+
+	ASSERT(0 <= column && column <= BTRFS_LIST_ALL);
+
+	if (column < BTRFS_LIST_ALL) {
+		btrfs_list_columns[column].need_print = 1;
+		return;
+	}
+
+	for (i = 0; i < BTRFS_LIST_ALL; i++)
+		btrfs_list_columns[i].need_print = 1;
+}
+
+static int comp_entry_with_rootid(const struct listed_subvol *entry1,
+				  const struct listed_subvol *entry2,
+				  int is_descending)
+{
+	int ret;
+
+	if (entry1->info.id > entry2->info.id)
+		ret = 1;
+	else if (entry1->info.id < entry2->info.id)
+		ret = -1;
+	else
+		ret = 0;
+
+	return is_descending ? -ret : ret;
+}
+
+static int comp_entry_with_gen(const struct listed_subvol *entry1,
+			       const struct listed_subvol *entry2,
+			       int is_descending)
+{
+	int ret;
+
+	if (entry1->info.generation > entry2->info.generation)
+		ret = 1;
+	else if (entry1->info.generation < entry2->info.generation)
+		ret = -1;
+	else
+		ret = 0;
+
+	return is_descending ? -ret : ret;
+}
+
+static int comp_entry_with_ogen(const struct listed_subvol *entry1,
+				const struct listed_subvol *entry2,
+				int is_descending)
+{
+	int ret;
+
+	if (entry1->info.otransid > entry2->info.otransid)
+		ret = 1;
+	else if (entry1->info.otransid < entry2->info.otransid)
+		ret = -1;
+	else
+		ret = 0;
+
+	return is_descending ? -ret : ret;
+}
+
+static int comp_entry_with_path(const struct listed_subvol *entry1,
+				const struct listed_subvol *entry2,
+				int is_descending)
+{
+	int ret;
+
+	if (strcmp(entry1->path, entry2->path) > 0)
+		ret = 1;
+	else if (strcmp(entry1->path, entry2->path) < 0)
+		ret = -1;
+	else
+		ret = 0;
+
+	return is_descending ? -ret : ret;
+}
+
+static btrfs_list_comp_func all_comp_funcs[] = {
+	[BTRFS_LIST_COMP_ROOTID]	= comp_entry_with_rootid,
+	[BTRFS_LIST_COMP_OGEN]		= comp_entry_with_ogen,
+	[BTRFS_LIST_COMP_GEN]		= comp_entry_with_gen,
+	[BTRFS_LIST_COMP_PATH]		= comp_entry_with_path,
+};
+
+static char *all_sort_items[] = {
+	[BTRFS_LIST_COMP_ROOTID]	= "rootid",
+	[BTRFS_LIST_COMP_OGEN]		= "ogen",
+	[BTRFS_LIST_COMP_GEN]		= "gen",
+	[BTRFS_LIST_COMP_PATH]		= "path",
+	[BTRFS_LIST_COMP_MAX]		= NULL,
+};
+
+static int btrfs_list_get_sort_item(char *sort_name)
+{
+	int i;
+
+	for (i = 0; i < BTRFS_LIST_COMP_MAX; i++) {
+		if (strcmp(sort_name, all_sort_items[i]) == 0)
+			return i;
+	}
+	return -1;
+}
+
+static struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void)
+{
+	struct btrfs_list_comparer_set *set;
+	int size;
+
+	size = sizeof(struct btrfs_list_comparer_set) +
+	       BTRFS_LIST_NCOMPS_INCREASE * sizeof(struct btrfs_list_comparer);
+	set = calloc(1, size);
+	if (!set) {
+		fprintf(stderr, "memory allocation failed\n");
+		exit(1);
+	}
+
+	set->total = BTRFS_LIST_NCOMPS_INCREASE;
+
+	return set;
+}
+
+static int btrfs_list_setup_comparer(struct btrfs_list_comparer_set **comp_set,
+				     enum btrfs_list_comp_enum comparer,
+				     int is_descending)
+{
+	struct btrfs_list_comparer_set *set = *comp_set;
+	int size;
+
+	ASSERT(set != NULL);
+	ASSERT(comparer < BTRFS_LIST_COMP_MAX);
+	ASSERT(set->ncomps <= set->total);
+
+	if (set->ncomps == set->total) {
+		void *tmp;
+
+		size = set->total + BTRFS_LIST_NCOMPS_INCREASE;
+		size = sizeof(*set) + size * sizeof(struct btrfs_list_comparer);
+		tmp = set;
+		set = realloc(set, size);
+		if (!set) {
+			fprintf(stderr, "memory allocation failed\n");
+			free(tmp);
+			exit(1);
+		}
+
+		memset(&set->comps[set->total], 0,
+		       BTRFS_LIST_NCOMPS_INCREASE *
+		       sizeof(struct btrfs_list_comparer));
+		set->total += BTRFS_LIST_NCOMPS_INCREASE;
+		*comp_set = set;
+	}
+
+	ASSERT(set->comps[set->ncomps].comp_func == NULL);
+
+	set->comps[set->ncomps].comp_func = all_comp_funcs[comparer];
+	set->comps[set->ncomps].is_descending = is_descending;
+	set->ncomps++;
+	return 0;
+}
+
+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 ret;
+	int i;
+
+	for (i = 0; set && i < set->ncomps; i++) {
+		if (!set->comps[i].comp_func)
+			break;
+
+		ret = set->comps[i].comp_func(entry1, entry2,
+					      set->comps[i].is_descending);
+		if (ret)
+			return ret;
+
+		if (set->comps[i].comp_func == comp_entry_with_rootid)
+			rootid_compared = 1;
+	}
+
+	if (!rootid_compared)
+		return comp_entry_with_rootid(entry1, entry2, 0);
+
+	return 0;
+}
+
+static void sort_subvols(struct btrfs_list_comparer_set *comp_set,
+			 struct subvol_list *subvols)
+{
+	qsort_r(subvols->subvols, subvols->num, sizeof(subvols->subvols[0]),
+		subvol_comp, comp_set);
+}
+
+static int filter_by_rootid(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.id == data;
+}
+
+static int filter_snapshot(struct listed_subvol *subvol, uint64_t data)
+{
+	return !uuid_is_null(subvol->info.parent_uuid);
+}
+
+static int filter_flags(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.flags & data;
+}
+
+static int filter_gen_more(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.generation >= data;
+}
+
+static int filter_gen_less(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.generation <= data;
+}
+
+static int filter_gen_equal(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.generation == data;
+}
+
+static int filter_cgen_more(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.otransid >= data;
+}
+
+static int filter_cgen_less(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.otransid <= data;
+}
+
+static int filter_cgen_equal(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.otransid == data;
+}
+
+static int filter_topid_equal(struct listed_subvol *subvol, uint64_t data)
+{
+	/* See the comment in print_subvolume_column() about top level. */
+	return subvol->info.parent_id == data;
+}
+
+static int filter_full_path(struct listed_subvol *subvol, uint64_t data)
+{
+	/*
+	 * This implements the same behavior as before the conversion to
+	 * libbtrfsutil, which is mostly nonsensical.
+	 */
+	if (subvol->info.parent_id != data) {
+		char *tmp;
+		int ret;
+
+		ret = asprintf(&tmp, "<FS_TREE>/%s", subvol->path);
+		if (ret == -1) {
+			error("out of memory");
+			exit(1);
+		}
+
+		free(subvol->path);
+		subvol->path = tmp;
+	}
+	return 1;
+}
+
+static int filter_by_parent(struct listed_subvol *subvol, uint64_t data)
+{
+	return !uuid_compare(subvol->info.parent_uuid,
+			     (u8 *)(unsigned long)data);
+}
+
+static btrfs_list_filter_func all_filter_funcs[] = {
+	[BTRFS_LIST_FILTER_ROOTID]		= filter_by_rootid,
+	[BTRFS_LIST_FILTER_SNAPSHOT_ONLY]	= filter_snapshot,
+	[BTRFS_LIST_FILTER_FLAGS]		= filter_flags,
+	[BTRFS_LIST_FILTER_GEN_MORE]		= filter_gen_more,
+	[BTRFS_LIST_FILTER_GEN_LESS]		= filter_gen_less,
+	[BTRFS_LIST_FILTER_GEN_EQUAL]           = filter_gen_equal,
+	[BTRFS_LIST_FILTER_CGEN_MORE]		= filter_cgen_more,
+	[BTRFS_LIST_FILTER_CGEN_LESS]		= filter_cgen_less,
+	[BTRFS_LIST_FILTER_CGEN_EQUAL]          = filter_cgen_equal,
+	[BTRFS_LIST_FILTER_TOPID_EQUAL]		= filter_topid_equal,
+	[BTRFS_LIST_FILTER_FULL_PATH]		= filter_full_path,
+	[BTRFS_LIST_FILTER_BY_PARENT]		= filter_by_parent,
+};
+
+static struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void)
+{
+	struct btrfs_list_filter_set *set;
+	int size;
+
+	size = sizeof(struct btrfs_list_filter_set) +
+	       BTRFS_LIST_NFILTERS_INCREASE * sizeof(struct btrfs_list_filter);
+	set = calloc(1, size);
+	if (!set) {
+		fprintf(stderr, "memory allocation failed\n");
+		exit(1);
+	}
+
+	set->total = BTRFS_LIST_NFILTERS_INCREASE;
+
+	return set;
+}
+
+/*
+ * Setup list filters. Exit if there's not enough memory, as we can't continue
+ * without the structures set up properly.
+ */
+static void btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
+				    enum btrfs_list_filter_enum filter,
+				    u64 data)
+{
+	struct btrfs_list_filter_set *set = *filter_set;
+	int size;
+
+	ASSERT(set != NULL);
+	ASSERT(filter < BTRFS_LIST_FILTER_MAX);
+	ASSERT(set->nfilters <= set->total);
+
+	if (set->nfilters == set->total) {
+		void *tmp;
+
+		size = set->total + BTRFS_LIST_NFILTERS_INCREASE;
+		size = sizeof(*set) + size * sizeof(struct btrfs_list_filter);
+		tmp = set;
+		set = realloc(set, size);
+		if (!set) {
+			fprintf(stderr, "memory allocation failed\n");
+			free(tmp);
+			exit(1);
+		}
+
+		memset(&set->filters[set->total], 0,
+		       BTRFS_LIST_NFILTERS_INCREASE *
+		       sizeof(struct btrfs_list_filter));
+		set->total += BTRFS_LIST_NFILTERS_INCREASE;
+		*filter_set = set;
+	}
+
+	ASSERT(set->filters[set->nfilters].filter_func == NULL);
+
+	if (filter == BTRFS_LIST_FILTER_DELETED) {
+		set->only_deleted = 1;
+	} else {
+		set->filters[set->nfilters].filter_func = all_filter_funcs[filter];
+		set->filters[set->nfilters].data = data;
+		set->nfilters++;
+	}
+}
+
+static int filters_match(struct listed_subvol *subvol,
+			 struct btrfs_list_filter_set *set)
+{
+	int i, ret;
+
+	if (!set)
+		return 1;
+
+	for (i = 0; i < set->nfilters; i++) {
+		if (!set->filters[i].filter_func)
+			break;
+		ret = set->filters[i].filter_func(subvol, set->filters[i].data);
+		if (!ret)
+			return 0;
+	}
+	return 1;
+}
+
+static void print_subvolume_column(struct listed_subvol *subvol,
+				   enum btrfs_list_column_enum column)
+{
+	char tstr[256];
+	char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
+
+	ASSERT(0 <= column && column < BTRFS_LIST_ALL);
+
+	switch (column) {
+	case BTRFS_LIST_OBJECTID:
+		printf("%" PRIu64, subvol->info.id);
+		break;
+	case BTRFS_LIST_GENERATION:
+		printf("%" PRIu64, subvol->info.generation);
+		break;
+	case BTRFS_LIST_OGENERATION:
+		printf("%" PRIu64, subvol->info.otransid);
+		break;
+	case BTRFS_LIST_PARENT:
+	/*
+	 * 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("%" PRIu64, subvol->info.parent_id);
+		break;
+	case BTRFS_LIST_OTIME:
+		if (subvol->info.otime.tv_sec) {
+			struct tm 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(subvol->info.uuid))
+			strcpy(uuidparse, "-");
+		else
+			uuid_unparse(subvol->info.uuid, uuidparse);
+		printf("%-36s", uuidparse);
+		break;
+	case BTRFS_LIST_PUUID:
+		if (uuid_is_null(subvol->info.parent_uuid))
+			strcpy(uuidparse, "-");
+		else
+			uuid_unparse(subvol->info.parent_uuid, uuidparse);
+		printf("%-36s", uuidparse);
+		break;
+	case BTRFS_LIST_RUUID:
+		if (uuid_is_null(subvol->info.received_uuid))
+			strcpy(uuidparse, "-");
+		else
+			uuid_unparse(subvol->info.received_uuid, uuidparse);
+		printf("%-36s", uuidparse);
+		break;
+	case BTRFS_LIST_PATH:
+		printf("%s", subvol->path);
+		break;
+	default:
+		break;
+	}
+}
+
+static void print_one_subvol_info_raw(struct listed_subvol *subvol,
+				      const char *raw_prefix)
+{
+	int i;
+
+	for (i = 0; i < BTRFS_LIST_ALL; i++) {
+		if (!btrfs_list_columns[i].need_print)
+			continue;
+
+		if (raw_prefix)
+			printf("%s",raw_prefix);
+
+		print_subvolume_column(subvol, i);
+	}
+	printf("\n");
+}
+
+static void print_one_subvol_info_table(struct listed_subvol *subvol)
+{
+	int i;
+
+	for (i = 0; i < BTRFS_LIST_ALL; i++) {
+		if (!btrfs_list_columns[i].need_print)
+			continue;
+
+		print_subvolume_column(subvol, i);
+
+		if (i != BTRFS_LIST_PATH)
+			printf("\t");
+
+		if (i == BTRFS_LIST_TOP_LEVEL)
+			printf("\t");
+	}
+	printf("\n");
+}
+
+static void print_one_subvol_info_default(struct listed_subvol *subvol)
+{
+	int i;
+
+	for (i = 0; i < BTRFS_LIST_ALL; i++) {
+		if (!btrfs_list_columns[i].need_print)
+			continue;
+
+		printf("%s ", btrfs_list_columns[i].name);
+		print_subvolume_column(subvol, i);
+
+		if (i != BTRFS_LIST_PATH)
+			printf(" ");
+	}
+	printf("\n");
+}
+
+static void print_all_subvol_info_tab_head(void)
+{
+	int i;
+	int len;
+	char barrier[20];
+
+	for (i = 0; i < BTRFS_LIST_ALL; i++) {
+		if (btrfs_list_columns[i].need_print)
+			printf("%s\t", btrfs_list_columns[i].name);
+
+		if (i == BTRFS_LIST_ALL-1)
+			printf("\n");
+	}
+
+	for (i = 0; i < BTRFS_LIST_ALL; i++) {
+		memset(barrier, 0, sizeof(barrier));
+
+		if (btrfs_list_columns[i].need_print) {
+			len = strlen(btrfs_list_columns[i].name);
+			while (len--)
+				strcat(barrier, "-");
+
+			printf("%s\t", barrier);
+		}
+		if (i == BTRFS_LIST_ALL-1)
+			printf("\n");
+	}
+}
+
+static void print_all_subvol_info(struct subvol_list *subvols,
+				  enum btrfs_list_layout layout,
+				  const char *raw_prefix)
+{
+	size_t i;
+
+	if (layout == BTRFS_LIST_LAYOUT_TABLE)
+		print_all_subvol_info_tab_head();
+
+	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(subvol);
+			break;
+		case BTRFS_LIST_LAYOUT_TABLE:
+			print_one_subvol_info_table(subvol);
+			break;
+		case BTRFS_LIST_LAYOUT_RAW:
+			print_one_subvol_info_raw(subvol, raw_prefix);
+			break;
+		}
+	}
+}
+
+static void free_subvol_list(struct subvol_list *subvols)
+{
+	size_t i;
+
+	if (subvols) {
+		for (i = 0; i < subvols->num; i++)
+			free(subvols->subvols[i].path);
+		free(subvols);
+	}
+}
+
+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_deleted_subvolumes_fd(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_subvolume_info_fd(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) {
+		free_subvol_list(subvols);
+		subvols = NULL;
+		free(ids);
+	}
+	return subvols;
+}
+
+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_create_subvolume_iterator_fd(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;
+}
+
+static 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 subvol_list *subvols;
+
+	/*
+	 * 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
+	 */
+
+	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);
+
+	free_subvol_list(subvols);
+
+	return 0;
+}
+
+static int btrfs_list_parse_sort_string(char *opt_arg,
+					struct btrfs_list_comparer_set **comps)
+{
+	int order;
+	int flag;
+	char *p;
+	char **ptr_argv;
+	int what_to_sort;
+
+	while ((p = strtok(opt_arg, ",")) != NULL) {
+		flag = 0;
+		ptr_argv = all_sort_items;
+
+		while (*ptr_argv) {
+			if (strcmp(*ptr_argv, p) == 0) {
+				flag = 1;
+				break;
+			} else {
+				p++;
+				if (strcmp(*ptr_argv, p) == 0) {
+					flag = 1;
+					p--;
+					break;
+				}
+				p--;
+			}
+			ptr_argv++;
+		}
+
+		if (flag == 0)
+			return -1;
+
+		else {
+			if (*p == '+') {
+				order = 0;
+				p++;
+			} else if (*p == '-') {
+				order = 1;
+				p++;
+			} else
+				order = 0;
+
+			what_to_sort = btrfs_list_get_sort_item(p);
+			btrfs_list_setup_comparer(comps, what_to_sort, order);
+		}
+		opt_arg = NULL;
+	}
+
+	return 0;
+}
+
+static int btrfs_list_parse_filter_string(char *opt_arg,
+					  struct btrfs_list_filter_set **filters,
+					  enum btrfs_list_filter_enum type)
+{
+
+	u64 arg;
+
+	switch (*(opt_arg++)) {
+	case '+':
+		arg = arg_strtou64(opt_arg);
+		type += 2;
+
+		btrfs_list_setup_filter(filters, type, arg);
+		break;
+	case '-':
+		arg = arg_strtou64(opt_arg);
+		type += 1;
+
+		btrfs_list_setup_filter(filters, type, arg);
+		break;
+	default:
+		opt_arg--;
+		arg = arg_strtou64(opt_arg);
+
+		btrfs_list_setup_filter(filters, type, arg);
+		break;
+	}
+
+	return 0;
+}
+
 /*
  * Naming of options:
  * - uppercase for filters and sort options
-- 
2.16.1


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

* [PATCH v2 27/27] btrfs-progs: deprecate libbtrfs helpers
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (25 preceding siblings ...)
  2018-02-15 19:05 ` [PATCH v2 26/27] btrfs-progs: use libbtrfsutil for subvolume list Omar Sandoval
@ 2018-02-15 19:05 ` Omar Sandoval
  2018-02-21 15:04   ` David Sterba
  2018-02-20 18:50 ` [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" David Sterba
  27 siblings, 1 reply; 64+ messages in thread
From: Omar Sandoval @ 2018-02-15 19:05 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. Update btrfs-progs to use 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>
---
 btrfs-list.c     |  6 ++++++
 btrfs-list.h     | 15 ++++++++++-----
 cmds-inspect.c   | 10 ++++++----
 cmds-receive.c   | 13 +++++++++----
 cmds-subvolume.c | 10 +++++++---
 send-utils.c     | 25 +++++++++++++++++--------
 utils.h          |  1 -
 7 files changed, 55 insertions(+), 25 deletions(-)

diff --git a/btrfs-list.c b/btrfs-list.c
index a2fdb3f9..56aa2455 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -34,6 +34,12 @@
 #include "btrfs-list.h"
 #include "rbtree-utils.h"
 
+/*
+ * The deprecated functions in this file depend on each other, so silence the
+ * warnings.
+ */
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
 /* we store all the roots we find in an rbtree so that we can
  * search for them later.
  */
diff --git a/btrfs-list.h b/btrfs-list.h
index 54e1888f..774bcece 100644
--- a/btrfs-list.h
+++ b/btrfs-list.h
@@ -82,10 +82,15 @@ struct root_info {
 };
 
 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_get_default_subvolume_fd()")));
+char *btrfs_list_path_for_root(int fd, u64 root)
+	__attribute__((deprecated("use btrfs_util_subvolume_path_fd()")));
+int btrfs_list_get_path_rootid(int fd, u64 *treeid)
+	__attribute__((deprecated("use btrfs_util_subvolume_id_fd()")));
+int btrfs_get_subvol(int fd, struct root_info *the_ri)
+	__attribute__((deprecated("use btrfs_util_subvolume_info_fd() and btrfs_util_subvolume_path_fd()")));
+int btrfs_get_toplevel_subvol(int fd, struct root_info *the_ri)
+	__attribute__((deprecated("use btrfs_util_subvolume_info_fd() and btrfs_util_subvolume_path_fd()")));
 
 #endif
diff --git a/cmds-inspect.c b/cmds-inspect.c
index afd7fe48..77585a23 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"
@@ -30,7 +32,6 @@
 #include "send-utils.h"
 #include "disk-io.h"
 #include "commands.h"
-#include "btrfs-list.h"
 #include "help.h"
 
 static const char * const inspect_cmd_group_usage[] = {
@@ -147,6 +148,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 +221,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_subvolume_path_fd(fd, root, &name);
+			if (err) {
+				ret = -errno;
 				goto out;
 			}
 			if (!name) {
diff --git a/cmds-receive.c b/cmds-receive.c
index 68123a31..cd880ee7 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -39,12 +39,13 @@
 #include <sys/xattr.h>
 #include <uuid/uuid.h>
 
+#include <btrfsutil.h>
+
 #include "ctree.h"
 #include "ioctl.h"
 #include "commands.h"
 #include "utils.h"
 #include "list.h"
-#include "btrfs-list.h"
 
 #include "send.h"
 #include "send-stream.h"
@@ -1086,12 +1087,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 +1138,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_subvolume_id_fd(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 392e4660..fad6ffcb 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -1370,13 +1370,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();
@@ -1489,9 +1490,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_subvolume_id_fd(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 b5289e76..1d6e2da7 100644
--- a/send-utils.c
+++ b/send-utils.c
@@ -23,10 +23,11 @@
 #include <limits.h>
 #include <errno.h>
 
+#include <btrfsutil.h>
+
 #include "ctree.h"
 #include "send-utils.h"
 #include "ioctl.h"
-#include "btrfs-list.h"
 
 static int btrfs_subvolid_resolve_sub(int fd, char *path, size_t *path_len,
 				      u64 subvol_id);
@@ -36,6 +37,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,7 +48,13 @@ 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_subvolume_id_fd(subvol_fd, &id);
+	if (err) {
+		ret = -errno;
+	} else {
+		*root_id = id;
+		ret = 0;
+	}
 	close(subvol_fd);
 	return ret;
 }
@@ -574,6 +583,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;
 
@@ -646,12 +656,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_subvolume_path_fd(mnt_fd,
+								   btrfs_search_header_objectid(sh),
+								   &path);
+				if (err) {
+					ret = -errno;
 					fprintf(stderr, "ERROR: unable to "
 							"resolve path "
 							"for root %llu\n",
diff --git a/utils.h b/utils.h
index 403de481..d5cf7edb 100644
--- a/utils.h
+++ b/utils.h
@@ -25,7 +25,6 @@
 #include <stdarg.h>
 #include "common-defs.h"
 #include "internal.h"
-#include "btrfs-list.h"
 #include "sizes.h"
 #include "messages.h"
 #include "ioctl.h"
-- 
2.16.1


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

* Re: [PATCH v2 02/27] Add libbtrfsutil
  2018-02-15 19:04 ` [PATCH v2 02/27] Add libbtrfsutil Omar Sandoval
@ 2018-02-20 17:32   ` Liu Bo
  2018-02-20 18:34     ` David Sterba
  0 siblings, 1 reply; 64+ messages in thread
From: Liu Bo @ 2018-02-20 17:32 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: linux-btrfs, kernel-team

On Thu, Feb 15, 2018 at 11:04:47AM -0800, 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                          |  72 ++--
>  Makefile.inc.in                   |   2 +-
>  libbtrfsutil/COPYING              | 674 ++++++++++++++++++++++++++++++++++++++
>  libbtrfsutil/COPYING.LESSER       | 165 ++++++++++
>  libbtrfsutil/README.md            |  35 ++
>  libbtrfsutil/btrfsutil.h          |  76 +++++
>  libbtrfsutil/btrfsutil_internal.h |  40 +++
>  libbtrfsutil/errors.c             |  55 ++++
>  9 files changed, 1100 insertions(+), 21 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/btrfsutil_internal.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
>

.gitignore is not part of btrfs-progs, is it?

I Skipped Makefile, the .c and .h look good to me, 

Reviewed-by: Liu Bo <bo.li.liu@oracle.com>

Thanks,

-liubo

>  /tests/*-tests-results.txt
>  /tests/test-console.txt
> diff --git a/Makefile b/Makefile
> index 00e21379..7fb70d06 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -73,10 +73,20 @@ CFLAGS = $(SUBST_CFLAGS) \
>  	 -fPIC \
>  	 -I$(TOPDIR) \
>  	 -I$(TOPDIR)/kernel-lib \
> +	 -I$(TOPDIR)/libbtrfsutil \
>  	 $(EXTRAWARN_CFLAGS) \
>  	 $(DEBUG_CFLAGS_INTERNAL) \
>  	 $(EXTRA_CFLAGS)
>  
> +LIBBTRFSUTIL_CFLAGS = $(SUBST_CFLAGS) \
> +		      $(CSTD) \
> +		      -D_GNU_SOURCE \
> +		      -fPIC \
> +		      -fvisibility=hidden \
> +		      -I$(TOPDIR)/libbtrfsutil \
> +		      $(EXTRAWARN_CFLAGS) \
> +		      $(EXTRA_CFLAGS)
> +
>  LDFLAGS = $(SUBST_LDFLAGS) \
>  	  -rdynamic -L$(TOPDIR) \
>  	  $(DEBUG_LDFLAGS_INTERNAL) \
> @@ -121,12 +131,17 @@ 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_major := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_MAJOR ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
> +libbtrfsutil_minor := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_MINOR ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
> +libbtrfsutil_patch := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_PATCH ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
> +libbtrfsutil_version := $(libbtrfsutil_major).$(libbtrfsutil_minor).$(libbtrfsutil_patch)
> +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 mkfs/rootdir.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)
>  
>  udev_rules = 64-btrfs-dm.rules
>  
> @@ -246,11 +261,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
> -headers = $(libbtrfs_headers)
> +lib_links = libbtrfs.so.0 libbtrfs.so libbtrfsutil.so.$(libbtrfsutil_major) libbtrfsutil.so
>  
>  # make C=1 to enable sparse
>  ifdef C
> @@ -287,7 +301,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-%,%,$@)"
> @@ -351,20 +365,35 @@ 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 $< $@
> +
> +libbtrfsutil/%.o: libbtrfsutil/%.c
> +	@echo "    [CC]     $@"
> +	$(Q)$(CC) $(LIBBTRFSUTIL_CFLAGS) -o $@ -c $< -o $@
>  
> -$(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) $(LIBBTRFSUTIL_CFLAGS) $(libbtrfsutil_objects) \
> +		-shared -Wl,-soname,libbtrfsutil.so.$(libbtrfsutil_major) -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.$(libbtrfsutil_major) 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))
> @@ -483,7 +512,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 && \
> @@ -547,7 +576,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"
> @@ -575,8 +605,9 @@ install: $(libs) $(progs_install) $(INSTALLDIRS)
>  	$(INSTALL) -m755 -d $(DESTDIR)$(libdir)
>  	$(INSTALL) $(libs) $(DESTDIR)$(libdir)
>  	cp -a $(lib_links) $(DESTDIR)$(libdir)
> -	$(INSTALL) -m755 -d $(DESTDIR)$(incdir)
> -	$(INSTALL) -m644 $(headers) $(DESTDIR)$(incdir)
> +	$(INSTALL) -m755 -d $(DESTDIR)$(incdir)/btrfs
> +	$(INSTALL) -m644 $(libbtrfs_headers) $(DESTDIR)$(incdir)/btrfs
> +	$(INSTALL) -m644 libbtrfsutil/btrfsutil.h $(DESTDIR)$(incdir)
>  ifneq ($(udevdir),)
>  	$(INSTALL) -m755 -d $(DESTDIR)$(udevruledir)
>  	$(INSTALL) -m644 $(udev_rules) $(DESTDIR)$(udevruledir)
> @@ -594,8 +625,9 @@ $(INSTALLDIRS):
>  
>  uninstall:
>  	$(Q)$(MAKE) $(MAKEOPTS) -C Documentation uninstall
> -	cd $(DESTDIR)$(incdir); $(RM) -f -- $(headers)
> -	$(RMDIR) -p --ignore-fail-on-non-empty -- $(DESTDIR)$(incdir)
> +	cd $(DESTDIR)$(incdir)/btrfs; $(RM) -f -- $(libbtrfs_headers)
> +	$(RMDIR) -p --ignore-fail-on-non-empty -- $(DESTDIR)$(incdir)/btrfs
> +	cd $(DESTDIR)$(incdir); $(RM) -f -- btrfsutil.h
>  	cd $(DESTDIR)$(libdir); $(RM) -f -- $(lib_links) $(libs)
>  	cd $(DESTDIR)$(bindir); $(RM) -f -- btrfsck fsck.btrfs $(progs_install)
>  
> diff --git a/Makefile.inc.in b/Makefile.inc.in
> index 56271903..b53bef80 100644
> --- a/Makefile.inc.in
> +++ b/Makefile.inc.in
> @@ -27,7 +27,7 @@ prefix ?= @prefix@
>  exec_prefix = @exec_prefix@
>  bindir = @bindir@
>  libdir ?= @libdir@
> -incdir = @includedir@/btrfs
> +incdir = @includedir@
>  udevdir = @UDEVDIR@
>  udevruledir = ${udevdir}/rules.d
>  
> 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..ee4c6a1d
> --- /dev/null
> +++ b/libbtrfsutil/README.md
> @@ -0,0 +1,35 @@
> +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.
> +
> +libbtrfsutil is semantically versioned separately from btrfs-progs. It is the
> +maintainers' responsibility to bump the version as needed (at most once per
> +release of btrfs-progs).
> +
> +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 (i.e., we don't want to bump
> +  the library major version if we don't have to)
> diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
> new file mode 100644
> index 00000000..867418f2
> --- /dev/null
> +++ b/libbtrfsutil/btrfsutil.h
> @@ -0,0 +1,76 @@
> +/*
> + * 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
> +
> +#define BTRFS_UTIL_VERSION_MAJOR 1
> +#define BTRFS_UTIL_VERSION_MINOR 0
> +#define BTRFS_UTIL_VERSION_PATCH 0
> +
> +#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/btrfsutil_internal.h b/libbtrfsutil/btrfsutil_internal.h
> new file mode 100644
> index 00000000..bdf8840a
> --- /dev/null
> +++ b/libbtrfsutil/btrfsutil_internal.h
> @@ -0,0 +1,40 @@
> +/*
> + * 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>
> +
> +#include "btrfsutil.h"
> +
> +#define PUBLIC __attribute__((visibility("default")))
> +
> +#define le16_to_cpu __le16_to_cpu
> +#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/errors.c b/libbtrfsutil/errors.c
> new file mode 100644
> index 00000000..634edc65
> --- /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_internal.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",
> +};
> +
> +PUBLIC 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
> 
> --
> 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] 64+ messages in thread

* Re: [PATCH v2 02/27] Add libbtrfsutil
  2018-02-20 17:32   ` Liu Bo
@ 2018-02-20 18:34     ` David Sterba
  0 siblings, 0 replies; 64+ messages in thread
From: David Sterba @ 2018-02-20 18:34 UTC (permalink / raw)
  To: Liu Bo; +Cc: Omar Sandoval, linux-btrfs, kernel-team

On Tue, Feb 20, 2018 at 10:32:17AM -0700, Liu Bo wrote:
> > --- a/.gitignore
> > +++ b/.gitignore
> > @@ -43,6 +43,8 @@ libbtrfs.so.0.1
> >  library-test
> >  library-test-static
> >  /fssum
> > +/libbtrfsutil.so*
> > +/libbtrfsutil.a
> 
> .gitignore is not part of btrfs-progs, is it?

It is, since version 0.19 :)

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

* Re: [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library"
  2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
                   ` (26 preceding siblings ...)
  2018-02-15 19:05 ` [PATCH v2 27/27] btrfs-progs: deprecate libbtrfs helpers Omar Sandoval
@ 2018-02-20 18:50 ` David Sterba
  2018-02-21 15:13   ` David Sterba
  27 siblings, 1 reply; 64+ messages in thread
From: David Sterba @ 2018-02-20 18:50 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: linux-btrfs, kernel-team

On Thu, Feb 15, 2018 at 11:04:45AM -0800, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>
> This is v2 of "btrfs-progs as a library".
> 
> Most of the changes since v1 are small:
> 
> - Rebased onto v4.15
> - Split up btrfs_util_subvolume_path() which was accidentally squashed together
>   with the commit adding btrfs_util_create_subvolume()
> - Renamed btrfs_util_f_* functions to btrfs_util_*_fd for clarity

I like this naming scheme.

> - Added -fvisibility=hidden and a macro for
>   __attribute__((visibility("default")))
> - Changed to use semantic versioning
> - Fixed missing install of btrfsutil.h
> - Documented functions which require root or are non-atomic
> - Added a missing license to setup.py
> 
> The bigger change is in the last two patches. Dave requested that I get
> rid of the runtime dependency of libbtrfsutil from libbtrfs. The easiest
> way to do this was to remove the btrfs_list_subvols_print()
> implementation from libbtrfs and put it in cmds-subvolume.c (details in
> patch 26). I'm open to alternatives.

This should be ok as a temporary fix to get the library going. The
column printing helpers will be replaced by libsmartcols (I have a
prototype for that but there are still some issues to fix).

> Omar Sandoval (27):
>   btrfs-progs: get rid of undocumented qgroup inheritance options

For initial merge I'll skip this patch (and what depends on it), as the
functionality is not yet out of krenel. I looked at the patch and am not
yet convinced to merge it, more time needed, but the library should not
be blocked by it.

>   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_path()
>   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

I'm going to add the above to devel now.

>   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: use libbtrfsutil for subvolume list
>   btrfs-progs: deprecate libbtrfs helpers

Besides the subvol and qgroup inheritance, all of the above look good,
but I'd like to spend more time merging them and we should also add
commandline tests for coverage.

I have more comments or maybe questions about the future development
workflow, but at this point the patchset is in a good shape for
incremental merge.

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

* Re: [PATCH v2 04/27] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id()
  2018-02-15 19:04 ` [PATCH v2 04/27] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id() Omar Sandoval
@ 2018-02-21 11:43   ` David Sterba
  2018-02-21 13:02     ` David Sterba
  0 siblings, 1 reply; 64+ messages in thread
From: David Sterba @ 2018-02-21 11:43 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: linux-btrfs, kernel-team

On Thu, Feb 15, 2018 at 11:04:49AM -0800, Omar Sandoval wrote:
> --- /dev/null
> +++ b/libbtrfsutil/subvolume.c
> @@ -0,0 +1,127 @@
> +/*
> + * 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>

How is this supposed to work? This pulls the system-wide includes, the
travis-ci environment has an old kernel and btrfs_tree.h does not exist
there. The missing defines are provided by libbtrfs' btrfs/ctree.h.

We can add a configure-time check to detect the availability of the
headers, but I'm not sure if this is right. As libbtrfsutil/subvolume.c
is built internally it should use the includes from the btrfs-progs git
itself, no?

Ie. the #ifdef BTRFS_FLAT_INCLUDES way is not necessary for non-exported
sources.

> +#include <linux/magic.h>
> +
> +#include "btrfsutil_internal.h"

All the include magic could be easily hidden inside this internal header
so we won't need to include anything from the linux/* namespace
directly.

I'll keep this patch merged and unmodified, as it works on my machine,
so any fix shall be an incremental on top of devel.

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

* Re: [PATCH v2 04/27] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id()
  2018-02-21 11:43   ` David Sterba
@ 2018-02-21 13:02     ` David Sterba
  2018-02-21 18:13       ` Omar Sandoval
  0 siblings, 1 reply; 64+ messages in thread
From: David Sterba @ 2018-02-21 13:02 UTC (permalink / raw)
  To: dsterba, Omar Sandoval, linux-btrfs, kernel-team

On Wed, Feb 21, 2018 at 12:43:07PM +0100, David Sterba wrote:
> On Thu, Feb 15, 2018 at 11:04:49AM -0800, Omar Sandoval wrote:
> > --- /dev/null
> > +++ b/libbtrfsutil/subvolume.c
> > @@ -0,0 +1,127 @@
> > +/*
> > + * 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>
> 
> How is this supposed to work? This pulls the system-wide includes, the
> travis-ci environment has an old kernel and btrfs_tree.h does not exist
> there. The missing defines are provided by libbtrfs' btrfs/ctree.h.
> 
> We can add a configure-time check to detect the availability of the
> headers, but I'm not sure if this is right. As libbtrfsutil/subvolume.c
> is built internally it should use the includes from the btrfs-progs git
> itself, no?

Oh yeah, the fun has begun.  This will need to be sorted first. ctree.h
unconditionally pulls kerncompat.h, so it cannot be used instead of the
linux/btrfs*.h headers, there are warnings about redefined endianity
conversion macros.

We can split the headers by type, so ctree.h does not contain
everything, or copy defines and types to libbtrfsutil header so it is
completely independent of the git or system headers. Or something else.

I can now make it compile without including btrfs_tree.h and copying
parts of ctree.h until it compiles. The type u8 also has to be __u8.

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

* Re: [PATCH v2 03/27] libbtrfsutil: add Python bindings
  2018-02-15 19:04 ` [PATCH v2 03/27] libbtrfsutil: add Python bindings Omar Sandoval
@ 2018-02-21 13:47   ` David Sterba
  2018-02-21 18:08     ` Omar Sandoval
  2018-02-22  1:44   ` Misono, Tomohiro
  1 sibling, 1 reply; 64+ messages in thread
From: David Sterba @ 2018-02-21 13:47 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: linux-btrfs, kernel-team

On Thu, Feb 15, 2018 at 11:04:48AM -0800, Omar Sandoval wrote:
> +setup(
> +    name='btrfsutil',
> +    version=get_version(),
> +    description='Library for managing Btrfs filesystems',
> +    url='https://github.com/kdave/btrfs-progs',
> +    license='GPLv3',

LGPLv3?

> +    cmdclass={'build_ext': my_build_ext},
> +    ext_modules=[module],
> +)

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

* Re: [PATCH v2 27/27] btrfs-progs: deprecate libbtrfs helpers
  2018-02-15 19:05 ` [PATCH v2 27/27] btrfs-progs: deprecate libbtrfs helpers Omar Sandoval
@ 2018-02-21 15:04   ` David Sterba
  2018-02-21 18:19     ` Omar Sandoval
  0 siblings, 1 reply; 64+ messages in thread
From: David Sterba @ 2018-02-21 15:04 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: linux-btrfs, kernel-team

On Thu, Feb 15, 2018 at 11:05:12AM -0800, Omar Sandoval wrote:
> diff --git a/btrfs-list.c b/btrfs-list.c
> index a2fdb3f9..56aa2455 100644
> --- a/btrfs-list.c
> +++ b/btrfs-list.c
> @@ -34,6 +34,12 @@
>  #include "btrfs-list.h"
>  #include "rbtree-utils.h"
>  
> +/*
> + * The deprecated functions in this file depend on each other, so silence the
> + * warnings.
> + */
> +#pragma GCC diagnostic ignored "-Wdeprecated-declarations"

How does this behave under clang?

> +
>  /* we store all the roots we find in an rbtree so that we can
>   * search for them later.
>   */
> diff --git a/btrfs-list.h b/btrfs-list.h
> index 54e1888f..774bcece 100644
> --- a/btrfs-list.h
> +++ b/btrfs-list.h
> @@ -82,10 +82,15 @@ struct root_info {
>  };
>  
>  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_get_default_subvolume_fd()")));

IIRC the parametrized deprecated("...") is not supported on older
compilers, see 1b1fd2c190ddb896a010a4c704ec1c2d46922aaf . We might avoid
using raw __attirbute__ anyway, so a wrapper __deprecated("with string")
could make it ifdef-ed properly. With some configure-time detection.

> +char *btrfs_list_path_for_root(int fd, u64 root)
> +	__attribute__((deprecated("use btrfs_util_subvolume_path_fd()")));
> +int btrfs_list_get_path_rootid(int fd, u64 *treeid)
> +	__attribute__((deprecated("use btrfs_util_subvolume_id_fd()")));
> +int btrfs_get_subvol(int fd, struct root_info *the_ri)
> +	__attribute__((deprecated("use btrfs_util_subvolume_info_fd() and btrfs_util_subvolume_path_fd()")));
> +int btrfs_get_toplevel_subvol(int fd, struct root_info *the_ri)
> +	__attribute__((deprecated("use btrfs_util_subvolume_info_fd() and btrfs_util_subvolume_path_fd()")));

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

* Re: [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library"
  2018-02-20 18:50 ` [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" David Sterba
@ 2018-02-21 15:13   ` David Sterba
  2018-02-21 18:50     ` Omar Sandoval
  0 siblings, 1 reply; 64+ messages in thread
From: David Sterba @ 2018-02-21 15:13 UTC (permalink / raw)
  To: dsterba, Omar Sandoval, linux-btrfs, kernel-team

On Tue, Feb 20, 2018 at 07:50:48PM +0100, David Sterba wrote:
> I have more comments or maybe questions about the future development
> workflow, but at this point the patchset is in a good shape for
> incremental merge.

After removnig the first patch adding subvolume.c (with
linux/btrfs_tree.h) and what depends on it, I'm left with:

Omar Sandoval (4):
      Add libbtrfsutil
      libbtrfsutil: add Python bindings
      libbtrfsutil: add qgroup inheritance helpers
      libbtrfsutil: add filesystem sync helpers

with some context updates. That builds and passes the CI tests.

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

* Re: [PATCH v2 03/27] libbtrfsutil: add Python bindings
  2018-02-21 13:47   ` David Sterba
@ 2018-02-21 18:08     ` Omar Sandoval
  0 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-21 18:08 UTC (permalink / raw)
  To: dsterba, linux-btrfs, kernel-team

On Wed, Feb 21, 2018 at 02:47:54PM +0100, David Sterba wrote:
> On Thu, Feb 15, 2018 at 11:04:48AM -0800, Omar Sandoval wrote:
> > +setup(
> > +    name='btrfsutil',
> > +    version=get_version(),
> > +    description='Library for managing Btrfs filesystems',
> > +    url='https://github.com/kdave/btrfs-progs',
> > +    license='GPLv3',
> 
> LGPLv3?

Yes, that was a typo. I'll send an incremental patch.

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

* Re: [PATCH v2 04/27] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id()
  2018-02-21 13:02     ` David Sterba
@ 2018-02-21 18:13       ` Omar Sandoval
  0 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-21 18:13 UTC (permalink / raw)
  To: dsterba, linux-btrfs, kernel-team

On Wed, Feb 21, 2018 at 02:02:02PM +0100, David Sterba wrote:
> On Wed, Feb 21, 2018 at 12:43:07PM +0100, David Sterba wrote:
> > On Thu, Feb 15, 2018 at 11:04:49AM -0800, Omar Sandoval wrote:
> > > --- /dev/null
> > > +++ b/libbtrfsutil/subvolume.c
> > > @@ -0,0 +1,127 @@
> > > +/*
> > > + * 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>
> > 
> > How is this supposed to work? This pulls the system-wide includes, the
> > travis-ci environment has an old kernel and btrfs_tree.h does not exist
> > there. The missing defines are provided by libbtrfs' btrfs/ctree.h.
> > 
> > We can add a configure-time check to detect the availability of the
> > headers, but I'm not sure if this is right. As libbtrfsutil/subvolume.c
> > is built internally it should use the includes from the btrfs-progs git
> > itself, no?

Dang it, yes, since the lib started out as its own repo I used the
system headers, but when I ported it to btrfs-progs I forgot to sort
this out.

> Oh yeah, the fun has begun.  This will need to be sorted first. ctree.h
> unconditionally pulls kerncompat.h, so it cannot be used instead of the
> linux/btrfs*.h headers, there are warnings about redefined endianity
> conversion macros.
> 
> We can split the headers by type, so ctree.h does not contain
> everything, or copy defines and types to libbtrfsutil header so it is
> completely independent of the git or system headers. Or something else.
> 
> I can now make it compile without including btrfs_tree.h and copying
> parts of ctree.h until it compiles. The type u8 also has to be __u8.

I'll figure something out and send a patch, thanks.

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

* Re: [PATCH v2 27/27] btrfs-progs: deprecate libbtrfs helpers
  2018-02-21 15:04   ` David Sterba
@ 2018-02-21 18:19     ` Omar Sandoval
  0 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-21 18:19 UTC (permalink / raw)
  To: dsterba, linux-btrfs, kernel-team

On Wed, Feb 21, 2018 at 04:04:28PM +0100, David Sterba wrote:
> On Thu, Feb 15, 2018 at 11:05:12AM -0800, Omar Sandoval wrote:
> > diff --git a/btrfs-list.c b/btrfs-list.c
> > index a2fdb3f9..56aa2455 100644
> > --- a/btrfs-list.c
> > +++ b/btrfs-list.c
> > @@ -34,6 +34,12 @@
> >  #include "btrfs-list.h"
> >  #include "rbtree-utils.h"
> >  
> > +/*
> > + * The deprecated functions in this file depend on each other, so silence the
> > + * warnings.
> > + */
> > +#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
> 
> How does this behave under clang?

Clang supports it.

> > +
> >  /* we store all the roots we find in an rbtree so that we can
> >   * search for them later.
> >   */
> > diff --git a/btrfs-list.h b/btrfs-list.h
> > index 54e1888f..774bcece 100644
> > --- a/btrfs-list.h
> > +++ b/btrfs-list.h
> > @@ -82,10 +82,15 @@ struct root_info {
> >  };
> >  
> >  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_get_default_subvolume_fd()")));
> 
> IIRC the parametrized deprecated("...") is not supported on older
> compilers, see 1b1fd2c190ddb896a010a4c704ec1c2d46922aaf . We might avoid
> using raw __attirbute__ anyway, so a wrapper __deprecated("with string")
> could make it ifdef-ed properly. With some configure-time detection.

This should probably be a compile-time check because clients using the
libbtrfs headers might be compiled with a different setup than what was
used to build btrfs-progs, but yes, I'll try to do this. Failing that,
I'll just remove the string. Thanks!

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

* Re: [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library"
  2018-02-21 15:13   ` David Sterba
@ 2018-02-21 18:50     ` Omar Sandoval
  2018-02-23 20:28       ` David Sterba
  0 siblings, 1 reply; 64+ messages in thread
From: Omar Sandoval @ 2018-02-21 18:50 UTC (permalink / raw)
  To: dsterba, linux-btrfs, kernel-team

On Wed, Feb 21, 2018 at 04:13:38PM +0100, David Sterba wrote:
> On Tue, Feb 20, 2018 at 07:50:48PM +0100, David Sterba wrote:
> > I have more comments or maybe questions about the future development
> > workflow, but at this point the patchset is in a good shape for
> > incremental merge.
> 
> After removnig the first patch adding subvolume.c (with
> linux/btrfs_tree.h) and what depends on it, I'm left with:
> 
> Omar Sandoval (4):
>       Add libbtrfsutil
>       libbtrfsutil: add Python bindings
>       libbtrfsutil: add qgroup inheritance helpers
>       libbtrfsutil: add filesystem sync helpers
> 
> with some context updates. That builds and passes the CI tests.

Great. Does the CI system run the Python tests yet?

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

* Re: [PATCH v2 03/27] libbtrfsutil: add Python bindings
  2018-02-15 19:04 ` [PATCH v2 03/27] libbtrfsutil: add Python bindings Omar Sandoval
  2018-02-21 13:47   ` David Sterba
@ 2018-02-22  1:44   ` Misono, Tomohiro
  1 sibling, 0 replies; 64+ messages in thread
From: Misono, Tomohiro @ 2018-02-22  1:44 UTC (permalink / raw)
  To: Omar Sandoval, linux-btrfs; +Cc: kernel-team

On 2018/02/16 4:04, Omar Sandoval wrote:
> 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          | 108 ++++++++++++++++++
>  libbtrfsutil/python/tests/__init__.py |   0
>  11 files changed, 601 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 7fb70d06..95ee9678 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -154,8 +154,10 @@ endif
>  
>  ifeq ($(BUILD_VERBOSE),1)
>    Q =
> +  SETUP_PY_Q =
>  else
>    Q = @
> +  SETUP_PY_Q = -q
>  endif
>  
>  ifeq ("$(origin D)", "command line")
> @@ -302,6 +304,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-%,%,$@)"
> @@ -345,6 +350,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
> @@ -395,6 +410,15 @@ libbtrfsutil.so.$(libbtrfsutil_major) libbtrfsutil.so: libbtrfsutil.so.$(libbtrf
>  	@echo "    [LN]     $@"
>  	$(Q)$(LN_S) -f $< $@
>  
> +ifeq ($(PYTHON_BINDINGS),1)
> +libbtrfsutil_python: libbtrfsutil.so libbtrfsutil/btrfsutil.h
> +	@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))
>  
> @@ -578,6 +602,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"
> @@ -613,6 +641,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 b53bef80..159d38ed 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 3afcdb47..50d36e4f 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -195,6 +195,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

I'm not familiar with autoconf, but python3-devel package is also needed for Python.h for Fedora
and can we check the dependency too?

> +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.
> @@ -249,6 +262,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 ee4c6a1d..0c8eba44 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
>  -----------
> @@ -33,3 +34,5 @@ A few guidelines:
>    type specific to `libbtrfsutil`)
>  * Preserve API and ABI compatability at all times (i.e., we don't want to bump
>    the library major version if we don't have to)
> +* Include Python bindings for all interfaces
> +* Write tests for all interfaces
> 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..cd8a6048
> --- /dev/null
> +++ b/libbtrfsutil/python/setup.py
> @@ -0,0 +1,108 @@
> +#!/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 get_version():
> +    with open('../btrfsutil.h', 'r') as f:
> +        btrfsutil_h = f.read()
> +    major = re.search(r'^#define BTRFS_UTIL_VERSION_MAJOR ([0-9]+)$',
> +                      btrfsutil_h, flags=re.MULTILINE).group(1)
> +    minor = re.search(r'^#define BTRFS_UTIL_VERSION_MINOR ([0-9]+)$',
> +                      btrfsutil_h, flags=re.MULTILINE).group(1)
> +    patch = re.search(r'^#define BTRFS_UTIL_VERSION_PATCH ([0-9]+)$',
> +                      btrfsutil_h, flags=re.MULTILINE).group(1)
> +    return major + '.' + minor + '.' + patch
> +
> +
> +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'],
> +)
> +
> +setup(
> +    name='btrfsutil',
> +    version=get_version(),
> +    description='Library for managing Btrfs filesystems',
> +    url='https://github.com/kdave/btrfs-progs',
> +    license='GPLv3',
> +    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
> 


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

* Re: [PATCH v2 10/27] libbtrfsutil: add btrfs_util_[gs]et_default_subvolume()
  2018-02-15 19:04 ` [PATCH v2 10/27] libbtrfsutil: add btrfs_util_[gs]et_default_subvolume() Omar Sandoval
@ 2018-02-22  1:55   ` Misono, Tomohiro
  2018-02-23 22:40     ` Omar Sandoval
  0 siblings, 1 reply; 64+ messages in thread
From: Misono, Tomohiro @ 2018-02-22  1:55 UTC (permalink / raw)
  To: Omar Sandoval, linux-btrfs; +Cc: kernel-team

On 2018/02/16 4:04, Omar Sandoval wrote:
> 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                    |  41 ++++++++++
>  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                    | 113 ++++++++++++++++++++++++++++
>  6 files changed, 234 insertions(+)
> 
> diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
> index 8bd2b847..54777f1d 100644
> --- a/libbtrfsutil/btrfsutil.h
> +++ b/libbtrfsutil/btrfsutil.h
> @@ -256,6 +256,8 @@ enum btrfs_util_error btrfs_util_get_subvolume_read_only_fd(int fd, bool *ret);
>   * @path: Subvolume path.
>   * @read_only: New value of read-only flag.
>   *
> + * This requires appropriate privilege (CAP_SYS_ADMIN).
> + *
>   * 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,
> @@ -268,6 +270,45 @@ enum btrfs_util_error btrfs_util_set_subvolume_read_only(const char *path,
>  enum btrfs_util_error btrfs_util_set_subvolume_read_only_fd(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.
> + *
> + * This requires appropriate privilege (CAP_SYS_ADMIN).
> + *
> + * 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_get_default_subvolume_fd() - See
> + * btrfs_util_get_default_subvolume().
> + */
> +enum btrfs_util_error btrfs_util_get_default_subvolume_fd(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.

The line "This requires appropriate privilege (CAP_SYS_ADMIN)." is missing here.

> + *
> + * 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_set_default_subvolume_fd() - See
> + * btrfs_util_set_default_subvolume().
> + */
> +enum btrfs_util_error btrfs_util_set_default_subvolume_fd(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 76487865..fa3ec4a7 100644
> --- a/libbtrfsutil/python/subvolume.c
> +++ b/libbtrfsutil/python/subvolume.c
> @@ -270,6 +270,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_get_default_subvolume_fd(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_set_default_subvolume_fd(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 bc5b309e..b3f768ed 100644
> --- a/libbtrfsutil/subvolume.c
> +++ b/libbtrfsutil/subvolume.c
> @@ -467,6 +467,119 @@ PUBLIC enum btrfs_util_error btrfs_util_set_subvolume_read_only_fd(int fd,
>  	return BTRFS_UTIL_OK;
>  }
>  
> +PUBLIC 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_get_default_subvolume_fd(fd, id_ret);
> +	SAVE_ERRNO_AND_CLOSE(fd);
> +	return err;
> +}
> +
> +PUBLIC enum btrfs_util_error btrfs_util_get_default_subvolume_fd(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;
> +}
> +
> +PUBLIC 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_set_default_subvolume_fd(fd, id);
> +	SAVE_ERRNO_AND_CLOSE(fd);
> +	return err;
> +}
> +
> +PUBLIC enum btrfs_util_error btrfs_util_set_default_subvolume_fd(int fd,
> +								 uint64_t id)
> +{
> +	enum btrfs_util_error err;
> +	int ret;
> +
> +	if (id == 0) {
> +		err = btrfs_util_is_subvolume_fd(fd);
> +		if (err)
> +			return err;
> +
> +		err = btrfs_util_subvolume_id_fd(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)
> 


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

* Re: [PATCH v2 23/27] btrfs-progs: use libbtrfsutil for subvol sync
  2018-02-15 19:05 ` [PATCH v2 23/27] btrfs-progs: use libbtrfsutil for subvol sync Omar Sandoval
@ 2018-02-22  2:03   ` Misono, Tomohiro
  2018-02-23 22:41     ` Omar Sandoval
  2018-02-22  2:09   ` Misono, Tomohiro
  1 sibling, 1 reply; 64+ messages in thread
From: Misono, Tomohiro @ 2018-02-22  2:03 UTC (permalink / raw)
  To: Omar Sandoval, linux-btrfs; +Cc: kernel-team



On 2018/02/16 4:05, Omar Sandoval wrote:
> 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 49c9c8cf..9bab9312 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_subvolume_info_fd(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;
>  			}
> @@ -1028,160 +999,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;

"ids" should be initialized to prevent to be used uninitialized for free()
in error path (clang build warning).

> +	size_t id_count, i;
>  	int sleep_interval = 1;
> +	enum btrfs_util_error err;
>  
>  	while (1) {
>  		int c = getopt(argc, argv, "s:");
> @@ -1215,10 +1041,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_deleted_subvolumes_fd(fd, &ids, &id_count);
> +		if (err) {
> +			error_btrfs_util(err);
>  			ret = 1;
>  			goto out;
>  		}
> @@ -1227,7 +1052,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;
> @@ -1241,13 +1066,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;
> 


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

* Re: [PATCH v2 23/27] btrfs-progs: use libbtrfsutil for subvol sync
  2018-02-15 19:05 ` [PATCH v2 23/27] btrfs-progs: use libbtrfsutil for subvol sync Omar Sandoval
  2018-02-22  2:03   ` Misono, Tomohiro
@ 2018-02-22  2:09   ` Misono, Tomohiro
  1 sibling, 0 replies; 64+ messages in thread
From: Misono, Tomohiro @ 2018-02-22  2:09 UTC (permalink / raw)
  To: Omar Sandoval, linux-btrfs; +Cc: kernel-team

On 2018/02/16 4:05, Omar Sandoval wrote:
> 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().

And, the function names are older version.

> 
> 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 49c9c8cf..9bab9312 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_subvolume_info_fd(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;
>  			}
> @@ -1028,160 +999,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:");
> @@ -1215,10 +1041,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_deleted_subvolumes_fd(fd, &ids, &id_count);
> +		if (err) {
> +			error_btrfs_util(err);
>  			ret = 1;
>  			goto out;
>  		}
> @@ -1227,7 +1052,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;
> @@ -1241,13 +1066,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;
> 


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

* Re: [PATCH v2 16/27] btrfs-progs: use libbtrfsutil for read-only property
  2018-02-15 19:05 ` [PATCH v2 16/27] btrfs-progs: use libbtrfsutil for read-only property Omar Sandoval
@ 2018-02-22  4:23   ` Misono, Tomohiro
  2018-02-23 22:41     ` Omar Sandoval
  0 siblings, 1 reply; 64+ messages in thread
From: Misono, Tomohiro @ 2018-02-22  4:23 UTC (permalink / raw)
  To: Omar Sandoval, linux-btrfs; +Cc: kernel-team



On 2018/02/16 4:05, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>
> 
> Signed-off-by: Omar Sandoval <osandov@fb.com>
> ---
>  messages.h | 13 ++++++++++++
>  props.c    | 69 +++++++++++++++++++++++---------------------------------------
>  2 files changed, 38 insertions(+), 44 deletions(-)
> 
> diff --git a/messages.h b/messages.h
> index 4999c7b9..004d5167 100644
> --- a/messages.h
> +++ b/messages.h
> @@ -54,6 +54,19 @@
>  			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)		\

"make D=trace" fails because ";" is missing here.

> +		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;					\
> 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,
> 


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

* Re: [PATCH v2 14/27] libbtrfsutil: add btrfs_util_deleted_subvolumes()
  2018-02-15 19:04 ` [PATCH v2 14/27] libbtrfsutil: add btrfs_util_deleted_subvolumes() Omar Sandoval
@ 2018-02-23  2:12   ` Misono, Tomohiro
  2018-02-23 23:33     ` Omar Sandoval
  0 siblings, 1 reply; 64+ messages in thread
From: Misono, Tomohiro @ 2018-02-23  2:12 UTC (permalink / raw)
  To: Omar Sandoval, linux-btrfs; +Cc: kernel-team


On 2018/02/16 4:04, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>
> 
> Signed-off-by: Omar Sandoval <osandov@fb.com>
> ---
>  libbtrfsutil/btrfsutil.h                    | 21 +++++++
>  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                    | 89 +++++++++++++++++++++++++++++
>  7 files changed, 183 insertions(+), 15 deletions(-)
> 
> diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
> index 00c86174..677ab3c1 100644
> --- a/libbtrfsutil/btrfsutil.h
> +++ b/libbtrfsutil/btrfsutil.h
> @@ -534,6 +534,27 @@ 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.
> + *
> + * This requires appropriate privilege (CAP_SYS_ADMIN).
> + *
> + * 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_deleted_subvolumes_fd() - See btrfs_util_deleted_subvolumes().
> + */
> +enum btrfs_util_error btrfs_util_deleted_subvolumes_fd(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 eb3f6e27..069e606b 100644
> --- a/libbtrfsutil/python/subvolume.c
> +++ b/libbtrfsutil/python/subvolume.c
> @@ -425,6 +425,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_deleted_subvolumes_fd(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 908e71db..4ae581b2 100644
> --- a/libbtrfsutil/subvolume.c
> +++ b/libbtrfsutil/subvolume.c
> @@ -1277,3 +1277,92 @@ PUBLIC enum btrfs_util_error btrfs_util_subvolume_iterator_next_info(struct btrf
>  
>  	return btrfs_util_subvolume_info_fd(iter->fd, id, subvol);
>  }
> +
> +PUBLIC 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_deleted_subvolumes_fd(fd, ids, n);
> +	SAVE_ERRNO_AND_CLOSE(fd);
> +	return err;
> +}
> +
> +PUBLIC enum btrfs_util_error btrfs_util_deleted_subvolumes_fd(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,
> +		},
> +	};
It seems btrfs_util_deleted_subvolume() does not works perfectly.

Above search will return all ORPHAN_ITEM but some may be of free space cache inode (correct me if I'm wrong).
Since this is a public function, we should filter those non-subvolume objectid by checking root tree again.
I think this is the reason of failure of misc-test/013 after merging 26th patch.

> +	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;
> +}
> 


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

* Re: [PATCH v2 26/27] btrfs-progs: use libbtrfsutil for subvolume list
  2018-02-15 19:05 ` [PATCH v2 26/27] btrfs-progs: use libbtrfsutil for subvolume list Omar Sandoval
@ 2018-02-23  2:26   ` Misono, Tomohiro
  2018-02-23 23:05     ` Omar Sandoval
  0 siblings, 1 reply; 64+ messages in thread
From: Misono, Tomohiro @ 2018-02-23  2:26 UTC (permalink / raw)
  To: Omar Sandoval, linux-btrfs; +Cc: kernel-team



On 2018/02/16 4:05, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>

> +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_deleted_subvolumes_fd(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_subvolume_info_fd(fd, ids[i], &subvol->info);
> +		if (err) {

I think there is a small chance that subvolume would be removed from tree between 
btrfs_util_deleted_subvolumes_fd() and btrfs_util_subvolume_info_fd().
So, error of BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND should be ignored.

> +			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) {
> +		free_subvol_list(subvols);
> +		subvols = NULL;
> +		free(ids);
> +	}
> +	return subvols;
> +}


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

* Re: [PATCH v2 07/27] libbtrfsutil: add btrfs_util_subvolume_path()
  2018-02-15 19:04 ` [PATCH v2 07/27] libbtrfsutil: add btrfs_util_subvolume_path() Omar Sandoval
@ 2018-02-23  6:27   ` Misono, Tomohiro
  2018-02-23 22:44     ` Omar Sandoval
  0 siblings, 1 reply; 64+ messages in thread
From: Misono, Tomohiro @ 2018-02-23  6:27 UTC (permalink / raw)
  To: Omar Sandoval, linux-btrfs; +Cc: kernel-team

On 2018/02/16 4:04, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>

> +PUBLIC enum btrfs_util_error btrfs_util_subvolume_path_fd(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_is_subvolume_fd(fd);
> +		if (err)
> +			return err;

'path' should be freed here and below.

> +
> +		err = btrfs_util_subvolume_id_fd(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)
> 


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

* Re: [PATCH v2 11/27] libbtrfsutil: add subvolume iterator helpers
  2018-02-15 19:04 ` [PATCH v2 11/27] libbtrfsutil: add subvolume iterator helpers Omar Sandoval
@ 2018-02-23  7:40   ` Misono, Tomohiro
  2018-02-23 22:49     ` Omar Sandoval
  0 siblings, 1 reply; 64+ messages in thread
From: Misono, Tomohiro @ 2018-02-23  7:40 UTC (permalink / raw)
  To: Omar Sandoval, linux-btrfs; +Cc: kernel-team

On 2018/02/16 4:04, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>

> +PUBLIC 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_create_subvolume_iterator_fd(fd, top, flags, ret);
> +	if (err == BTRFS_UTIL_OK)
> +		(*ret)->flags |= BTRFS_UTIL_SUBVOLUME_ITERATOR_CLOSE_FD;

If btrfs_util_create_subvolume_iterator_fd() returns error, 'fd' remains open.
So, fd should be closed here.

> +
> +	return err;
> +}


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

* Re: [PATCH v2 06/27] libbtrfsutil: add btrfs_util_create_subvolume()
  2018-02-15 19:04 ` [PATCH v2 06/27] libbtrfsutil: add btrfs_util_create_subvolume() Omar Sandoval
@ 2018-02-23  8:24   ` Misono, Tomohiro
  2018-02-23 22:58     ` Omar Sandoval
  0 siblings, 1 reply; 64+ messages in thread
From: Misono, Tomohiro @ 2018-02-23  8:24 UTC (permalink / raw)
  To: Omar Sandoval, linux-btrfs; +Cc: kernel-team

On 2018/02/16 4:04, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>

> +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;

tmp_path should be also freed here.

> +		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;
> +}



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

* Re: [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library"
  2018-02-21 18:50     ` Omar Sandoval
@ 2018-02-23 20:28       ` David Sterba
  2018-02-26 23:36         ` Omar Sandoval
  0 siblings, 1 reply; 64+ messages in thread
From: David Sterba @ 2018-02-23 20:28 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: dsterba, linux-btrfs, kernel-team

On Wed, Feb 21, 2018 at 10:50:32AM -0800, Omar Sandoval wrote:
> On Wed, Feb 21, 2018 at 04:13:38PM +0100, David Sterba wrote:
> > On Tue, Feb 20, 2018 at 07:50:48PM +0100, David Sterba wrote:
> > > I have more comments or maybe questions about the future development
> > > workflow, but at this point the patchset is in a good shape for
> > > incremental merge.
> > 
> > After removnig the first patch adding subvolume.c (with
> > linux/btrfs_tree.h) and what depends on it, I'm left with:
> > 
> > Omar Sandoval (4):
> >       Add libbtrfsutil
> >       libbtrfsutil: add Python bindings
> >       libbtrfsutil: add qgroup inheritance helpers
> >       libbtrfsutil: add filesystem sync helpers
> > 
> > with some context updates. That builds and passes the CI tests.
> 
> Great. Does the CI system run the Python tests yet?

Tested here https://travis-ci.org/kdave/btrfs-progs/jobs/345410536 ,
does not pass.


test_start_sync (test_filesystem.TestSubvolume) ... mkfs.btrfs: invalid option -- 'q'
usage: mkfs.btrfs [options] dev [ dev ... ]


Looks like it tries to use the system mkfs.btrfs that is old.

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

* Re: [PATCH v2 10/27] libbtrfsutil: add btrfs_util_[gs]et_default_subvolume()
  2018-02-22  1:55   ` Misono, Tomohiro
@ 2018-02-23 22:40     ` Omar Sandoval
  0 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-23 22:40 UTC (permalink / raw)
  To: Misono, Tomohiro; +Cc: linux-btrfs, kernel-team

On Thu, Feb 22, 2018 at 10:55:48AM +0900, Misono, Tomohiro wrote:
> On 2018/02/16 4:04, Omar Sandoval wrote:
> > 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                    |  41 ++++++++++
> >  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                    | 113 ++++++++++++++++++++++++++++
> >  6 files changed, 234 insertions(+)
> > 
> > diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
> > index 8bd2b847..54777f1d 100644
> > --- a/libbtrfsutil/btrfsutil.h
> > +++ b/libbtrfsutil/btrfsutil.h
> > @@ -256,6 +256,8 @@ enum btrfs_util_error btrfs_util_get_subvolume_read_only_fd(int fd, bool *ret);
> >   * @path: Subvolume path.
> >   * @read_only: New value of read-only flag.
> >   *
> > + * This requires appropriate privilege (CAP_SYS_ADMIN).
> > + *
> >   * 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,
> > @@ -268,6 +270,45 @@ enum btrfs_util_error btrfs_util_set_subvolume_read_only(const char *path,
> >  enum btrfs_util_error btrfs_util_set_subvolume_read_only_fd(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.
> > + *
> > + * This requires appropriate privilege (CAP_SYS_ADMIN).
> > + *
> > + * 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_get_default_subvolume_fd() - See
> > + * btrfs_util_get_default_subvolume().
> > + */
> > +enum btrfs_util_error btrfs_util_get_default_subvolume_fd(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.
> 
> The line "This requires appropriate privilege (CAP_SYS_ADMIN)." is missing here.

Good catch, thanks, fixed.

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

* Re: [PATCH v2 23/27] btrfs-progs: use libbtrfsutil for subvol sync
  2018-02-22  2:03   ` Misono, Tomohiro
@ 2018-02-23 22:41     ` Omar Sandoval
  2018-02-23 23:22       ` David Sterba
  0 siblings, 1 reply; 64+ messages in thread
From: Omar Sandoval @ 2018-02-23 22:41 UTC (permalink / raw)
  To: Misono, Tomohiro; +Cc: linux-btrfs, kernel-team

On Thu, Feb 22, 2018 at 11:03:05AM +0900, Misono, Tomohiro wrote:
> 
> 
> On 2018/02/16 4:05, Omar Sandoval wrote:
> > 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 49c9c8cf..9bab9312 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_subvolume_info_fd(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;
> >  			}
> > @@ -1028,160 +999,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;
> 
> "ids" should be initialized to prevent to be used uninitialized for free()
> in error path (clang build warning).

I'm disappointed that GCC didn't also warn here, but thanks, fixed.

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

* Re: [PATCH v2 16/27] btrfs-progs: use libbtrfsutil for read-only property
  2018-02-22  4:23   ` Misono, Tomohiro
@ 2018-02-23 22:41     ` Omar Sandoval
  0 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-23 22:41 UTC (permalink / raw)
  To: Misono, Tomohiro; +Cc: linux-btrfs, kernel-team

On Thu, Feb 22, 2018 at 01:23:45PM +0900, Misono, Tomohiro wrote:
> 
> 
> On 2018/02/16 4:05, Omar Sandoval wrote:
> > From: Omar Sandoval <osandov@fb.com>
> > 
> > Signed-off-by: Omar Sandoval <osandov@fb.com>
> > ---
> >  messages.h | 13 ++++++++++++
> >  props.c    | 69 +++++++++++++++++++++++---------------------------------------
> >  2 files changed, 38 insertions(+), 44 deletions(-)
> > 
> > diff --git a/messages.h b/messages.h
> > index 4999c7b9..004d5167 100644
> > --- a/messages.h
> > +++ b/messages.h
> > @@ -54,6 +54,19 @@
> >  			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)		\
> 
> "make D=trace" fails because ";" is missing here.

That's embarrassing, thanks!

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

* Re: [PATCH v2 07/27] libbtrfsutil: add btrfs_util_subvolume_path()
  2018-02-23  6:27   ` Misono, Tomohiro
@ 2018-02-23 22:44     ` Omar Sandoval
  0 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-23 22:44 UTC (permalink / raw)
  To: Misono, Tomohiro; +Cc: linux-btrfs, kernel-team

On Fri, Feb 23, 2018 at 03:27:52PM +0900, Misono, Tomohiro wrote:
> On 2018/02/16 4:04, Omar Sandoval wrote:
> > From: Omar Sandoval <osandov@fb.com>
> 
> > +PUBLIC enum btrfs_util_error btrfs_util_subvolume_path_fd(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_is_subvolume_fd(fd);
> > +		if (err)
> > +			return err;
> 
> 'path' should be freed here and below.
> 
> > +
> > +		err = btrfs_util_subvolume_id_fd(fd, &id);
> > +		if (err)
> > +			return err;
> > +	}

Indeed, although I'll just change it to allocate path after this
instead.

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

* Re: [PATCH v2 11/27] libbtrfsutil: add subvolume iterator helpers
  2018-02-23  7:40   ` Misono, Tomohiro
@ 2018-02-23 22:49     ` Omar Sandoval
  0 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-23 22:49 UTC (permalink / raw)
  To: Misono, Tomohiro; +Cc: linux-btrfs, kernel-team

On Fri, Feb 23, 2018 at 04:40:45PM +0900, Misono, Tomohiro wrote:
> On 2018/02/16 4:04, Omar Sandoval wrote:
> > From: Omar Sandoval <osandov@fb.com>
> 
> > +PUBLIC 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_create_subvolume_iterator_fd(fd, top, flags, ret);
> > +	if (err == BTRFS_UTIL_OK)
> > +		(*ret)->flags |= BTRFS_UTIL_SUBVOLUME_ITERATOR_CLOSE_FD;
> 
> If btrfs_util_create_subvolume_iterator_fd() returns error, 'fd' remains open.
> So, fd should be closed here.

Good catch, fixed.

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

* Re: [PATCH v2 06/27] libbtrfsutil: add btrfs_util_create_subvolume()
  2018-02-23  8:24   ` Misono, Tomohiro
@ 2018-02-23 22:58     ` Omar Sandoval
  0 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-23 22:58 UTC (permalink / raw)
  To: Misono, Tomohiro; +Cc: linux-btrfs, kernel-team

On Fri, Feb 23, 2018 at 05:24:04PM +0900, Misono, Tomohiro wrote:
> On 2018/02/16 4:04, Omar Sandoval wrote:
> > From: Omar Sandoval <osandov@fb.com>
> 
> > +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;
> 
> tmp_path should be also freed here.

Another good catch, thanks.

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

* Re: [PATCH v2 26/27] btrfs-progs: use libbtrfsutil for subvolume list
  2018-02-23  2:26   ` Misono, Tomohiro
@ 2018-02-23 23:05     ` Omar Sandoval
  0 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-23 23:05 UTC (permalink / raw)
  To: Misono, Tomohiro; +Cc: linux-btrfs, kernel-team

On Fri, Feb 23, 2018 at 11:26:35AM +0900, Misono, Tomohiro wrote:
> 
> 
> On 2018/02/16 4:05, Omar Sandoval wrote:
> > From: Omar Sandoval <osandov@fb.com>
> 
> > +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_deleted_subvolumes_fd(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_subvolume_info_fd(fd, ids[i], &subvol->info);
> > +		if (err) {
> 
> I think there is a small chance that subvolume would be removed from tree between 
> btrfs_util_deleted_subvolumes_fd() and btrfs_util_subvolume_info_fd().
> So, error of BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND should be ignored.

Thanks, since this patch isn't in the devel branch in, I'll fold the fix
in.

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

* Re: [PATCH v2 23/27] btrfs-progs: use libbtrfsutil for subvol sync
  2018-02-23 22:41     ` Omar Sandoval
@ 2018-02-23 23:22       ` David Sterba
  0 siblings, 0 replies; 64+ messages in thread
From: David Sterba @ 2018-02-23 23:22 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: Misono, Tomohiro, linux-btrfs, kernel-team

On Fri, Feb 23, 2018 at 02:41:27PM -0800, Omar Sandoval wrote:
> > >  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;
> > 
> > "ids" should be initialized to prevent to be used uninitialized for free()
> > in error path (clang build warning).
> 
> I'm disappointed that GCC didn't also warn here, but thanks, fixed.

It's not reported even with the make W=3 level, gcc version 7.3.0.

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

* Re: [PATCH v2 14/27] libbtrfsutil: add btrfs_util_deleted_subvolumes()
  2018-02-23  2:12   ` Misono, Tomohiro
@ 2018-02-23 23:33     ` Omar Sandoval
  2018-02-28  4:11       ` Misono, Tomohiro
  0 siblings, 1 reply; 64+ messages in thread
From: Omar Sandoval @ 2018-02-23 23:33 UTC (permalink / raw)
  To: Misono, Tomohiro; +Cc: linux-btrfs, kernel-team

On Fri, Feb 23, 2018 at 11:12:56AM +0900, Misono, Tomohiro wrote:
> 
> On 2018/02/16 4:04, Omar Sandoval wrote:
> > From: Omar Sandoval <osandov@fb.com>
> > 
> > Signed-off-by: Omar Sandoval <osandov@fb.com>
> > ---
> >  libbtrfsutil/btrfsutil.h                    | 21 +++++++
> >  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                    | 89 +++++++++++++++++++++++++++++
> >  7 files changed, 183 insertions(+), 15 deletions(-)
> > 
> > diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
> > index 00c86174..677ab3c1 100644
> > --- a/libbtrfsutil/btrfsutil.h
> > +++ b/libbtrfsutil/btrfsutil.h
> > @@ -534,6 +534,27 @@ 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.
> > + *
> > + * This requires appropriate privilege (CAP_SYS_ADMIN).
> > + *
> > + * 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_deleted_subvolumes_fd() - See btrfs_util_deleted_subvolumes().
> > + */
> > +enum btrfs_util_error btrfs_util_deleted_subvolumes_fd(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 eb3f6e27..069e606b 100644
> > --- a/libbtrfsutil/python/subvolume.c
> > +++ b/libbtrfsutil/python/subvolume.c
> > @@ -425,6 +425,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_deleted_subvolumes_fd(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 908e71db..4ae581b2 100644
> > --- a/libbtrfsutil/subvolume.c
> > +++ b/libbtrfsutil/subvolume.c
> > @@ -1277,3 +1277,92 @@ PUBLIC enum btrfs_util_error btrfs_util_subvolume_iterator_next_info(struct btrf
> >  
> >  	return btrfs_util_subvolume_info_fd(iter->fd, id, subvol);
> >  }
> > +
> > +PUBLIC 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_deleted_subvolumes_fd(fd, ids, n);
> > +	SAVE_ERRNO_AND_CLOSE(fd);
> > +	return err;
> > +}
> > +
> > +PUBLIC enum btrfs_util_error btrfs_util_deleted_subvolumes_fd(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,
> > +		},
> > +	};
> It seems btrfs_util_deleted_subvolume() does not works perfectly.
> 
> Above search will return all ORPHAN_ITEM but some may be of free space cache inode (correct me if I'm wrong).
> Since this is a public function, we should filter those non-subvolume objectid by checking root tree again.
> I think this is the reason of failure of misc-test/013 after merging 26th patch.

So looking at the code that this replaces, I don't see the difference:

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;
}

This function does the same exact search, as far as I can tell. What does the
test failure look like? I'll try to reproduce it on my side.

> > +	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;
> > +}
> > 
> 

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

* Re: [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library"
  2018-02-23 20:28       ` David Sterba
@ 2018-02-26 23:36         ` Omar Sandoval
  2018-02-27 15:04           ` David Sterba
  0 siblings, 1 reply; 64+ messages in thread
From: Omar Sandoval @ 2018-02-26 23:36 UTC (permalink / raw)
  To: dsterba, linux-btrfs, kernel-team

On Fri, Feb 23, 2018 at 09:28:42PM +0100, David Sterba wrote:
> On Wed, Feb 21, 2018 at 10:50:32AM -0800, Omar Sandoval wrote:
> > On Wed, Feb 21, 2018 at 04:13:38PM +0100, David Sterba wrote:
> > > On Tue, Feb 20, 2018 at 07:50:48PM +0100, David Sterba wrote:
> > > > I have more comments or maybe questions about the future development
> > > > workflow, but at this point the patchset is in a good shape for
> > > > incremental merge.
> > > 
> > > After removnig the first patch adding subvolume.c (with
> > > linux/btrfs_tree.h) and what depends on it, I'm left with:
> > > 
> > > Omar Sandoval (4):
> > >       Add libbtrfsutil
> > >       libbtrfsutil: add Python bindings
> > >       libbtrfsutil: add qgroup inheritance helpers
> > >       libbtrfsutil: add filesystem sync helpers
> > > 
> > > with some context updates. That builds and passes the CI tests.
> > 
> > Great. Does the CI system run the Python tests yet?
> 
> Tested here https://travis-ci.org/kdave/btrfs-progs/jobs/345410536 ,
> does not pass.
> 
> 
> test_start_sync (test_filesystem.TestSubvolume) ... mkfs.btrfs: invalid option -- 'q'
> usage: mkfs.btrfs [options] dev [ dev ... ]
> 
> 
> Looks like it tries to use the system mkfs.btrfs that is old.

Hm... according the documentation for the existing tests, the person
running the tests is expected to set PATH to contain the local binaries,
otherwise it'll use the system ones. Does the CI system not do that?

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

* Re: [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library"
  2018-02-26 23:36         ` Omar Sandoval
@ 2018-02-27 15:04           ` David Sterba
  2018-02-27 20:48             ` Omar Sandoval
  0 siblings, 1 reply; 64+ messages in thread
From: David Sterba @ 2018-02-27 15:04 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: dsterba, linux-btrfs, kernel-team

On Mon, Feb 26, 2018 at 03:36:41PM -0800, Omar Sandoval wrote:
> On Fri, Feb 23, 2018 at 09:28:42PM +0100, David Sterba wrote:
> > On Wed, Feb 21, 2018 at 10:50:32AM -0800, Omar Sandoval wrote:
> > > On Wed, Feb 21, 2018 at 04:13:38PM +0100, David Sterba wrote:
> > > > On Tue, Feb 20, 2018 at 07:50:48PM +0100, David Sterba wrote:
> > > > > I have more comments or maybe questions about the future development
> > > > > workflow, but at this point the patchset is in a good shape for
> > > > > incremental merge.
> > > > 
> > > > After removnig the first patch adding subvolume.c (with
> > > > linux/btrfs_tree.h) and what depends on it, I'm left with:
> > > > 
> > > > Omar Sandoval (4):
> > > >       Add libbtrfsutil
> > > >       libbtrfsutil: add Python bindings
> > > >       libbtrfsutil: add qgroup inheritance helpers
> > > >       libbtrfsutil: add filesystem sync helpers
> > > > 
> > > > with some context updates. That builds and passes the CI tests.
> > > 
> > > Great. Does the CI system run the Python tests yet?
> > 
> > Tested here https://travis-ci.org/kdave/btrfs-progs/jobs/345410536 ,
> > does not pass.
> > 
> > 
> > test_start_sync (test_filesystem.TestSubvolume) ... mkfs.btrfs: invalid option -- 'q'
> > usage: mkfs.btrfs [options] dev [ dev ... ]
> > 
> > 
> > Looks like it tries to use the system mkfs.btrfs that is old.
> 
> Hm... according the documentation for the existing tests, the person
> running the tests is expected to set PATH to contain the local binaries,

No, where is this written? The closest hit is in the 'Exported
testsuite' but otherwise all paths must be "$TOP/mkfs.btrfs". The
testsuite will detect where it is running and will set the TOP variable
accordingly, but this is transparent to the tests.

The python tests should probably build on the same exec path magic.

> otherwise it'll use the system ones. Does the CI system not do that?

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

* Re: [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library"
  2018-02-27 15:04           ` David Sterba
@ 2018-02-27 20:48             ` Omar Sandoval
  0 siblings, 0 replies; 64+ messages in thread
From: Omar Sandoval @ 2018-02-27 20:48 UTC (permalink / raw)
  To: dsterba, linux-btrfs, kernel-team

On Tue, Feb 27, 2018 at 04:04:28PM +0100, David Sterba wrote:
> On Mon, Feb 26, 2018 at 03:36:41PM -0800, Omar Sandoval wrote:
> > On Fri, Feb 23, 2018 at 09:28:42PM +0100, David Sterba wrote:
> > > On Wed, Feb 21, 2018 at 10:50:32AM -0800, Omar Sandoval wrote:
> > > > On Wed, Feb 21, 2018 at 04:13:38PM +0100, David Sterba wrote:
> > > > > On Tue, Feb 20, 2018 at 07:50:48PM +0100, David Sterba wrote:
> > > > > > I have more comments or maybe questions about the future development
> > > > > > workflow, but at this point the patchset is in a good shape for
> > > > > > incremental merge.
> > > > > 
> > > > > After removnig the first patch adding subvolume.c (with
> > > > > linux/btrfs_tree.h) and what depends on it, I'm left with:
> > > > > 
> > > > > Omar Sandoval (4):
> > > > >       Add libbtrfsutil
> > > > >       libbtrfsutil: add Python bindings
> > > > >       libbtrfsutil: add qgroup inheritance helpers
> > > > >       libbtrfsutil: add filesystem sync helpers
> > > > > 
> > > > > with some context updates. That builds and passes the CI tests.
> > > > 
> > > > Great. Does the CI system run the Python tests yet?
> > > 
> > > Tested here https://travis-ci.org/kdave/btrfs-progs/jobs/345410536 ,
> > > does not pass.
> > > 
> > > 
> > > test_start_sync (test_filesystem.TestSubvolume) ... mkfs.btrfs: invalid option -- 'q'
> > > usage: mkfs.btrfs [options] dev [ dev ... ]
> > > 
> > > 
> > > Looks like it tries to use the system mkfs.btrfs that is old.
> > 
> > Hm... according the documentation for the existing tests, the person
> > running the tests is expected to set PATH to contain the local binaries,
> 
> No, where is this written? The closest hit is in the 'Exported
> testsuite' but otherwise all paths must be "$TOP/mkfs.btrfs". The
> testsuite will detect where it is running and will set the TOP variable
> accordingly, but this is transparent to the tests.
> 
> The python tests should probably build on the same exec path magic.

Oops, I misunderstood from skimming the exported testsuite too quickly.
I'll update the Python tests to do something similar.

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

* Re: [PATCH v2 14/27] libbtrfsutil: add btrfs_util_deleted_subvolumes()
  2018-02-23 23:33     ` Omar Sandoval
@ 2018-02-28  4:11       ` Misono, Tomohiro
  0 siblings, 0 replies; 64+ messages in thread
From: Misono, Tomohiro @ 2018-02-28  4:11 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: linux-btrfs, kernel-team

On 2018/02/24 8:33, Omar Sandoval wrote:
> On Fri, Feb 23, 2018 at 11:12:56AM +0900, Misono, Tomohiro wrote:
>>
>> On 2018/02/16 4:04, Omar Sandoval wrote:
>>> From: Omar Sandoval <osandov@fb.com>
>>>
>>> Signed-off-by: Omar Sandoval <osandov@fb.com>
>>> ---
>>>  libbtrfsutil/btrfsutil.h                    | 21 +++++++
>>>  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                    | 89 +++++++++++++++++++++++++++++
>>>  7 files changed, 183 insertions(+), 15 deletions(-)
>>>
>>> diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
>>> index 00c86174..677ab3c1 100644
>>> --- a/libbtrfsutil/btrfsutil.h
>>> +++ b/libbtrfsutil/btrfsutil.h
>>> @@ -534,6 +534,27 @@ 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.
>>> + *
>>> + * This requires appropriate privilege (CAP_SYS_ADMIN).
>>> + *
>>> + * 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_deleted_subvolumes_fd() - See btrfs_util_deleted_subvolumes().
>>> + */
>>> +enum btrfs_util_error btrfs_util_deleted_subvolumes_fd(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 eb3f6e27..069e606b 100644
>>> --- a/libbtrfsutil/python/subvolume.c
>>> +++ b/libbtrfsutil/python/subvolume.c
>>> @@ -425,6 +425,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_deleted_subvolumes_fd(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 908e71db..4ae581b2 100644
>>> --- a/libbtrfsutil/subvolume.c
>>> +++ b/libbtrfsutil/subvolume.c
>>> @@ -1277,3 +1277,92 @@ PUBLIC enum btrfs_util_error btrfs_util_subvolume_iterator_next_info(struct btrf
>>>  
>>>  	return btrfs_util_subvolume_info_fd(iter->fd, id, subvol);
>>>  }
>>> +
>>> +PUBLIC 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_deleted_subvolumes_fd(fd, ids, n);
>>> +	SAVE_ERRNO_AND_CLOSE(fd);
>>> +	return err;
>>> +}
>>> +
>>> +PUBLIC enum btrfs_util_error btrfs_util_deleted_subvolumes_fd(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,
>>> +		},
>>> +	};
>> It seems btrfs_util_deleted_subvolume() does not works perfectly.
>>
>> Above search will return all ORPHAN_ITEM but some may be of free space cache inode (correct me if I'm wrong).
>> Since this is a public function, we should filter those non-subvolume objectid by checking root tree again.
>> I think this is the reason of failure of misc-test/013 after merging 26th patch.
> 
> So looking at the code that this replaces, I don't see the difference:
> 
> 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;
> }
> 
> This function does the same exact search, as far as I can tell. What does the
> test failure look like? I'll try to reproduce it on my side.
> 

Hello, sorry for late replay.

Actually this is not a problem for functionality of "sub list -d" or "sub sync",
because after this function is called we check root tree to see if ROOT_ITEM
for deleted subvolume still exists or not. If the returned id is not related subvolume,
there is no ROOT_ITEM at first.

Since your github branch (libbtrfs) is updated to skip BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND as I said
in 26th patch, there is no test failure anymore.

But if I run misc-test/013 (on 4.16-rc3) with following diff[1] on libbtrfs branch to show
which objectid returns BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND after btrfs_util_subvolume_info_fd(),
I can see the garbage id in tests/misc-tests-result.txt:
===
[snip]

 ID 291 gen 78 top level 5 path snap34
 ID 293 gen 78 top level 5 path snap35

[snip]
 
 Subvolume id 291 is gone
 Subvolume id 293 is gone

[snip]

############### /home/misono/src/btrfs-progs/btrfs subvolum list -d .
 cleaned: 292  <- there was no subvolume with objectid 292

[snip]
===

I'm not sure where this number comes from, but isn't it better to filter them?

---
 cmds-subvolume.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 5fa390ff..b034bb77 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -1112,6 +1112,7 @@ static struct subvol_list *btrfs_list_deleted_subvols(int fd,
 			 * The subvolume might have been cleaned up since it was
 			 * returned.
 			 */
+			printf("cleaned: %ld\n", ids[i]);
 			continue;
 		} else if (err) {
 			error_btrfs_util(err);
---


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

end of thread, other threads:[~2018-02-28  4:12 UTC | newest]

Thread overview: 64+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-02-15 19:04 [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" Omar Sandoval
2018-02-15 19:04 ` [PATCH v2 01/27] btrfs-progs: get rid of undocumented qgroup inheritance options Omar Sandoval
2018-02-15 19:04 ` [PATCH v2 02/27] Add libbtrfsutil Omar Sandoval
2018-02-20 17:32   ` Liu Bo
2018-02-20 18:34     ` David Sterba
2018-02-15 19:04 ` [PATCH v2 03/27] libbtrfsutil: add Python bindings Omar Sandoval
2018-02-21 13:47   ` David Sterba
2018-02-21 18:08     ` Omar Sandoval
2018-02-22  1:44   ` Misono, Tomohiro
2018-02-15 19:04 ` [PATCH v2 04/27] libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id() Omar Sandoval
2018-02-21 11:43   ` David Sterba
2018-02-21 13:02     ` David Sterba
2018-02-21 18:13       ` Omar Sandoval
2018-02-15 19:04 ` [PATCH v2 05/27] libbtrfsutil: add qgroup inheritance helpers Omar Sandoval
2018-02-15 19:04 ` [PATCH v2 06/27] libbtrfsutil: add btrfs_util_create_subvolume() Omar Sandoval
2018-02-23  8:24   ` Misono, Tomohiro
2018-02-23 22:58     ` Omar Sandoval
2018-02-15 19:04 ` [PATCH v2 07/27] libbtrfsutil: add btrfs_util_subvolume_path() Omar Sandoval
2018-02-23  6:27   ` Misono, Tomohiro
2018-02-23 22:44     ` Omar Sandoval
2018-02-15 19:04 ` [PATCH v2 08/27] libbtrfsutil: add btrfs_util_subvolume_info() Omar Sandoval
2018-02-15 19:04 ` [PATCH v2 09/27] libbtrfsutil: add btrfs_util_[gs]et_read_only() Omar Sandoval
2018-02-15 19:04 ` [PATCH v2 10/27] libbtrfsutil: add btrfs_util_[gs]et_default_subvolume() Omar Sandoval
2018-02-22  1:55   ` Misono, Tomohiro
2018-02-23 22:40     ` Omar Sandoval
2018-02-15 19:04 ` [PATCH v2 11/27] libbtrfsutil: add subvolume iterator helpers Omar Sandoval
2018-02-23  7:40   ` Misono, Tomohiro
2018-02-23 22:49     ` Omar Sandoval
2018-02-15 19:04 ` [PATCH v2 12/27] libbtrfsutil: add btrfs_util_create_snapshot() Omar Sandoval
2018-02-15 19:04 ` [PATCH v2 13/27] libbtrfsutil: add btrfs_util_delete_subvolume() Omar Sandoval
2018-02-15 19:04 ` [PATCH v2 14/27] libbtrfsutil: add btrfs_util_deleted_subvolumes() Omar Sandoval
2018-02-23  2:12   ` Misono, Tomohiro
2018-02-23 23:33     ` Omar Sandoval
2018-02-28  4:11       ` Misono, Tomohiro
2018-02-15 19:05 ` [PATCH v2 15/27] libbtrfsutil: add filesystem sync helpers Omar Sandoval
2018-02-15 19:05 ` [PATCH v2 16/27] btrfs-progs: use libbtrfsutil for read-only property Omar Sandoval
2018-02-22  4:23   ` Misono, Tomohiro
2018-02-23 22:41     ` Omar Sandoval
2018-02-15 19:05 ` [PATCH v2 17/27] btrfs-progs: use libbtrfsutil for sync ioctls Omar Sandoval
2018-02-15 19:05 ` [PATCH v2 18/27] btrfs-progs: use libbtrfsutil for set-default Omar Sandoval
2018-02-15 19:05 ` [PATCH v2 19/27] btrfs-progs: use libbtrfsutil for get-default Omar Sandoval
2018-02-15 19:05 ` [PATCH v2 20/27] btrfs-progs: use libbtrfsutil for subvol create and snapshot Omar Sandoval
2018-02-15 19:05 ` [PATCH v2 21/27] btrfs-progs: use libbtrfsutil for subvol delete Omar Sandoval
2018-02-15 19:05 ` [PATCH v2 22/27] btrfs-progs: use libbtrfsutil for subvol show Omar Sandoval
2018-02-15 19:05 ` [PATCH v2 23/27] btrfs-progs: use libbtrfsutil for subvol sync Omar Sandoval
2018-02-22  2:03   ` Misono, Tomohiro
2018-02-23 22:41     ` Omar Sandoval
2018-02-23 23:22       ` David Sterba
2018-02-22  2:09   ` Misono, Tomohiro
2018-02-15 19:05 ` [PATCH v2 24/27] btrfs-progs: replace test_issubvolume() with btrfs_util_is_subvolume() Omar Sandoval
2018-02-15 19:05 ` [PATCH v2 25/27] btrfs-progs: add recursive snapshot/delete using libbtrfsutil Omar Sandoval
2018-02-15 19:05 ` [PATCH v2 26/27] btrfs-progs: use libbtrfsutil for subvolume list Omar Sandoval
2018-02-23  2:26   ` Misono, Tomohiro
2018-02-23 23:05     ` Omar Sandoval
2018-02-15 19:05 ` [PATCH v2 27/27] btrfs-progs: deprecate libbtrfs helpers Omar Sandoval
2018-02-21 15:04   ` David Sterba
2018-02-21 18:19     ` Omar Sandoval
2018-02-20 18:50 ` [PATCH v2 00/27] btrfs-progs: introduce libbtrfsutil, "btrfs-progs as a library" David Sterba
2018-02-21 15:13   ` David Sterba
2018-02-21 18:50     ` Omar Sandoval
2018-02-23 20:28       ` David Sterba
2018-02-26 23:36         ` Omar Sandoval
2018-02-27 15:04           ` David Sterba
2018-02-27 20:48             ` Omar Sandoval

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.