From: Daan De Meyer Date: Mon, 14 Aug 2023 13:33:15 +0000 (+0200) Subject: copy: Add support for creating subvolumes to copy_tree_at() X-Git-Tag: v255-rc1~724^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ad6fae7ffcdd10bc2b1523bfb08143f285287222;p=thirdparty%2Fsystemd.git 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. --- diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 4b830bf2a66..103ee41fe7f 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.c b/src/home/homework.c index 9773bfa7a32..066483e342b 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/pull-tar.c b/src/import/pull-tar.c index f9c82daec0e..2db25192da1 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"); diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 5ecb570848b..effee717b65 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 52b03ec7f0a..b73427c9df2 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -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); diff --git a/src/shared/copy.c b/src/shared/copy.c index d8355a837ce..04603fd20e9 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 3bf496e42bd..4e4eb74986a 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/test/test-copy.c b/src/test/test-copy.c index e22a620ade7..06fa05d8fae 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/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index bff09376dc0..376d3ad7d3f 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) {