1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-11 09:18:07 +03:00

copy: Add support for creating subvolumes to copy_tree_at()

The subvolumes set is a set of source inodes similar to how the
denylist hashmap contains source inodes as keys. It indicates
directories in the source tree that should become subvolumes in
the target tree.
This commit is contained in:
Daan De Meyer 2023-08-14 15:33:15 +02:00
parent bc6a6130ac
commit ad6fae7ffc
9 changed files with 31 additions and 27 deletions

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);

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;

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");

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);

View File

@ -4381,14 +4381,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, NULL);
} 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, NULL);
if (r < 0)
return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m",
strempty(arg_root), *source, strempty(root), *target);

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);

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);

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);

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) {