Merge pull request #28833 from DaanDeMeyer/copy-subvolume

repart: Add Subvolumes= setting
This commit is contained in:
Daan De Meyer
2023-08-15 09:53:02 +02:00
committed by GitHub
27 changed files with 271 additions and 166 deletions
+15
View File
@@ -500,6 +500,21 @@
attributes.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Subvolumes=</varname></term>
<listitem><para>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 <varname>MakeDirectories=</varname> and <varname>CopyFiles=</varname>.</para>
<para>Note that this option only takes effect if the target filesystem supports subvolumes, such as
<literal>btrfs</literal>.</para>
<para>Note that due to limitations of <literal>mkfs.btrfs</literal>, this option is only supported
when running with <option>--offline=no</option>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Encrypt=</varname></term>
+91
View File
@@ -0,0 +1,91 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#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;
_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 */
}
+9
View File
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
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);
+5 -1
View File
@@ -9,6 +9,7 @@
#include <unistd.h>
#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;
+2 -1
View File
@@ -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);
+1
View File
@@ -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',
+10 -5
View File
@@ -5,6 +5,7 @@
#include <string.h>
#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)) {
+1 -1
View File
@@ -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);
+3 -1
View File
@@ -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;
}
+2 -2
View File
@@ -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);
+1 -1
View File
@@ -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.");
+1 -1
View File
@@ -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");
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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,
+2 -2
View File
@@ -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,
+2 -2
View File
@@ -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);
+81 -9
View File
@@ -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);
}
+3 -98
View File
@@ -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. */
+1 -5
View File
@@ -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);
+10 -6
View File
@@ -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);
+5 -5
View File
@@ -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);
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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)
+9 -9
View File
@@ -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");
+7 -7
View File
@@ -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);
+4 -4
View File
@@ -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);
+2 -2
View File
@@ -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;