summaryrefslogtreecommitdiffstats
path: root/src/shared/copy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/copy.c')
-rw-r--r--src/shared/copy.c123
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));