From: Daan De Meyer Date: Fri, 23 Sep 2022 19:07:41 +0000 (+0200) Subject: copy: Support passing a deny list of files/directories to not copy X-Git-Tag: v253-rc1~551^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a424958aa6a95ce037976a9b065e4c88e97992c7;p=thirdparty%2Fsystemd.git copy: Support passing a deny list of files/directories to not copy --- diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 81764690e99..a90b7e076d0 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -1122,9 +1122,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); + r = copy_tree_at(source_fd, ".", dfd, bn, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS, NULL); } else - r = copy_tree_at(source_fd, ".", target_fd, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS); + r = copy_tree_at(source_fd, ".", target_fd, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS, 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 c41866cd41b..bc38437f2cf 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); + r = copy_tree_at(AT_FDCWD, skel, root_fd, ".", UID_INVALID, GID_INVALID, COPY_MERGE|COPY_REPLACE, 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 cf18461db30..d3a1179ebc7 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); + r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS, 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 0a245247ec7..75f397dd6ba 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -1014,9 +1014,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); + 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); 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); + 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); hostfd = safe_close(hostfd); containerfd = safe_close(containerfd); diff --git a/src/partition/repart.c b/src/partition/repart.c index a037d7be1d2..96606b89970 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -3273,13 +3273,15 @@ static int do_copy_files(Partition *p, const char *root) { sfd, ".", pfd, fn, UID_INVALID, GID_INVALID, - COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS); + COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS, + NULL); } else r = copy_tree_at( sfd, ".", tfd, ".", UID_INVALID, GID_INVALID, - COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS); + COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS, + NULL); if (r < 0) return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target); } else { diff --git a/src/shared/copy.c b/src/shared/copy.c index 182544a206b..49d2fd94004 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -687,6 +687,7 @@ static int fd_copy_tree_generic( uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, + const Set *denylist, HardlinkContext *hardlink_context, const char *display_path, copy_progress_path_t progress_path, @@ -877,6 +878,7 @@ static int fd_copy_directory( uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, + const Set *denylist, HardlinkContext *hardlink_context, const char *display_path, copy_progress_path_t progress_path, @@ -979,6 +981,11 @@ static int fd_copy_directory( return r; } + if (set_contains(denylist, &buf)) { + log_debug("%s/%s is in the denylist, skipping", from, de->d_name); + continue; + } + if (S_ISDIR(buf.st_mode)) { /* * Don't descend into directories on other file systems, if this is requested. We do a simple @@ -1011,7 +1018,10 @@ 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, hardlink_context, child_display_path, progress_path, progress_bytes, userdata); + 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, denylist, + hardlink_context, child_display_path, progress_path, progress_bytes, + userdata); if (q == -EINTR) /* Propagate SIGINT/SIGTERM up instantly */ return q; @@ -1082,6 +1092,7 @@ static int fd_copy_tree_generic( uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, + const Set *denylist, HardlinkContext *hardlink_context, const char *display_path, copy_progress_path_t progress_path, @@ -1090,7 +1101,9 @@ static int fd_copy_tree_generic( int r; 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, hardlink_context, display_path, progress_path, progress_bytes, userdata); + 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); r = fd_copy_leaf(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_bytes, userdata); /* We just tried to copy a leaf node of the tree. If it failed because the node already exists *and* the COPY_REPLACE flag has been provided, we should unlink the node and re-copy. */ @@ -1113,6 +1126,7 @@ int copy_tree_at_full( uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, + const Set *denylist, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata) { @@ -1126,7 +1140,9 @@ int copy_tree_at_full( if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0) return -errno; - r = fd_copy_tree_generic(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata); + 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, + progress_bytes, userdata); if (r < 0) return r; @@ -1188,7 +1204,7 @@ int copy_directory_fd_full( COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, - NULL, NULL, + NULL, NULL, NULL, progress_path, progress_bytes, userdata); @@ -1231,7 +1247,7 @@ int copy_directory_full( COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, - NULL, NULL, + NULL, NULL, NULL, progress_path, progress_bytes, userdata); diff --git a/src/shared/copy.h b/src/shared/copy.h index d755916bd98..d19361c9a29 100644 --- a/src/shared/copy.h +++ b/src/shared/copy.h @@ -9,6 +9,8 @@ #include #include +#include "set.h" + typedef enum CopyFlags { COPY_REFLINK = 1 << 0, /* Try to reflink */ COPY_MERGE = 1 << 1, /* Merge existing trees with our new one to copy */ @@ -45,12 +47,12 @@ static inline int copy_file_atomic(const char *from, const char *to, mode_t mode return copy_file_atomic_full(from, to, mode, chattr_flags, chattr_mask, 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, 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) { - return copy_tree_at_full(fdf, from, fdt, to, override_uid, override_gid, copy_flags, 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, const Set *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, const Set *denylist) { + return copy_tree_at_full(fdf, from, fdt, 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) { - return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, 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, const Set *denylist) { + return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, denylist, NULL, NULL, NULL); } int copy_directory_fd_full(int dirfd, 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 00a38b18f6a..103f7ca73cb 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) == -EEXIST); + assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK, 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) == 0); + assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE, NULL) == 0); assert_se(read_file_at_and_streq(AT_FDCWD, dst, "bar bar\n")); } @@ -91,14 +91,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) == -EEXIST); + assert_se(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK, 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) == 0); + assert_se(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE, 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")); @@ -131,6 +131,8 @@ TEST(copy_file_fd) { } TEST(copy_tree) { + _cleanup_set_free_ Set *denylist = NULL; + _cleanup_free_ char *cp = NULL; char original_dir[] = "/tmp/test-copy_tree/"; char copy_dir[] = "/tmp/test-copy_tree-copy/"; char **files = STRV_MAKE("file", "dir1/file", "dir1/dir2/file", "dir1/dir2/dir3/dir4/dir5/file"); @@ -138,7 +140,7 @@ TEST(copy_tree) { "link2", "dir1/file"); char **hardlinks = STRV_MAKE("hlink", "file", "hlink2", "dir1/file"); - const char *unixsockp; + const char *unixsockp, *ignorep; struct stat st; int xattr_worked = -1; /* xattr support is optional in temporary directories, hence use it if we can, * but don't fail if we can't */ @@ -184,7 +186,14 @@ TEST(copy_tree) { unixsockp = strjoina(original_dir, "unixsock"); assert_se(mknod(unixsockp, S_IFSOCK|0644, 0) >= 0); - assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS) == 0); + ignorep = strjoina(original_dir, "ignore/file"); + assert_se(write_string_file(ignorep, "ignore", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) == 0); + assert_se(RET_NERRNO(stat(ignorep, &st)) >= 0); + assert_se(cp = memdup(&st, sizeof(st))); + assert_se(set_ensure_put(&denylist, &inode_hash_ops, cp) >= 0); + TAKE_PTR(cp); + + assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS, denylist) == 0); STRV_FOREACH(p, files) { _cleanup_free_ char *buf, *f, *c = NULL; @@ -236,8 +245,11 @@ 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) < 0); - assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0); + 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); + + ignorep = strjoina(copy_dir, "ignore/file"); + assert_se(RET_NERRNO(access(ignorep, F_OK)) == -ENOENT); (void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL); (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL); diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 1fec6d48319..d3258ec7011 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -1637,7 +1637,8 @@ static int copy_files(Item *i) { dfd, bn, i->uid_set ? i->uid : UID_INVALID, i->gid_set ? i->gid : GID_INVALID, - COPY_REFLINK | COPY_MERGE_EMPTY | COPY_MAC_CREATE | COPY_HARDLINKS); + COPY_REFLINK | COPY_MERGE_EMPTY | COPY_MAC_CREATE | COPY_HARDLINKS, + NULL); fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH); if (fd < 0) {