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:
parent
bc6a6130ac
commit
ad6fae7ffc
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user