diff options
Diffstat (limited to '')
-rw-r--r-- | src/shared/copy.c | 123 |
1 files changed, 82 insertions, 41 deletions
diff --git a/src/shared/copy.c b/src/shared/copy.c index c0e30cd..2b87cba 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -208,6 +208,7 @@ int copy_bytes_full( r = reflink_range(fdf, foffset, fdt, toffset, max_bytes == UINT64_MAX ? 0 : max_bytes); /* partial reflink */ if (r >= 0) { off_t t; + int ret; /* This worked, yay! Now — to be fully correct — let's adjust the file pointers */ if (max_bytes == UINT64_MAX) { @@ -226,7 +227,14 @@ int copy_bytes_full( if (t < 0) return -errno; - return 0; /* we copied the whole thing, hence hit EOF, return 0 */ + if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) { + r = fd_verify_linked(fdf); + if (r < 0) + return r; + } + + /* We copied the whole thing, hence hit EOF, return 0. */ + ret = 0; } else { t = lseek(fdf, foffset + max_bytes, SEEK_SET); if (t < 0) @@ -236,8 +244,18 @@ int copy_bytes_full( if (t < 0) return -errno; - return 1; /* we copied only some number of bytes, which worked, but this means we didn't hit EOF, return 1 */ + /* We copied only some number of bytes, which worked, but + * this means we didn't hit EOF, return 1. */ + ret = 1; + } + + if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) { + r = fd_verify_linked(fdf); + if (r < 0) + return r; } + + return ret; } } } @@ -316,7 +334,7 @@ int copy_bytes_full( if (try_cfr) { n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u); if (n < 0) { - if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF)) + if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF, -EOPNOTSUPP)) return n; try_cfr = false; @@ -483,6 +501,12 @@ int copy_bytes_full( copied_something = true; } + if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) { + r = fd_verify_linked(fdf); + if (r < 0) + return r; + } + if (copy_flags & COPY_TRUNCATE) { off_t off = lseek(fdt, 0, SEEK_CUR); if (off < 0) @@ -508,7 +532,6 @@ static int fd_copy_symlink( _cleanup_free_ char *target = NULL; int r; - assert(from); assert(st); assert(to); @@ -526,7 +549,10 @@ static int fd_copy_symlink( mac_selinux_create_file_clear(); if (r < 0) { if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r))) { - log_notice_errno(r, "Failed to copy symlink '%s', ignoring: %m", from); + log_notice_errno(r, "Failed to copy symlink%s%s%s, ignoring: %m", + isempty(from) ? "" : " '", + strempty(from), + isempty(from) ? "" : "'"); return 0; } @@ -757,7 +783,6 @@ static int fd_copy_regular( _cleanup_close_ int fdf = -EBADF, fdt = -EBADF; int r, q; - assert(from); assert(st); assert(to); @@ -767,9 +792,9 @@ static int fd_copy_regular( if (r > 0) /* worked! */ return 0; - fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + fdf = xopenat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); if (fdf < 0) - return -errno; + return fdf; if (copy_flags & COPY_MAC_CREATE) { r = mac_selinux_create_file_prepare_at(dt, to, S_IFREG); @@ -797,6 +822,12 @@ static int fd_copy_regular( (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim }); (void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags); + if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) { + r = fd_verify_linked(fdf); + if (r < 0) + return r; + } + if (copy_flags & COPY_FSYNC) { if (fsync(fdt) < 0) { r = -errno; @@ -830,7 +861,6 @@ static int fd_copy_fifo( HardlinkContext *hardlink_context) { int r; - assert(from); assert(st); assert(to); @@ -849,7 +879,10 @@ static int fd_copy_fifo( if (copy_flags & COPY_MAC_CREATE) mac_selinux_create_file_clear(); if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_NEG_PRIVILEGE(r) || ERRNO_IS_NEG_NOT_SUPPORTED(r))) { - log_notice_errno(r, "Failed to copy fifo '%s', ignoring: %m", from); + log_notice_errno(r, "Failed to copy fifo%s%s%s, ignoring: %m", + isempty(from) ? "" : " '", + strempty(from), + isempty(from) ? "" : "'"); return 0; } else if (r < 0) return r; @@ -881,7 +914,6 @@ static int fd_copy_node( HardlinkContext *hardlink_context) { int r; - assert(from); assert(st); assert(to); @@ -900,7 +932,10 @@ static int fd_copy_node( if (copy_flags & COPY_MAC_CREATE) mac_selinux_create_file_clear(); if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_NEG_PRIVILEGE(r) || ERRNO_IS_NEG_NOT_SUPPORTED(r))) { - log_notice_errno(r, "Failed to copy node '%s', ignoring: %m", from); + log_notice_errno(r, "Failed to copy node%s%s%s, ignoring: %m", + isempty(from) ? "" : " '", + strempty(from), + isempty(from) ? "" : "'"); return 0; } else if (r < 0) return r; @@ -955,12 +990,9 @@ static int fd_copy_directory( if (depth_left == 0) return -ENAMETOOLONG; - if (from) - fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - else - fdf = fcntl(df, F_DUPFD_CLOEXEC, 3); + fdf = xopenat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); if (fdf < 0) - return -errno; + return fdf; if (!hardlink_context) { /* If recreating hardlinks is requested let's set up a context for that now. */ @@ -984,19 +1016,19 @@ static int fd_copy_directory( exists = r >= 0; - 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)|(set_contains(subvolumes, st) ? XO_SUBVOLUME : 0), - st->st_mode & 07777, - copy_flags & COPY_LOCK_BSD ? LOCK_BSD : LOCK_NONE, - LOCK_EX); + fdt = xopenat_lock_full(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)|(set_contains(subvolumes, st) ? XO_SUBVOLUME : 0), + st->st_mode & 07777, + copy_flags & COPY_LOCK_BSD ? LOCK_BSD : LOCK_NONE, + LOCK_EX); if (fdt < 0) return fdt; r = 0; if (PTR_TO_INT(hashmap_get(denylist, st)) == DENY_CONTENTS) { - log_debug("%s is in the denylist, not recursing", from); + log_debug("%s is in the denylist, not recursing", from ?: "file to copy"); goto finish; } @@ -1030,7 +1062,8 @@ static int fd_copy_directory( } if (PTR_TO_INT(hashmap_get(denylist, &buf)) == DENY_INODE) { - log_debug("%s/%s is in the denylist, ignoring", from, de->d_name); + log_debug("%s%s%s is in the denylist, ignoring", + strempty(from), isempty(from) ? "" : "/", de->d_name); continue; } @@ -1163,10 +1196,10 @@ static int fd_copy_tree_generic( DenyType t = PTR_TO_INT(hashmap_get(denylist, st)); if (t == DENY_INODE) { - log_debug("%s is in the denylist, ignoring", from); + log_debug("%s is in the denylist, ignoring", from ?: "file to copy"); return 0; } else if (t == DENY_CONTENTS) - log_debug("%s is configured to have its contents excluded, but is not a directory", from); + log_debug("%s is configured to have its contents excluded, but is not a directory", from ?: "file to copy"); 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. */ @@ -1198,11 +1231,10 @@ int copy_tree_at_full( struct stat st; int r; - assert(from); assert(to); assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD)); - if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0) + if (fstatat(fdf, strempty(from), &st, AT_SYMLINK_NOFOLLOW | (isempty(from) ? AT_EMPTY_PATH : 0)) < 0) return -errno; r = fd_copy_tree_generic(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, @@ -1305,13 +1337,12 @@ int copy_file_fd_at_full( int r; assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD); - assert(from); assert(fdt >= 0); assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD)); - fdf = openat(dir_fdf, from, O_RDONLY|O_CLOEXEC|O_NOCTTY); + fdf = xopenat(dir_fdf, from, O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fdf < 0) - return -errno; + return fdf; r = fd_verify_regular(fdf); if (r < 0) @@ -1332,6 +1363,12 @@ int copy_file_fd_at_full( (void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags); } + if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) { + r = fd_verify_linked(fdf); + if (r < 0) + return r; + } + if (copy_flags & COPY_FSYNC_FULL) { r = fsync_full(fdt); if (r < 0) @@ -1363,12 +1400,11 @@ int copy_file_at_full( assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD); assert(dir_fdt >= 0 || dir_fdt == AT_FDCWD); - assert(from); assert(to); - fdf = openat(dir_fdf, from, O_RDONLY|O_CLOEXEC|O_NOCTTY); + fdf = xopenat(dir_fdf, from, O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fdf < 0) - return -errno; + return fdf; if (fstat(fdf, &st) < 0) return -errno; @@ -1378,11 +1414,11 @@ int copy_file_at_full( return r; WITH_UMASK(0000) { - fdt = xopenat_lock(dir_fdt, to, - flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, - (copy_flags & COPY_MAC_CREATE ? XO_LABEL : 0), - mode != MODE_INVALID ? mode : st.st_mode, - copy_flags & COPY_LOCK_BSD ? LOCK_BSD : LOCK_NONE, LOCK_EX); + fdt = xopenat_lock_full(dir_fdt, to, + flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, + (copy_flags & COPY_MAC_CREATE ? XO_LABEL : 0), + mode != MODE_INVALID ? mode : st.st_mode, + copy_flags & COPY_LOCK_BSD ? LOCK_BSD : LOCK_NONE, LOCK_EX); if (fdt < 0) return fdt; } @@ -1403,6 +1439,12 @@ int copy_file_at_full( (void) copy_times(fdf, fdt, copy_flags); (void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags); + if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) { + r = fd_verify_linked(fdf); + if (r < 0) + goto fail; + } + if (chattr_mask != 0) (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL); @@ -1451,7 +1493,6 @@ int copy_file_atomic_at_full( _cleanup_close_ int fdt = -EBADF; int r; - assert(from); assert(to); assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD)); |