diff --git a/man/repart.d.xml b/man/repart.d.xml
index a79724a93e..261c673249 100644
--- a/man/repart.d.xml
+++ b/man/repart.d.xml
@@ -500,6 +500,21 @@
attributes.
+
+ Subvolumes=
+
+ Takes one or more absolute paths, separated by whitespace, each declaring a directory
+ that should be a subvolume within the new file system. This option may be used more than once to
+ specify multiple directories. Note that this setting does not create the directories themselves, that
+ can be configured with MakeDirectories= and CopyFiles=.
+
+ Note that this option only takes effect if the target filesystem supports subvolumes, such as
+ btrfs.
+
+ Note that due to limitations of mkfs.btrfs, this option is only supported
+ when running with .
+
+
Encrypt=
diff --git a/src/basic/btrfs.c b/src/basic/btrfs.c
new file mode 100644
index 0000000000..a13fec4873
--- /dev/null
+++ b/src/basic/btrfs.c
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include
+#include
+
+#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;
+ _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, NULL);
+ if (r >= 0) {
+ fd = open_parent_at(dir_fd, path, O_RDONLY|O_CLOEXEC|O_CLOEXEC, 0);
+ if (fd < 0)
+ return fd;
+
+ 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 (r != -ENOTTY)
+ return r;
+
+ if (mkdirat(dir_fd, path, mode) < 0)
+ return -errno;
+
+ return 0; /* plain directory */
+}
diff --git a/src/basic/btrfs.h b/src/basic/btrfs.h
new file mode 100644
index 0000000000..38be9d2b3b
--- /dev/null
+++ b/src/basic/btrfs.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include
+
+int btrfs_validate_subvolume_name(const char *name);
+
+int btrfs_subvol_make(int dir_fd, const char *path);
+
+int btrfs_subvol_make_fallback(int dir_fd, const char *path, mode_t mode);
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 804440ef2a..a9336f1a67 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -9,6 +9,7 @@
#include
#include "alloc-util.h"
+#include "btrfs.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
@@ -1110,7 +1111,10 @@ int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags
}
if (FLAGS_SET(open_flags, O_DIRECTORY|O_CREAT)) {
- r = RET_NERRNO(mkdirat(dir_fd, path, mode));
+ if (FLAGS_SET(xopen_flags, XO_SUBVOLUME))
+ r = btrfs_subvol_make_fallback(dir_fd, path, mode);
+ else
+ r = RET_NERRNO(mkdirat(dir_fd, path, mode));
if (r == -EEXIST) {
if (FLAGS_SET(open_flags, O_EXCL))
return -EEXIST;
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index a19836d138..1023ab73ca 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -133,7 +133,8 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode);
int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created);
typedef enum XOpenFlags {
- XO_LABEL = 1 << 0,
+ XO_LABEL = 1 << 0,
+ XO_SUBVOLUME = 1 << 1,
} XOpenFlags;
int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode);
diff --git a/src/basic/meson.build b/src/basic/meson.build
index f6e6b6ace5..77ce2cf262 100644
--- a/src/basic/meson.build
+++ b/src/basic/meson.build
@@ -8,6 +8,7 @@ basic_sources = files(
'argv-util.c',
'arphrd-util.c',
'audit-util.c',
+ 'btrfs.c',
'build.c',
'bus-label.c',
'cap-list.c',
diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c
index 41af1482bc..c770e5ed32 100644
--- a/src/basic/mkdir.c
+++ b/src/basic/mkdir.c
@@ -5,6 +5,7 @@
#include
#include "alloc-util.h"
+#include "btrfs.h"
#include "chase.h"
#include "fd-util.h"
#include "format-util.h"
@@ -209,7 +210,7 @@ int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, g
return mkdir_p_internal(prefix, path, mode, uid, gid, flags, mkdirat_errno_wrapper);
}
-int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m) {
+int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, char **subvolumes) {
_cleanup_free_ char *pp = NULL, *bn = NULL;
_cleanup_close_ int dfd = -EBADF;
int r;
@@ -227,7 +228,7 @@ int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m
return r;
else {
/* Extracting the parent dir worked, hence we aren't top-level? Recurse up first. */
- r = mkdir_p_root(root, pp, uid, gid, m);
+ r = mkdir_p_root(root, pp, uid, gid, m, subvolumes);
if (r < 0)
return r;
@@ -242,11 +243,15 @@ int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m
if (r < 0)
return r;
- if (mkdirat(dfd, bn, m) < 0) {
- if (errno == EEXIST)
+ if (path_strv_contains(subvolumes, p))
+ r = btrfs_subvol_make_fallback(dfd, bn, m);
+ else
+ r = RET_NERRNO(mkdirat(dfd, bn, m));
+ if (r < 0) {
+ if (r == -EEXIST)
return 0;
- return -errno;
+ return r;
}
if (uid_is_valid(uid) || gid_is_valid(gid)) {
diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h
index 117d9e8061..e5387483e2 100644
--- a/src/basic/mkdir.h
+++ b/src/basic/mkdir.h
@@ -23,7 +23,7 @@ static inline int mkdir_parents(const char *path, mode_t mode) {
int mkdir_parents_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags);
int mkdir_p(const char *path, mode_t mode);
int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags);
-int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m);
+int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, char **subvolumes);
/* The following are used to implement the mkdir_xyz_label() calls, don't use otherwise. */
typedef int (*mkdirat_func_t)(int dir_fd, const char *pathname, mode_t mode);
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index f832f85c68..a4c9d332f0 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -1116,7 +1116,9 @@ int path_extract_directory(const char *path, char **ret) {
if (!path_is_valid(a))
return -EINVAL;
- *ret = TAKE_PTR(a);
+ if (ret)
+ *ret = TAKE_PTR(a);
+
return 0;
}
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c
index 4b830bf2a6..103ee41fe7 100644
--- a/src/dissect/dissect.c
+++ b/src/dissect/dissect.c
@@ -1414,9 +1414,9 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) {
if (errno != ENOENT)
return log_error_errno(errno, "Failed to open destination '%s': %m", arg_target);
- r = copy_tree_at(source_fd, ".", dfd, bn, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS, NULL);
+ r = copy_tree_at(source_fd, ".", dfd, bn, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS, NULL, NULL);
} else
- r = copy_tree_at(source_fd, ".", target_fd, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS, NULL);
+ r = copy_tree_at(source_fd, ".", target_fd, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to copy '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image);
diff --git a/src/home/homework-directory.c b/src/home/homework-directory.c
index 575ac52551..4ec5f3dde1 100644
--- a/src/home/homework-directory.c
+++ b/src/home/homework-directory.c
@@ -131,7 +131,7 @@ int home_create_directory_or_subvolume(UserRecord *h, HomeSetup *setup, UserReco
case USER_SUBVOLUME:
WITH_UMASK(0077)
- r = btrfs_subvol_make(d);
+ r = btrfs_subvol_make(AT_FDCWD, d);
if (r >= 0) {
log_info("Subvolume created.");
diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c
index e41920ce9c..23a29d5811 100644
--- a/src/home/homework-luks.c
+++ b/src/home/homework-luks.c
@@ -2383,7 +2383,7 @@ int home_create_luks(
return log_oom();
/* Prefer using a btrfs subvolume if we can, fall back to directory otherwise */
- r = btrfs_subvol_make_fallback(subdir, 0700);
+ r = btrfs_subvol_make_fallback(AT_FDCWD, subdir, 0700);
if (r < 0)
return log_error_errno(r, "Failed to create user directory in mounted image file: %m");
diff --git a/src/home/homework.c b/src/home/homework.c
index 9773bfa7a3..066483e342 100644
--- a/src/home/homework.c
+++ b/src/home/homework.c
@@ -1036,7 +1036,7 @@ static int copy_skel(int root_fd, const char *skel) {
assert(root_fd >= 0);
- r = copy_tree_at(AT_FDCWD, skel, root_fd, ".", UID_INVALID, GID_INVALID, COPY_MERGE|COPY_REPLACE, NULL);
+ r = copy_tree_at(AT_FDCWD, skel, root_fd, ".", UID_INVALID, GID_INVALID, COPY_MERGE|COPY_REPLACE, NULL, NULL);
if (r == -ENOENT) {
log_info("Skeleton directory %s missing, ignoring.", skel);
return 0;
diff --git a/src/import/import-tar.c b/src/import/import-tar.c
index dff6d3276a..ff32ae4e59 100644
--- a/src/import/import-tar.c
+++ b/src/import/import-tar.c
@@ -225,7 +225,7 @@ static int tar_import_fork_tar(TarImport *i) {
(void) rm_rf(d, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
if (i->flags & IMPORT_BTRFS_SUBVOL)
- r = btrfs_subvol_make_fallback(d, 0755);
+ r = btrfs_subvol_make_fallback(AT_FDCWD, d, 0755);
else
r = RET_NERRNO(mkdir(d, 0755));
if (r == -EEXIST && (i->flags & IMPORT_DIRECT)) /* EEXIST is OK if in direct mode, but not otherwise,
diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c
index e474dffa72..2db25192da 100644
--- a/src/import/pull-tar.c
+++ b/src/import/pull-tar.c
@@ -245,7 +245,7 @@ static int tar_pull_make_local_copy(TarPull *i) {
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
BTRFS_SNAPSHOT_RECURSIVE);
else
- r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS, NULL);
+ r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to create local image: %m");
@@ -516,7 +516,7 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) {
(void) rm_rf(where, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
if (i->flags & PULL_BTRFS_SUBVOL)
- r = btrfs_subvol_make_fallback(where, 0755);
+ r = btrfs_subvol_make_fallback(AT_FDCWD, where, 0755);
else
r = RET_NERRNO(mkdir(where, 0755));
if (r == -EEXIST && (i->flags & PULL_DIRECT)) /* EEXIST is OK if in direct mode, but not otherwise,
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index 5ecb570848..effee717b6 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -1019,9 +1019,9 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
* 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy
* the UID/GIDs as they are. */
if (copy_from)
- r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, uid_shift == 0 ? UID_INVALID : 0, uid_shift == 0 ? GID_INVALID : 0, copy_flags, NULL);
+ r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, uid_shift == 0 ? UID_INVALID : 0, uid_shift == 0 ? GID_INVALID : 0, copy_flags, NULL, NULL);
else
- r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, uid_shift == 0 ? UID_INVALID : uid_shift, uid_shift == 0 ? GID_INVALID : uid_shift, copy_flags, NULL);
+ r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, uid_shift == 0 ? UID_INVALID : uid_shift, uid_shift == 0 ? GID_INVALID : uid_shift, copy_flags, NULL, NULL);
hostfd = safe_close(hostfd);
containerfd = safe_close(containerfd);
diff --git a/src/partition/repart.c b/src/partition/repart.c
index 52b03ec7f0..decf4e8589 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -243,6 +243,7 @@ typedef struct Partition {
char **exclude_files_source;
char **exclude_files_target;
char **make_directories;
+ char **subvolumes;
EncryptMode encrypt;
VerityMode verity;
char *verity_match_key;
@@ -389,6 +390,7 @@ static Partition* partition_free(Partition *p) {
strv_free(p->exclude_files_source);
strv_free(p->exclude_files_target);
strv_free(p->make_directories);
+ strv_free(p->subvolumes);
free(p->verity_match_key);
free(p->roothash);
@@ -417,6 +419,7 @@ static void partition_foreignize(Partition *p) {
p->exclude_files_source = strv_free(p->exclude_files_source);
p->exclude_files_target = strv_free(p->exclude_files_target);
p->make_directories = strv_free(p->make_directories);
+ p->subvolumes = strv_free(p->subvolumes);
p->verity_match_key = mfree(p->verity_match_key);
p->priority = 0;
@@ -1503,7 +1506,7 @@ static int config_parse_make_dirs(
void *data,
void *userdata) {
- Partition *partition = ASSERT_PTR(data);
+ char ***sv = ASSERT_PTR(data);
const char *p = ASSERT_PTR(rvalue);
int r;
@@ -1531,7 +1534,7 @@ static int config_parse_make_dirs(
if (r < 0)
continue;
- r = strv_consume(&partition->make_directories, TAKE_PTR(d));
+ r = strv_consume(sv, TAKE_PTR(d));
if (r < 0)
return log_oom();
}
@@ -1626,7 +1629,7 @@ static int partition_read_definition(Partition *p, const char *path, const char
{ "Partition", "CopyFiles", config_parse_copy_files, 0, &p->copy_files },
{ "Partition", "ExcludeFiles", config_parse_exclude_files, 0, &p->exclude_files_source },
{ "Partition", "ExcludeFilesTarget", config_parse_exclude_files, 0, &p->exclude_files_target },
- { "Partition", "MakeDirectories", config_parse_make_dirs, 0, p },
+ { "Partition", "MakeDirectories", config_parse_make_dirs, 0, &p->make_directories },
{ "Partition", "Encrypt", config_parse_encrypt, 0, &p->encrypt },
{ "Partition", "Verity", config_parse_verity, 0, &p->verity },
{ "Partition", "VerityMatchKey", config_parse_string, 0, &p->verity_match_key },
@@ -1636,6 +1639,7 @@ static int partition_read_definition(Partition *p, const char *path, const char
{ "Partition", "GrowFileSystem", config_parse_tristate, 0, &p->growfs },
{ "Partition", "SplitName", config_parse_string, 0, &p->split_name_format },
{ "Partition", "Minimize", config_parse_minimize, 0, &p->minimize },
+ { "Partition", "Subvolumes", config_parse_make_dirs, 0, &p->subvolumes },
{}
};
int r;
@@ -1749,6 +1753,10 @@ static int partition_read_definition(Partition *p, const char *path, const char
"SizeMinBytes=/SizeMaxBytes= cannot be used with Verity=%s",
verity_mode_to_string(p->verity));
+ if (!strv_isempty(p->subvolumes) && arg_offline != 0)
+ return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Subvolumes= can only be used with --offline=no");
+
/* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
if ((IN_SET(p->type.designator,
PARTITION_ROOT_VERITY,
@@ -4304,6 +4312,66 @@ static int make_copy_files_denylist(
return 0;
}
+static int add_subvolume_path(const char *path, Set **subvolumes) {
+ _cleanup_free_ struct stat *st = NULL;
+ int r;
+
+ assert(path);
+ assert(subvolumes);
+
+ st = new(struct stat, 1);
+ if (!st)
+ return log_oom();
+
+ r = chase_and_stat(path, arg_root, CHASE_PREFIX_ROOT, NULL, st);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to stat source file '%s/%s': %m", strempty(arg_root), path);
+
+ r = set_ensure_put(subvolumes, &inode_hash_ops, st);
+ if (r < 0)
+ return log_oom();
+ if (r > 0)
+ TAKE_PTR(st);
+
+ return 0;
+}
+
+static int make_subvolumes_set(
+ Context *context,
+ const Partition *p,
+ const char *source,
+ const char *target,
+ Set **ret) {
+ _cleanup_set_free_ Set *subvolumes = NULL;
+ int r;
+
+ assert(context);
+ assert(p);
+ assert(target);
+ assert(ret);
+
+ STRV_FOREACH(subvolume, p->subvolumes) {
+ _cleanup_free_ char *path = NULL;
+
+ const char *s = path_startswith(*subvolume, target);
+ if (!s)
+ continue;
+
+ path = path_join(source, s);
+ if (!path)
+ return log_oom();
+
+ r = add_subvolume_path(path, &subvolumes);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(subvolumes);
+ return 0;
+}
+
static int do_copy_files(Context *context, Partition *p, const char *root) {
int r;
@@ -4337,12 +4405,17 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
STRV_FOREACH_PAIR(source, target, p->copy_files) {
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
+ _cleanup_set_free_ Set *subvolumes_by_source_inode = NULL;
_cleanup_close_ int sfd = -EBADF, pfd = -EBADF, tfd = -EBADF;
r = make_copy_files_denylist(context, p, *source, *target, &denylist);
if (r < 0)
return r;
+ r = make_subvolumes_set(context, p, *source, *target, &subvolumes_by_source_inode);
+ if (r < 0)
+ return r;
+
sfd = chase_and_open(*source, arg_root, CHASE_PREFIX_ROOT, O_CLOEXEC|O_NOCTTY, NULL);
if (sfd < 0)
return log_error_errno(sfd, "Failed to open source file '%s%s': %m", strempty(arg_root), *source);
@@ -4368,7 +4441,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
if (r < 0)
return log_error_errno(r, "Failed to extract directory from '%s': %m", *target);
- r = mkdir_p_root(root, dn, UID_INVALID, GID_INVALID, 0755);
+ r = mkdir_p_root(root, dn, UID_INVALID, GID_INVALID, 0755, p->subvolumes);
if (r < 0)
return log_error_errno(r, "Failed to create parent directory '%s': %m", dn);
@@ -4381,14 +4454,14 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
pfd, fn,
UID_INVALID, GID_INVALID,
COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE,
- denylist);
+ denylist, subvolumes_by_source_inode);
} else
r = copy_tree_at(
sfd, ".",
tfd, ".",
UID_INVALID, GID_INVALID,
COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE,
- denylist);
+ denylist, subvolumes_by_source_inode);
if (r < 0)
return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m",
strempty(arg_root), *source, strempty(root), *target);
@@ -4408,7 +4481,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
if (r < 0)
return log_error_errno(r, "Failed to extract directory from '%s': %m", *target);
- r = mkdir_p_root(root, dn, UID_INVALID, GID_INVALID, 0755);
+ r = mkdir_p_root(root, dn, UID_INVALID, GID_INVALID, 0755, p->subvolumes);
if (r < 0)
return log_error_errno(r, "Failed to create parent directory: %m");
@@ -4440,8 +4513,7 @@ static int do_make_directories(Partition *p, const char *root) {
assert(root);
STRV_FOREACH(d, p->make_directories) {
-
- r = mkdir_p_root(root, *d, UID_INVALID, GID_INVALID, 0755);
+ r = mkdir_p_root(root, *d, UID_INVALID, GID_INVALID, 0755, p->subvolumes);
if (r < 0)
return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
}
diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c
index d6479929c1..d91c69b624 100644
--- a/src/shared/btrfs-util.c
+++ b/src/shared/btrfs-util.c
@@ -42,36 +42,6 @@
* device nodes (that reference drivers) rather than fds to normal
* files or directories. */
-static int 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 = validate_subvolume_name(fn);
- if (r < 0)
- return r;
-
- *ret = TAKE_PTR(fn);
- return 0;
-}
-
int btrfs_is_subvol_at(int dir_fd, const char *path) {
struct stat st;
@@ -88,71 +58,6 @@ int btrfs_is_subvol_at(int dir_fd, const char *path) {
return is_fs_type_at(dir_fd, path, BTRFS_SUPER_MAGIC);
}
-int btrfs_subvol_make_fd(int fd, const char *subvolume) {
- struct btrfs_ioctl_vol_args args = {};
- _cleanup_close_ int real_fd = -EBADF;
- int r;
-
- assert(subvolume);
-
- r = validate_subvolume_name(subvolume);
- if (r < 0)
- return r;
-
- /* If an O_PATH fd was specified, let's convert here to a proper one, as btrfs ioctl's can't deal
- * with O_PATH. */
- fd = fd_reopen_condition(fd, O_RDONLY|O_CLOEXEC|O_DIRECTORY, O_PATH|O_DIRECTORY, &real_fd);
- if (fd < 0)
- return fd;
-
- strncpy(args.name, subvolume, sizeof(args.name)-1);
-
- return RET_NERRNO(ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args));
-}
-
-int btrfs_subvol_make(const char *path) {
- _cleanup_free_ char *subvolume = NULL;
- _cleanup_close_ int fd = -EBADF;
- int r;
-
- assert(path);
-
- r = extract_subvolume_name(path, &subvolume);
- if (r < 0)
- return r;
-
- fd = open_parent(path, O_CLOEXEC, 0);
- if (fd < 0)
- return fd;
-
- return btrfs_subvol_make_fd(fd, subvolume);
-}
-
-int btrfs_subvol_make_fallback(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(path);
- umask(old);
-
- if (r >= 0)
- return 1; /* subvol worked */
- if (r != -ENOTTY)
- return r;
-
- if (mkdir(path, mode) < 0)
- return -errno;
-
- return 0; /* plain directory */
-}
-
int btrfs_subvol_set_read_only_at(int dir_fd, const char *path, bool b) {
_cleanup_close_ int fd = -EBADF;
uint64_t flags, nflags;
@@ -1131,7 +1036,7 @@ int btrfs_subvol_remove_at(int dir_fd, const char *path, BtrfsRemoveFlags flags)
if (fd < 0)
return fd;
- r = validate_subvolume_name(subvolume);
+ r = btrfs_validate_subvolume_name(subvolume);
if (r < 0)
return r;
@@ -1551,7 +1456,7 @@ int btrfs_subvol_snapshot_at_full(
if (new_fd < 0)
return new_fd;
- r = validate_subvolume_name(subvolume);
+ r = btrfs_validate_subvolume_name(subvolume);
if (r < 0)
return r;
@@ -1565,7 +1470,7 @@ int btrfs_subvol_snapshot_at_full(
if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
return -EISDIR;
- r = btrfs_subvol_make_fd(new_fd, subvolume);
+ r = btrfs_subvol_make(new_fd, subvolume);
if (r < 0) {
if (ERRNO_IS_NOT_SUPPORTED(r) && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) {
/* If the destination doesn't support subvolumes, then use a plain directory, if that's requested. */
diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h
index c972cc0744..22ff0c1115 100644
--- a/src/shared/btrfs-util.h
+++ b/src/shared/btrfs-util.h
@@ -7,6 +7,7 @@
#include "sd-id128.h"
+#include "btrfs.h"
#include "copy.h"
#include "time-util.h"
@@ -70,11 +71,6 @@ int btrfs_quota_scan_start(int fd);
int btrfs_quota_scan_wait(int fd);
int btrfs_quota_scan_ongoing(int fd);
-int btrfs_subvol_make(const char *path);
-int btrfs_subvol_make_fd(int fd, const char *subvolume);
-
-int btrfs_subvol_make_fallback(const char *path, mode_t);
-
int btrfs_subvol_snapshot_at_full(int dir_fdf, const char *from, int dir_fdt, const char *to, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
static inline int btrfs_subvol_snapshot_at(int dir_fdf, const char *from, int dir_fdt, const char *to, BtrfsSnapshotFlags flags) {
return btrfs_subvol_snapshot_at_full(dir_fdf, from, dir_fdt, to, flags, NULL, NULL, NULL);
diff --git a/src/shared/copy.c b/src/shared/copy.c
index d8355a837c..04603fd20e 100644
--- a/src/shared/copy.c
+++ b/src/shared/copy.c
@@ -730,6 +730,7 @@ static int fd_copy_tree_generic(
gid_t override_gid,
CopyFlags copy_flags,
Hashmap *denylist,
+ Set *subvolumes,
HardlinkContext *hardlink_context,
const char *display_path,
copy_progress_path_t progress_path,
@@ -933,6 +934,7 @@ static int fd_copy_directory(
gid_t override_gid,
CopyFlags copy_flags,
Hashmap *denylist,
+ Set *subvolumes,
HardlinkContext *hardlink_context,
const char *display_path,
copy_progress_path_t progress_path,
@@ -986,7 +988,7 @@ static int fd_copy_directory(
fdt = xopenat_lock(dt, to,
O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|(exists ? 0 : O_CREAT|O_EXCL),
- (copy_flags & COPY_MAC_CREATE ? XO_LABEL : 0),
+ (copy_flags & COPY_MAC_CREATE ? XO_LABEL : 0)|(set_contains(subvolumes, st) ? XO_SUBVOLUME : 0),
st->st_mode & 07777,
copy_flags & COPY_LOCK_BSD ? LOCK_BSD : LOCK_NONE,
LOCK_EX);
@@ -1068,7 +1070,7 @@ static int fd_copy_directory(
q = fd_copy_tree_generic(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device,
depth_left-1, override_uid, override_gid, copy_flags & ~COPY_LOCK_BSD,
- denylist, hardlink_context, child_display_path, progress_path,
+ denylist, subvolumes, hardlink_context, child_display_path, progress_path,
progress_bytes, userdata);
if (q == -EINTR) /* Propagate SIGINT/SIGTERM up instantly */
@@ -1145,6 +1147,7 @@ static int fd_copy_tree_generic(
gid_t override_gid,
CopyFlags copy_flags,
Hashmap *denylist,
+ Set *subvolumes,
HardlinkContext *hardlink_context,
const char *display_path,
copy_progress_path_t progress_path,
@@ -1157,8 +1160,8 @@ static int fd_copy_tree_generic(
if (S_ISDIR(st->st_mode))
return fd_copy_directory(df, from, st, dt, to, original_device, depth_left-1, override_uid,
- override_gid, copy_flags, denylist, hardlink_context, display_path,
- progress_path, progress_bytes, userdata);
+ override_gid, copy_flags, denylist, subvolumes, hardlink_context,
+ display_path, progress_path, progress_bytes, userdata);
DenyType t = PTR_TO_INT(hashmap_get(denylist, st));
if (t == DENY_INODE) {
@@ -1189,6 +1192,7 @@ int copy_tree_at_full(
gid_t override_gid,
CopyFlags copy_flags,
Hashmap *denylist,
+ Set *subvolumes,
copy_progress_path_t progress_path,
copy_progress_bytes_t progress_bytes,
void *userdata) {
@@ -1204,7 +1208,7 @@ int copy_tree_at_full(
return -errno;
r = fd_copy_tree_generic(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid,
- override_gid, copy_flags, denylist, NULL, NULL, progress_path,
+ override_gid, copy_flags, denylist, subvolumes, NULL, NULL, progress_path,
progress_bytes, userdata);
if (r < 0)
return r;
@@ -1273,7 +1277,7 @@ int copy_directory_at_full(
COPY_DEPTH_MAX,
UID_INVALID, GID_INVALID,
copy_flags,
- NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
progress_path,
progress_bytes,
userdata);
diff --git a/src/shared/copy.h b/src/shared/copy.h
index 3bf496e42b..4e4eb74986 100644
--- a/src/shared/copy.h
+++ b/src/shared/copy.h
@@ -76,12 +76,12 @@ static inline int copy_file_atomic(const char *from, const char *to, mode_t mode
return copy_file_atomic_full(from, to, mode, 0, 0, copy_flags, NULL, NULL);
}
-int copy_tree_at_full(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, Hashmap *denylist, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
-static inline int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, Hashmap *denylist) {
- return copy_tree_at_full(fdf, from, fdt, to, override_uid, override_gid, copy_flags, denylist, NULL, NULL, NULL);
+int copy_tree_at_full(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, Hashmap *denylist, Set *subvolumes, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
+static inline int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, Hashmap *denylist, Set *subvolumes) {
+ return copy_tree_at_full(fdf, from, fdt, to, override_uid, override_gid, copy_flags, denylist, subvolumes, NULL, NULL, NULL);
}
-static inline int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, Hashmap *denylist) {
- return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, denylist, NULL, NULL, NULL);
+static inline int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, Hashmap *denylist, Set *subvolumes) {
+ return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, denylist, subvolumes, NULL, NULL, NULL);
}
int copy_directory_at_full(int dir_fdf, const char *from, int dir_fdt, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index b4fbc77177..3b77706f8f 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -1936,7 +1936,7 @@ static int mount_partition(
if (directory) {
/* Automatically create missing mount points inside the image, if necessary. */
- r = mkdir_p_root(where, directory, uid_shift, (gid_t) uid_shift, 0755);
+ r = mkdir_p_root(where, directory, uid_shift, (gid_t) uid_shift, 0755, NULL);
if (r < 0 && r != -EROFS)
return r;
diff --git a/src/shared/label-util.c b/src/shared/label-util.c
index 3316c9ed37..7262da2b1f 100644
--- a/src/shared/label-util.c
+++ b/src/shared/label-util.c
@@ -108,7 +108,7 @@ int btrfs_subvol_make_label(const char *path) {
if (r < 0)
return r;
- r = btrfs_subvol_make(path);
+ r = btrfs_subvol_make(AT_FDCWD, path);
mac_selinux_create_file_clear();
if (r < 0)
diff --git a/src/test/test-btrfs.c b/src/test/test-btrfs.c
index ba09563058..5980727c85 100644
--- a/src/test/test-btrfs.c
+++ b/src/test/test-btrfs.c
@@ -48,7 +48,7 @@ int main(int argc, char *argv[]) {
safe_close(fd);
}
- r = btrfs_subvol_make("/xxxtest");
+ r = btrfs_subvol_make(AT_FDCWD, "/xxxtest");
if (r < 0)
log_error_errno(r, "Failed to make subvolume: %m");
@@ -97,33 +97,33 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
- r = btrfs_subvol_make("/xxxrectest");
+ r = btrfs_subvol_make(AT_FDCWD, "/xxxrectest");
if (r < 0)
log_error_errno(r, "Failed to make subvolume: %m");
- r = btrfs_subvol_make("/xxxrectest/xxxrectest2");
+ r = btrfs_subvol_make(AT_FDCWD, "/xxxrectest/xxxrectest2");
if (r < 0)
log_error_errno(r, "Failed to make subvolume: %m");
- r = btrfs_subvol_make("/xxxrectest/xxxrectest3");
+ r = btrfs_subvol_make(AT_FDCWD, "/xxxrectest/xxxrectest3");
if (r < 0)
log_error_errno(r, "Failed to make subvolume: %m");
- r = btrfs_subvol_make("/xxxrectest/xxxrectest3/sub");
+ r = btrfs_subvol_make(AT_FDCWD, "/xxxrectest/xxxrectest3/sub");
if (r < 0)
log_error_errno(r, "Failed to make subvolume: %m");
if (mkdir("/xxxrectest/dir", 0755) < 0)
log_error_errno(errno, "Failed to make directory: %m");
- r = btrfs_subvol_make("/xxxrectest/dir/xxxrectest4");
+ r = btrfs_subvol_make(AT_FDCWD, "/xxxrectest/dir/xxxrectest4");
if (r < 0)
log_error_errno(r, "Failed to make subvolume: %m");
if (mkdir("/xxxrectest/dir/xxxrectest4/dir", 0755) < 0)
log_error_errno(errno, "Failed to make directory: %m");
- r = btrfs_subvol_make("/xxxrectest/dir/xxxrectest4/dir/xxxrectest5");
+ r = btrfs_subvol_make(AT_FDCWD, "/xxxrectest/dir/xxxrectest4/dir/xxxrectest5");
if (r < 0)
log_error_errno(r, "Failed to make subvolume: %m");
@@ -142,7 +142,7 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "Failed to recursively remove subvolume: %m");
- r = btrfs_subvol_make("/xxxquotatest");
+ r = btrfs_subvol_make(AT_FDCWD, "/xxxquotatest");
if (r < 0)
log_error_errno(r, "Failed to make subvolume: %m");
@@ -150,7 +150,7 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "Failed to set up auto qgroup: %m");
- r = btrfs_subvol_make("/xxxquotatest/beneath");
+ r = btrfs_subvol_make(AT_FDCWD, "/xxxquotatest/beneath");
if (r < 0)
log_error_errno(r, "Failed to make subvolume: %m");
diff --git a/src/test/test-copy.c b/src/test/test-copy.c
index e22a620ade..06fa05d8fa 100644
--- a/src/test/test-copy.c
+++ b/src/test/test-copy.c
@@ -67,11 +67,11 @@ TEST(copy_tree_replace_file) {
/* The file exists- now overwrite original contents, and test the COPY_REPLACE flag. */
- assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK, NULL) == -EEXIST);
+ assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK, NULL, NULL) == -EEXIST);
assert_se(read_file_at_and_streq(AT_FDCWD, dst, "foo foo foo\n"));
- assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE, NULL) == 0);
+ assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE, NULL, NULL) == 0);
assert_se(read_file_at_and_streq(AT_FDCWD, dst, "bar bar\n"));
}
@@ -92,14 +92,14 @@ TEST(copy_tree_replace_dirs) {
assert_se(write_string_file_at(dst, "bar", "dest file 2", WRITE_STRING_FILE_CREATE) == 0);
/* Copying without COPY_REPLACE should fail because the destination file already exists. */
- assert_se(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK, NULL) == -EEXIST);
+ assert_se(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK, NULL, NULL) == -EEXIST);
assert_se(read_file_at_and_streq(src, "foo", "src file 1\n"));
assert_se(read_file_at_and_streq(src, "bar", "src file 2\n"));
assert_se(read_file_at_and_streq(dst, "foo", "dest file 1\n"));
assert_se(read_file_at_and_streq(dst, "bar", "dest file 2\n"));
- assert_se(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE, NULL) == 0);
+ assert_se(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE, NULL, NULL) == 0);
assert_se(read_file_at_and_streq(src, "foo", "src file 1\n"));
assert_se(read_file_at_and_streq(src, "bar", "src file 2\n"));
@@ -191,7 +191,7 @@ TEST(copy_tree) {
assert_se(hashmap_ensure_put(&denylist, &inode_hash_ops, cp, INT_TO_PTR(DENY_INODE)) >= 0);
TAKE_PTR(cp);
- assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS, denylist) == 0);
+ assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS, denylist, NULL) == 0);
STRV_FOREACH(p, files) {
_cleanup_free_ char *buf = NULL, *f = NULL, *c = NULL;
@@ -243,8 +243,8 @@ TEST(copy_tree) {
assert_se(stat(unixsockp, &st) >= 0);
assert_se(S_ISSOCK(st.st_mode));
- assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK, denylist) < 0);
- assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK, denylist) < 0);
+ assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK, denylist, NULL) < 0);
+ assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK, denylist, NULL) < 0);
ignorep = strjoina(copy_dir, "ignore/file");
assert_se(RET_NERRNO(access(ignorep, F_OK)) == -ENOENT);
diff --git a/src/test/test-mkdir.c b/src/test/test-mkdir.c
index 24933a3464..03e5a4997c 100644
--- a/src/test/test-mkdir.c
+++ b/src/test/test-mkdir.c
@@ -96,7 +96,7 @@ TEST(mkdir_p_root) {
assert_se(mkdtemp_malloc("/tmp/test-mkdir-XXXXXX", &tmp) >= 0);
assert_se(p = path_join(tmp, "run/aaa/bbb"));
- assert_se(mkdir_p_root(tmp, "/run/aaa/bbb", UID_INVALID, GID_INVALID, 0755) >= 0);
+ assert_se(mkdir_p_root(tmp, "/run/aaa/bbb", UID_INVALID, GID_INVALID, 0755, NULL) >= 0);
assert_se(is_dir(p, false) > 0);
assert_se(is_dir(p, true) > 0);
@@ -109,18 +109,18 @@ TEST(mkdir_p_root) {
p = mfree(p);
assert_se(p = path_join(tmp, "var/run/hoge/foo/baz"));
- assert_se(mkdir_p_root(tmp, "/var/run/hoge/foo/baz", UID_INVALID, GID_INVALID, 0755) >= 0);
+ assert_se(mkdir_p_root(tmp, "/var/run/hoge/foo/baz", UID_INVALID, GID_INVALID, 0755, NULL) >= 0);
assert_se(is_dir(p, false) > 0);
assert_se(is_dir(p, true) > 0);
p = mfree(p);
assert_se(p = path_join(tmp, "not-exists"));
- assert_se(mkdir_p_root(p, "/aaa", UID_INVALID, GID_INVALID, 0755) == -ENOENT);
+ assert_se(mkdir_p_root(p, "/aaa", UID_INVALID, GID_INVALID, 0755, NULL) == -ENOENT);
p = mfree(p);
assert_se(p = path_join(tmp, "regular-file"));
assert_se(touch(p) >= 0);
- assert_se(mkdir_p_root(p, "/aaa", UID_INVALID, GID_INVALID, 0755) == -ENOTDIR);
+ assert_se(mkdir_p_root(p, "/aaa", UID_INVALID, GID_INVALID, 0755, NULL) == -ENOTDIR);
/* FIXME: The tests below do not work.
p = mfree(p);
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index e765db4895..376d3ad7d3 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -1866,7 +1866,7 @@ static int copy_files(Context *c, Item *i) {
i->uid_set ? i->uid : UID_INVALID,
i->gid_set ? i->gid : GID_INVALID,
COPY_REFLINK | ((i->append_or_force) ? COPY_MERGE : COPY_MERGE_EMPTY) | COPY_MAC_CREATE | COPY_HARDLINKS,
- NULL);
+ NULL, NULL);
fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
if (fd < 0) {
@@ -1932,7 +1932,7 @@ static int create_directory_or_subvolume(
subvol = false;
else {
WITH_UMASK((~mode) & 0777)
- r = btrfs_subvol_make_fd(pfd, bn);
+ r = btrfs_subvol_make(pfd, bn);
}
} else
r = 0;