/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/sendfile.h>
-#include <sys/stat.h>
#include <sys/xattr.h>
-#include <time.h>
#include <unistd.h>
#include "alloc-util.h"
#include "copy.h"
#include "dirent-util.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "io-util.h"
#include "macro.h"
-#include "missing.h"
+#include "missing_syscall.h"
#include "mountpoint-util.h"
+#include "nulstr-util.h"
+#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
+#include "tmpfile-util.h"
#include "umask-util.h"
#include "user-util.h"
#include "xattr-util.h"
return FLAGS_SET(flags, O_NONBLOCK) ? FD_IS_NONBLOCKING_PIPE : FD_IS_BLOCKING_PIPE;
}
+static int sigint_pending(void) {
+ sigset_t ss;
+
+ assert_se(sigemptyset(&ss) >= 0);
+ assert_se(sigaddset(&ss, SIGINT) >= 0);
+
+ if (sigtimedwait(&ss, NULL, &(struct timespec) { 0, 0 }) < 0) {
+ if (errno == EAGAIN)
+ return false;
+
+ return -errno;
+ }
+
+ return true;
+}
+
int copy_bytes_full(
int fdf, int fdt,
uint64_t max_bytes,
if (max_bytes <= 0)
return 1; /* return > 0 if we hit the max_bytes limit */
+ if (FLAGS_SET(copy_flags, COPY_SIGINT)) {
+ r = sigint_pending();
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return -EINTR;
+ }
+
if (max_bytes != UINT64_MAX && m > max_bytes)
m = max_bytes;
_cleanup_close_ int fdf = -1, fdt = -1;
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
- bool created;
+ bool exists, created;
int r;
assert(st);
return -errno;
fdf = -1;
- r = mkdirat(dt, to, st->st_mode & 07777);
- if (r >= 0)
- created = true;
- else if (errno == EEXIST && (copy_flags & COPY_MERGE))
+ exists = false;
+ if (copy_flags & COPY_MERGE_EMPTY) {
+ r = dir_is_empty_at(dt, to);
+ if (r < 0 && r != -ENOENT)
+ return r;
+ else if (r == 1)
+ exists = true;
+ }
+
+ if (exists)
created = false;
- else
- return -errno;
+ else {
+ r = mkdirat(dt, to, st->st_mode & 07777);
+ if (r >= 0)
+ created = true;
+ else if (errno == EEXIST && (copy_flags & COPY_MERGE))
+ created = false;
+ else
+ return -errno;
+ }
fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fdt < 0)
if (dot_or_dot_dot(de->d_name))
continue;
+ if (FLAGS_SET(copy_flags, COPY_SIGINT)) {
+ r = sigint_pending();
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return -EINTR;
+ }
+
if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
r = -errno;
continue;
if (progress_path) {
if (display_path)
- child_display_path = dp = strjoin(display_path, "/", de->d_name);
+ child_display_path = dp = path_join(display_path, de->d_name);
else
child_display_path = de->d_name;
else
q = -EOPNOTSUPP;
+ if (q == -EINTR) /* Propagate SIGINT up instantly */
+ return q;
if (q == -EEXIST && (copy_flags & COPY_MERGE))
q = 0;
-
if (q < 0)
r = q;
}
r = copy_bytes_full(fdf, fdt, (uint64_t) -1, copy_flags, NULL, NULL, progress_bytes, userdata);
- (void) copy_times(fdf, fdt);
+ (void) copy_times(fdf, fdt, copy_flags);
(void) copy_xattr(fdf, fdt);
return r;
int flags,
mode_t mode,
unsigned chattr_flags,
+ unsigned chattr_mask,
CopyFlags copy_flags,
copy_progress_bytes_t progress_bytes,
void *userdata) {
return -errno;
}
- if (chattr_flags != 0)
- (void) chattr_fd(fdt, chattr_flags, (unsigned) -1, NULL);
+ if (chattr_mask != 0)
+ (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL);
r = copy_file_fd_full(from, fdt, copy_flags, progress_bytes, userdata);
if (r < 0) {
return r;
}
+ if (chattr_mask != 0)
+ (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
+
if (close(fdt) < 0) {
unlink_noerrno(to);
return -errno;
const char *to,
mode_t mode,
unsigned chattr_flags,
+ unsigned chattr_mask,
CopyFlags copy_flags,
copy_progress_bytes_t progress_bytes,
void *userdata) {
return fdt;
}
- if (chattr_flags != 0)
- (void) chattr_fd(fdt, chattr_flags, (unsigned) -1, NULL);
+ if (chattr_mask != 0)
+ (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL);
r = copy_file_fd_full(from, fdt, copy_flags, progress_bytes, userdata);
if (r < 0)
return r;
}
+ if (chattr_mask != 0)
+ (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
+
t = mfree(t);
return 0;
}
-int copy_times(int fdf, int fdt) {
+int copy_times(int fdf, int fdt, CopyFlags flags) {
struct timespec ut[2];
struct stat st;
- usec_t crtime = 0;
assert(fdf >= 0);
assert(fdt >= 0);
if (futimens(fdt, ut) < 0)
return -errno;
- if (fd_getcrtime(fdf, &crtime) >= 0)
- (void) fd_setcrtime(fdt, crtime);
+ if (FLAGS_SET(flags, COPY_CRTIME)) {
+ usec_t crtime;
+
+ if (fd_getcrtime(fdf, &crtime) >= 0)
+ (void) fd_setcrtime(fdt, crtime);
+ }
return 0;
}
int copy_xattr(int fdf, int fdt) {
- _cleanup_free_ char *bufa = NULL, *bufb = NULL;
- size_t sza = 100, szb = 100;
- ssize_t n;
- int ret = 0;
+ _cleanup_free_ char *names = NULL;
+ int ret = 0, r;
const char *p;
- for (;;) {
- bufa = malloc(sza);
- if (!bufa)
- return -ENOMEM;
-
- n = flistxattr(fdf, bufa, sza);
- if (n == 0)
- return 0;
- if (n > 0)
- break;
- if (errno != ERANGE)
- return -errno;
-
- sza *= 2;
-
- bufa = mfree(bufa);
- }
-
- p = bufa;
- while (n > 0) {
- size_t l;
-
- l = strlen(p);
- assert(l < (size_t) n);
-
- if (startswith(p, "user.")) {
- ssize_t m;
-
- if (!bufb) {
- bufb = malloc(szb);
- if (!bufb)
- return -ENOMEM;
- }
+ r = flistxattr_malloc(fdf, &names);
+ if (r < 0)
+ return r;
- m = fgetxattr(fdf, p, bufb, szb);
- if (m < 0) {
- if (errno == ERANGE) {
- szb *= 2;
- bufb = mfree(bufb);
- continue;
- }
+ NULSTR_FOREACH(p, names) {
+ _cleanup_free_ char *value = NULL;
- return -errno;
- }
+ if (!startswith(p, "user."))
+ continue;
- if (fsetxattr(fdt, p, bufb, m, 0) < 0)
- ret = -errno;
- }
+ r = fgetxattr_malloc(fdf, p, &value);
+ if (r == -ENODATA)
+ continue; /* gone by now */
+ if (r < 0)
+ return r;
- p += l + 1;
- n -= l + 1;
+ if (fsetxattr(fdt, p, value, r, 0) < 0)
+ ret = -errno;
}
return ret;