From 3d5c0c203c3d13083e6bf27d2048b1e20c8f6112 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Fri, 30 Apr 2021 02:19:04 +0200 Subject: [PATCH] libbtrfsutil: add get and set filesystem label Signed-off-by: David Sterba --- libbtrfsutil.sym | 8 +++ libbtrfsutil/btrfsutil.h | 35 +++++++++++ libbtrfsutil/errors.c | 4 ++ libbtrfsutil/filesystem.c | 63 ++++++++++++++++++++ libbtrfsutil/python/btrfsutilpy.h | 2 + libbtrfsutil/python/filesystem.c | 50 ++++++++++++++++ libbtrfsutil/python/module.c | 6 ++ libbtrfsutil/python/tests/__init__.py | 2 +- libbtrfsutil/python/tests/test_filesystem.py | 6 ++ 9 files changed, 175 insertions(+), 1 deletion(-) diff --git a/libbtrfsutil.sym b/libbtrfsutil.sym index e1ab7152..8539c3e5 100644 --- a/libbtrfsutil.sym +++ b/libbtrfsutil.sym @@ -50,3 +50,11 @@ LIBBTRFSUTIL_1.2 { global: btrfs_util_delete_subvolume_by_id_fd; } LIBBTRFSUTIL_1.1; + +LIBBTRFSUTIL_1.3 { +global: + btrfs_util_filesystem_get_label; + btrfs_util_filesystem_get_label_fd; + btrfs_util_filesystem_set_label; + btrfs_util_filesystem_set_label_fd; +} LIBBTRFSUTIL_1.2; diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h index ddd2a7b2..1b860911 100644 --- a/libbtrfsutil/btrfsutil.h +++ b/libbtrfsutil/btrfsutil.h @@ -67,6 +67,8 @@ enum btrfs_util_error { BTRFS_UTIL_ERROR_GET_SUBVOL_ROOTREF_FAILED, BTRFS_UTIL_ERROR_INO_LOOKUP_USER_FAILED, BTRFS_UTIL_ERROR_FS_INFO_FAILED, + BTRFS_UTIL_ERROR_GET_LABEL_FAILED, + BTRFS_UTIL_ERROR_SET_LABEL_FAILED, }; /** @@ -122,6 +124,39 @@ enum btrfs_util_error btrfs_util_wait_sync(const char *path, uint64_t transid); */ enum btrfs_util_error btrfs_util_wait_sync_fd(int fd, uint64_t transid); +#define BTRFS_UTIL_LABEL_SIZE 256 + +/** + * btrfs_util_filesystem_set_label() - Set label of the filesystem under path + * @path: Path on a Btrfs filesystem. + * @label: New label as string, maximum length must be less than %BTRFS_LABEL_SIZE. + * + * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure. + */ +enum btrfs_util_error btrfs_util_filesystem_set_label(const char *path, + const char *label); + +/** + * btrfs_util_filesystem_get_label_fd() - See btrfs_util_filesystem_get_label(). + */ +enum btrfs_util_error btrfs_util_filesystem_set_label_fd(int fd, + const char *label); + +/** + * btrfs_util_filesystem_get_label - Get labelf of the filesystem under path + * @path: Path on a Btrfs filesystem. + * @label: Buffer where to store the label, must be at least %BTRFS_LABEL_SIZE. + * + * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure. + */ +enum btrfs_util_error btrfs_util_filesystem_get_label(const char *path, + char *label); + +/** + * btrfs_util_filesystem_get_label_fd() - See btrfs_util_filesystem_get_label(). + */ +enum btrfs_util_error btrfs_util_filesystem_get_label_fd(int fd, + char *label); /** * btrfs_util_is_subvolume() - Return whether a given path is a Btrfs subvolume. * @path: Path to check. diff --git a/libbtrfsutil/errors.c b/libbtrfsutil/errors.c index 989096e2..61c94d70 100644 --- a/libbtrfsutil/errors.c +++ b/libbtrfsutil/errors.c @@ -53,6 +53,10 @@ static const char * const error_messages[] = { "Could not resolve subvolume path with BTRFS_IOC_INO_LOOKUP_USER", [BTRFS_UTIL_ERROR_FS_INFO_FAILED] = "Could not get filesystem information", + [BTRFS_UTIL_ERROR_GET_LABEL_FAILED] = + "Could not get filesystem label", + [BTRFS_UTIL_ERROR_SET_LABEL_FAILED] = + "Could not set filesystem label", }; PUBLIC const char *btrfs_util_strerror(enum btrfs_util_error err) diff --git a/libbtrfsutil/filesystem.c b/libbtrfsutil/filesystem.c index 07fa9c5f..8499bc07 100644 --- a/libbtrfsutil/filesystem.c +++ b/libbtrfsutil/filesystem.c @@ -100,3 +100,66 @@ PUBLIC enum btrfs_util_error btrfs_util_wait_sync_fd(int fd, uint64_t transid) return BTRFS_UTIL_OK; } + +PUBLIC enum btrfs_util_error btrfs_util_filesystem_set_label_fd(int fd, + const char *label) +{ + int ret; + char buf[BTRFS_UTIL_LABEL_SIZE]; + + if (strlen(label) >= BTRFS_UTIL_LABEL_SIZE) { + errno = EINVAL; + return BTRFS_UTIL_ERROR_INVALID_ARGUMENT; + } + memset(buf, 0, sizeof(buf)); + strncpy(buf, label, BTRFS_UTIL_LABEL_SIZE - 1); + + ret = ioctl(fd, BTRFS_IOC_SET_FSLABEL, buf); + if (ret == -1) + return BTRFS_UTIL_ERROR_GET_LABEL_FAILED; + + return BTRFS_UTIL_OK; +} + +PUBLIC enum btrfs_util_error btrfs_util_filesystem_set_label(const char *path, + const char *label) +{ + enum btrfs_util_error err; + int fd; + + fd = open(path, O_RDONLY); + if (fd == -1) + return BTRFS_UTIL_ERROR_OPEN_FAILED; + + err = btrfs_util_filesystem_set_label_fd(fd, label); + SAVE_ERRNO_AND_CLOSE(fd); + return err; +} + +PUBLIC enum btrfs_util_error btrfs_util_filesystem_get_label_fd(int fd, + char *label) +{ + int ret; + + /* Kernel does not return more than BTRFS_LABEL_SIZE/BTRFS_UTIL_LABEL_SIZE */ + ret = ioctl(fd, BTRFS_IOC_GET_FSLABEL, label); + if (ret == -1) + return BTRFS_UTIL_ERROR_GET_LABEL_FAILED; + + return BTRFS_UTIL_OK; +} + +PUBLIC enum btrfs_util_error btrfs_util_filesystem_get_label(const char *path, + char *label) +{ + enum btrfs_util_error err; + int fd; + + fd = open(path, O_RDONLY); + if (fd == -1) + return BTRFS_UTIL_ERROR_OPEN_FAILED; + + err = btrfs_util_filesystem_get_label_fd(fd, label); + SAVE_ERRNO_AND_CLOSE(fd); + return err; +} diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h index ee70c23a..9f248514 100644 --- a/libbtrfsutil/python/btrfsutilpy.h +++ b/libbtrfsutil/python/btrfsutilpy.h @@ -66,6 +66,8 @@ void SetFromBtrfsUtilErrorWithPaths(enum btrfs_util_error err, 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 *filesystem_get_label(PyObject *self, PyObject *args, PyObject *kwds); +PyObject *fielsystem_set_label(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 index 5177c75d..f4211329 100644 --- a/libbtrfsutil/python/filesystem.c +++ b/libbtrfsutil/python/filesystem.c @@ -92,3 +92,53 @@ PyObject *wait_sync(PyObject *self, PyObject *args, PyObject *kwds) path_cleanup(&path); Py_RETURN_NONE; } + +PyObject *filesystem_get_label(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *keywords[] = {"path", NULL}; + struct path_arg path = {.allow_fd = true}; + enum btrfs_util_error err; + char label[BTRFS_UTIL_LABEL_SIZE]; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:filesystem_get_label", + keywords, &path_converter, &path)) + return NULL; + + if (path.path) + err = btrfs_util_filesystem_get_label(path.path, label); + else + err = btrfs_util_filesystem_get_label_fd(path.fd, label); + if (err) { + SetFromBtrfsUtilErrorWithPath(err, &path); + path_cleanup(&path); + return NULL; + } + + path_cleanup(&path); + return PyUnicode_DecodeFSDefault(label); +} + +PyObject *filesystem_set_label(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *keywords[] = {"path", "label", NULL}; + struct path_arg path = {.allow_fd = true}; + enum btrfs_util_error err; + char *label; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&s:filesystem_set_label", + keywords, &path_converter, &path, &label)) + return NULL; + + if (path.path) + err = btrfs_util_filesystem_set_label(path.path, label); + else + err = btrfs_util_filesystem_set_label_fd(path.fd, label); + 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 729f76db..903334cc 100644 --- a/libbtrfsutil/python/module.c +++ b/libbtrfsutil/python/module.c @@ -176,6 +176,12 @@ static PyMethodDef btrfsutil_methods[] = { "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"}, + {"filesystem_get_label", (PyCFunction)filesystem_get_label, + METH_VARARGS | METH_KEYWORDS, + "filesystem_get_label(path) -> str\n\n" + "Get filesystem label.\n\n" + "Arguments:\n" + "path -- string, bytes, path-like object, or open file descriptor"}, {"is_subvolume", (PyCFunction)is_subvolume, METH_VARARGS | METH_KEYWORDS, "is_subvolume(path) -> bool\n\n" diff --git a/libbtrfsutil/python/tests/__init__.py b/libbtrfsutil/python/tests/__init__.py index 9fd6f6de..4e93c73c 100644 --- a/libbtrfsutil/python/tests/__init__.py +++ b/libbtrfsutil/python/tests/__init__.py @@ -76,7 +76,7 @@ class BtrfsTestCase(unittest.TestCase): else: mkfs = 'mkfs.btrfs' try: - subprocess.check_call([mkfs, '-q', image]) + subprocess.check_call([mkfs, '-q', '-L', 'BTRFS-PROGS-PYTEST', image]) subprocess.check_call(['mount', '-o', 'loop', '--', image, mountpoint]) except Exception as e: os.rmdir(mountpoint) diff --git a/libbtrfsutil/python/tests/test_filesystem.py b/libbtrfsutil/python/tests/test_filesystem.py index 006a6b1e..1a03b8ca 100644 --- a/libbtrfsutil/python/tests/test_filesystem.py +++ b/libbtrfsutil/python/tests/test_filesystem.py @@ -71,3 +71,9 @@ class TestSubvolume(BtrfsTestCase): new_generation = self.super_generation() self.assertGreater(new_generation, old_generation) old_generation = new_generation + + def test_filesystem_get_label(self): + for arg in self.path_or_fd(self.mountpoint): + with self.subTest(type=type(arg)): + label = btrfsutil.filesystem_get_label(arg) + self.assertEqual(label, 'BTRFS-PROGS-PYTEST')