linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Omar Sandoval <osandov@osandov.com>
To: linux-btrfs@vger.kernel.org
Cc: kernel-team@fb.com
Subject: [PATCH v2 13/27] libbtrfsutil: add btrfs_util_delete_subvolume()
Date: Thu, 15 Feb 2018 11:04:58 -0800	[thread overview]
Message-ID: <0ff0bebb7d74e53a31a2867fb3d4c2a1c8317892.1518720598.git.osandov@fb.com> (raw)
In-Reply-To: <cover.1518720598.git.osandov@fb.com>
In-Reply-To: <cover.1518720598.git.osandov@fb.com>

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


  parent reply	other threads:[~2018-02-15 19:05 UTC|newest]

Thread overview: 64+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 ` Omar Sandoval [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=0ff0bebb7d74e53a31a2867fb3d4c2a1c8317892.1518720598.git.osandov@fb.com \
    --to=osandov@osandov.com \
    --cc=kernel-team@fb.com \
    --cc=linux-btrfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).