summaryrefslogtreecommitdiff
path: root/src/basic/copy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/basic/copy.c')
-rw-r--r--src/basic/copy.c129
1 files changed, 89 insertions, 40 deletions
diff --git a/src/basic/copy.c b/src/basic/copy.c
index e9a7efd232..6273ac9b47 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -45,6 +45,7 @@
#include "strv.h"
#include "time-util.h"
#include "umask-util.h"
+#include "user-util.h"
#include "xattr-util.h"
#define COPY_BUFFER_SIZE (16*1024u)
@@ -68,7 +69,7 @@ static ssize_t try_copy_file_range(int fd_in, loff_t *off_in,
return -errno;
}
-int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
+int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
bool try_cfr = true, try_sendfile = true, try_splice = true;
int r;
size_t m = SSIZE_MAX; /* that is the maximum that sendfile and c_f_r accept */
@@ -77,7 +78,7 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
assert(fdt >= 0);
/* Try btrfs reflinks first. */
- if (try_reflink &&
+ if ((copy_flags & COPY_REFLINK) &&
max_bytes == (uint64_t) -1 &&
lseek(fdf, 0, SEEK_CUR) == 0 &&
lseek(fdt, 0, SEEK_CUR) == 0) {
@@ -176,7 +177,16 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
return 0; /* return 0 if we hit EOF earlier than the size limit */
}
-static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
+static int fd_copy_symlink(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags) {
+
_cleanup_free_ char *target = NULL;
int r;
@@ -191,13 +201,25 @@ static int fd_copy_symlink(int df, const char *from, const struct stat *st, int
if (symlinkat(target, dt, to) < 0)
return -errno;
- if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+ if (fchownat(dt, to,
+ uid_is_valid(override_uid) ? override_uid : st->st_uid,
+ gid_is_valid(override_gid) ? override_gid : st->st_gid,
+ AT_SYMLINK_NOFOLLOW) < 0)
return -errno;
return 0;
}
-static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
+static int fd_copy_regular(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags) {
+
_cleanup_close_ int fdf = -1, fdt = -1;
struct timespec ts[2];
int r, q;
@@ -214,13 +236,15 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
if (fdt < 0)
return -errno;
- r = copy_bytes(fdf, fdt, (uint64_t) -1, true);
+ r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags);
if (r < 0) {
unlinkat(dt, to, 0);
return r;
}
- if (fchown(fdt, st->st_uid, st->st_gid) < 0)
+ if (fchown(fdt,
+ uid_is_valid(override_uid) ? override_uid : st->st_uid,
+ gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
r = -errno;
if (fchmod(fdt, st->st_mode & 07777) < 0)
@@ -229,7 +253,6 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
ts[0] = st->st_atim;
ts[1] = st->st_mtim;
(void) futimens(fdt, ts);
-
(void) copy_xattr(fdf, fdt);
q = close(fdt);
@@ -243,7 +266,15 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
return r;
}
-static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
+static int fd_copy_fifo(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags) {
int r;
assert(from);
@@ -254,7 +285,10 @@ static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt,
if (r < 0)
return -errno;
- if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+ if (fchownat(dt, to,
+ uid_is_valid(override_uid) ? override_uid : st->st_uid,
+ gid_is_valid(override_gid) ? override_gid : st->st_gid,
+ AT_SYMLINK_NOFOLLOW) < 0)
r = -errno;
if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
@@ -263,7 +297,15 @@ static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt,
return r;
}
-static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
+static int fd_copy_node(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags) {
int r;
assert(from);
@@ -274,7 +316,10 @@ static int fd_copy_node(int df, const char *from, const struct stat *st, int dt,
if (r < 0)
return -errno;
- if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+ if (fchownat(dt, to,
+ uid_is_valid(override_uid) ? override_uid : st->st_uid,
+ gid_is_valid(override_gid) ? override_gid : st->st_gid,
+ AT_SYMLINK_NOFOLLOW) < 0)
r = -errno;
if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
@@ -290,7 +335,9 @@ static int fd_copy_directory(
int dt,
const char *to,
dev_t original_device,
- bool merge) {
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags) {
_cleanup_close_ int fdf = -1, fdt = -1;
_cleanup_closedir_ DIR *d = NULL;
@@ -316,7 +363,7 @@ static int fd_copy_directory(
r = mkdirat(dt, to, st->st_mode & 07777);
if (r >= 0)
created = true;
- else if (errno == EEXIST && merge)
+ else if (errno == EEXIST && (copy_flags & COPY_MERGE))
created = false;
else
return -errno;
@@ -343,19 +390,19 @@ static int fd_copy_directory(
continue;
if (S_ISREG(buf.st_mode))
- q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else if (S_ISDIR(buf.st_mode))
- q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
+ q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, override_uid, override_gid, copy_flags);
else if (S_ISLNK(buf.st_mode))
- q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else if (S_ISFIFO(buf.st_mode))
- q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode))
- q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else
q = -EOPNOTSUPP;
- if (q == -EEXIST && merge)
+ if (q == -EEXIST && (copy_flags & COPY_MERGE))
q = 0;
if (q < 0)
@@ -368,7 +415,9 @@ static int fd_copy_directory(
st->st_mtim
};
- if (fchown(fdt, st->st_uid, st->st_gid) < 0)
+ if (fchown(fdt,
+ uid_is_valid(override_uid) ? override_uid : st->st_uid,
+ gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
r = -errno;
if (fchmod(fdt, st->st_mode & 07777) < 0)
@@ -381,7 +430,7 @@ static int fd_copy_directory(
return r;
}
-int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
+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) {
struct stat st;
assert(from);
@@ -391,24 +440,24 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge)
return -errno;
if (S_ISREG(st.st_mode))
- return fd_copy_regular(fdf, from, &st, fdt, to);
+ return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else if (S_ISDIR(st.st_mode))
- return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
+ return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, override_uid, override_gid, copy_flags);
else if (S_ISLNK(st.st_mode))
- return fd_copy_symlink(fdf, from, &st, fdt, to);
+ return fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else if (S_ISFIFO(st.st_mode))
- return fd_copy_fifo(fdf, from, &st, fdt, to);
+ return fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode))
- return fd_copy_node(fdf, from, &st, fdt, to);
+ return fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else
return -EOPNOTSUPP;
}
-int copy_tree(const char *from, const char *to, bool merge) {
- return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
+int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
+ return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags);
}
-int copy_directory_fd(int dirfd, const char *to, bool merge) {
+int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
struct stat st;
assert(dirfd >= 0);
@@ -420,10 +469,10 @@ int copy_directory_fd(int dirfd, const char *to, bool merge) {
if (!S_ISDIR(st.st_mode))
return -ENOTDIR;
- return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
+ return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, UID_INVALID, GID_INVALID, copy_flags);
}
-int copy_directory(const char *from, const char *to, bool merge) {
+int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
struct stat st;
assert(from);
@@ -435,10 +484,10 @@ int copy_directory(const char *from, const char *to, bool merge) {
if (!S_ISDIR(st.st_mode))
return -ENOTDIR;
- return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge);
+ return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, UID_INVALID, GID_INVALID, copy_flags);
}
-int copy_file_fd(const char *from, int fdt, bool try_reflink) {
+int copy_file_fd(const char *from, int fdt, CopyFlags copy_flags) {
_cleanup_close_ int fdf = -1;
int r;
@@ -449,7 +498,7 @@ int copy_file_fd(const char *from, int fdt, bool try_reflink) {
if (fdf < 0)
return -errno;
- r = copy_bytes(fdf, fdt, (uint64_t) -1, try_reflink);
+ r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags);
(void) copy_times(fdf, fdt);
(void) copy_xattr(fdf, fdt);
@@ -457,7 +506,7 @@ int copy_file_fd(const char *from, int fdt, bool try_reflink) {
return r;
}
-int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
+int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
int fdt = -1, r;
assert(from);
@@ -472,7 +521,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
if (chattr_flags != 0)
(void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
- r = copy_file_fd(from, fdt, true);
+ r = copy_file_fd(from, fdt, copy_flags);
if (r < 0) {
close(fdt);
unlink(to);
@@ -487,7 +536,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
return 0;
}
-int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
+int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
_cleanup_free_ char *t = NULL;
int r;
@@ -498,11 +547,11 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace
if (r < 0)
return r;
- r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
+ r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags, copy_flags);
if (r < 0)
return r;
- if (replace) {
+ if (copy_flags & COPY_REPLACE) {
r = renameat(AT_FDCWD, t, AT_FDCWD, to);
if (r < 0)
r = -errno;