From 624e0233e09a034a0b8a6da445f725706ef2d9f8 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Thu, 18 Jan 2018 13:51:16 -0800 Subject: libbtrfsutil: add btrfs_util_[gs]et_default_subvolume() 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 Signed-off-by: David Sterba --- libbtrfsutil/btrfsutil.h | 43 +++++++++++ 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, 236 insertions(+) diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h index ab96f444..76ab06b6 100644 --- a/libbtrfsutil/btrfsutil.h +++ b/libbtrfsutil/btrfsutil.h @@ -300,6 +300,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, @@ -312,6 +314,47 @@ 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. + * + * 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_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 aa17080c..a56e41fc 100644 --- a/libbtrfsutil/python/btrfsutilpy.h +++ b/libbtrfsutil/python/btrfsutilpy.h @@ -69,6 +69,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 3173ff7c..91c4b9be 100644 --- a/libbtrfsutil/python/module.c +++ b/libbtrfsutil/python/module.c @@ -194,6 +194,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 a8b8a761..965376e3 100644 --- a/libbtrfsutil/subvolume.c +++ b/libbtrfsutil/subvolume.c @@ -465,6 +465,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) -- cgit v1.2.3