summaryrefslogtreecommitdiff
path: root/src/libnm-systemd-shared/src/basic/btrfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-systemd-shared/src/basic/btrfs.c')
-rw-r--r--src/libnm-systemd-shared/src/basic/btrfs.c100
1 files changed, 100 insertions, 0 deletions
diff --git a/src/libnm-systemd-shared/src/basic/btrfs.c b/src/libnm-systemd-shared/src/basic/btrfs.c
new file mode 100644
index 0000000000..3b23607849
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/btrfs.c
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-shared.h"
+
+#include <linux/btrfs.h>
+#include <sys/ioctl.h>
+
+#include "btrfs.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "path-util.h"
+
+int btrfs_validate_subvolume_name(const char *name) {
+
+ if (!filename_is_valid(name))
+ return -EINVAL;
+
+ if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
+ return -E2BIG;
+
+ return 0;
+}
+
+static int extract_subvolume_name(const char *path, char **ret) {
+ _cleanup_free_ char *fn = NULL;
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ r = path_extract_filename(path, &fn);
+ if (r < 0)
+ return r;
+
+ r = btrfs_validate_subvolume_name(fn);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(fn);
+ return 0;
+}
+
+int btrfs_subvol_make(int dir_fd, const char *path) {
+ struct btrfs_ioctl_vol_args args = {};
+ _cleanup_free_ char *subvolume = NULL, *parent = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+ assert(!isempty(path));
+
+ r = extract_subvolume_name(path, &subvolume);
+ if (r < 0)
+ return r;
+
+ r = path_extract_directory(path, &parent);
+ if (r < 0) {
+ if (r != -EDESTADDRREQ) /* Propagate error, unless only a filename was specified, which is OK */
+ return r;
+
+ dir_fd = fd_reopen_condition(dir_fd, O_CLOEXEC, O_PATH, &fd); /* drop O_PATH if it is set */
+ if (dir_fd < 0)
+ return dir_fd;
+ } else {
+ fd = openat(dir_fd, parent, O_DIRECTORY|O_RDONLY|O_CLOEXEC, 0);
+ if (fd < 0)
+ return -errno;
+
+ dir_fd = fd;
+ }
+
+ strncpy(args.name, subvolume, sizeof(args.name)-1);
+
+ return RET_NERRNO(ioctl(dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args));
+}
+
+int btrfs_subvol_make_fallback(int dir_fd, const char *path, mode_t mode) {
+ mode_t old, combined;
+ int r;
+
+ assert(path);
+
+ /* Let's work like mkdir(), i.e. take the specified mode, and mask it with the current umask. */
+ old = umask(~mode);
+ combined = old | ~mode;
+ if (combined != ~mode)
+ umask(combined);
+ r = btrfs_subvol_make(dir_fd, path);
+ umask(old);
+
+ if (r >= 0)
+ return 1; /* subvol worked */
+ if (!ERRNO_IS_NOT_SUPPORTED(r))
+ return r;
+
+ if (mkdirat(dir_fd, path, mode) < 0)
+ return -errno;
+
+ return 0; /* plain directory */
+}