copy: drop COPY_REFLINK
Reflinking is a safe optimization whenever the source and destination are regular, seekable files on a filesystem that supports cloning. Requiring each caller to opt in through COPY_REFLINK adds flag plumbing without changing the necessary fallback behavior. Drop COPY_REFLINK, renumber the remaining flags, and have copy_bytes_full() unconditionally try FICLONE or FICLONERANGE before entering its normal copy loop. Isolate the clone attempt and its file-offset bookkeeping in a helper so copy_bytes_full() only has to handle its cloned, unavailable, and error results. A successful clone therefore completes in one operation, while an unsupported or rejected clone falls back to progress-limited copy_file_range(), sendfile(), or buffered copying. Keep the public reflink helpers for operations that specifically require clone semantics. Existing test-copy coverage exercises both bounded and unbounded regular-file copies through copy_bytes_full(). Signed-off-by: Daan De Meyer <daan@amutable.com>
This commit is contained in:
committed by
Zbigniew Jędrzejewski-Szmek
parent
28b1e4ea16
commit
5a12005c83
@@ -46,7 +46,7 @@ static int process_aliases(char *argv[], char *tempdir, char ***ret) {
|
||||
if (!dst)
|
||||
return -ENOMEM;
|
||||
|
||||
r = copy_file(src, dst, 0, 0644, COPY_REFLINK);
|
||||
r = copy_file(src, dst, 0, 0644, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -523,7 +523,7 @@ static int copy_file_with_version_check(
|
||||
* might be left at the end of the file. (Resetting before rather than after a copy attempt is safer
|
||||
* because a previous attempt might have failed half-way, leaving the file offset at some undefined
|
||||
* place.) */
|
||||
r = copy_bytes(source_fd, write_fd, UINT64_MAX, COPY_REFLINK|COPY_SEEK0_SOURCE);
|
||||
r = copy_bytes(source_fd, write_fd, UINT64_MAX, COPY_SEEK0_SOURCE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy data from \"%s\" to \"%s\": %m", source_path, dest_path);
|
||||
|
||||
|
||||
@@ -453,7 +453,7 @@ static int begin_copy_file(
|
||||
CLEANUP_TMPFILE_AT(target_dir_fd, t);
|
||||
|
||||
if (source_fd >= 0) {
|
||||
r = copy_bytes(source_fd, write_fd, UINT64_MAX, COPY_REFLINK|COPY_SEEK0_SOURCE);
|
||||
r = copy_bytes(source_fd, write_fd, UINT64_MAX, COPY_SEEK0_SOURCE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy data into '%s': %m", filename);
|
||||
|
||||
|
||||
@@ -3594,7 +3594,7 @@ static int setup_ephemeral(
|
||||
log_debug("Making ephemeral copy of %s to %s", rootfs->image, new_root);
|
||||
|
||||
fd = copy_file(rootfs->image, new_root, O_EXCL, 0600,
|
||||
COPY_LOCK_BSD|COPY_REFLINK|COPY_CRTIME|COPY_NOCOW_AFTER);
|
||||
COPY_LOCK_BSD|COPY_CRTIME|COPY_NOCOW_AFTER);
|
||||
if (fd < 0) {
|
||||
*reterr_path = strdup(rootfs->image);
|
||||
return log_debug_errno(fd, "Failed to copy image %s to %s: %m",
|
||||
|
||||
@@ -1402,7 +1402,7 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
|
||||
|
||||
/* Copying to stdout? */
|
||||
if (streq(arg_target, "-")) {
|
||||
r = copy_bytes(source_fd, STDOUT_FILENO, UINT64_MAX, COPY_REFLINK);
|
||||
r = copy_bytes(source_fd, STDOUT_FILENO, UINT64_MAX, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy bytes from %s in mage '%s' to stdout: %m", arg_source, arg_image);
|
||||
|
||||
@@ -1416,7 +1416,7 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
|
||||
AT_FDCWD, arg_target,
|
||||
arg_copy_ownership == 0 ? getuid() : UID_INVALID,
|
||||
arg_copy_ownership == 0 ? getgid() : GID_INVALID,
|
||||
COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS);
|
||||
COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS);
|
||||
if (r >= 0)
|
||||
return 0;
|
||||
if (r != -ENOTDIR)
|
||||
@@ -1433,7 +1433,7 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
|
||||
if (target_fd < 0)
|
||||
return log_error_errno(errno, "Failed to create regular file at target path '%s': %m", arg_target);
|
||||
|
||||
r = copy_bytes(source_fd, target_fd, UINT64_MAX, COPY_REFLINK);
|
||||
r = copy_bytes(source_fd, target_fd, UINT64_MAX, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy bytes from %s in mage '%s' to '%s': %m", arg_source, arg_image, arg_target);
|
||||
|
||||
@@ -1472,7 +1472,7 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
|
||||
if (target_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open target file '%s': %m", arg_target);
|
||||
|
||||
r = copy_bytes(STDIN_FILENO, target_fd, UINT64_MAX, COPY_REFLINK);
|
||||
r = copy_bytes(STDIN_FILENO, target_fd, UINT64_MAX, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy bytes from stdin to '%s' in image '%s': %m", arg_target, arg_image);
|
||||
|
||||
@@ -1505,7 +1505,7 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
|
||||
dfd, bn,
|
||||
arg_copy_ownership == 0 ? getuid() : UID_INVALID,
|
||||
arg_copy_ownership == 0 ? getgid() : GID_INVALID,
|
||||
COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS,
|
||||
COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS,
|
||||
/* denylist= */ NULL,
|
||||
/* subvolumes= */ NULL);
|
||||
} else
|
||||
@@ -1514,7 +1514,7 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
|
||||
target_fd, ".",
|
||||
arg_copy_ownership == 0 ? getuid() : UID_INVALID,
|
||||
arg_copy_ownership == 0 ? getgid() : GID_INVALID,
|
||||
COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS,
|
||||
COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS,
|
||||
/* denylist= */ NULL,
|
||||
/* subvolumes= */ NULL);
|
||||
if (r < 0)
|
||||
@@ -1531,7 +1531,7 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
|
||||
if (target_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open target file '%s': %m", arg_target);
|
||||
|
||||
r = copy_bytes(source_fd, target_fd, UINT64_MAX, COPY_REFLINK);
|
||||
r = copy_bytes(source_fd, target_fd, UINT64_MAX, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy bytes from '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image);
|
||||
|
||||
|
||||
@@ -384,7 +384,7 @@ static int process_locale(int rfd, sd_varlink **mute_console_link) {
|
||||
return log_error_errno(r, "Failed to check if directory file descriptor is root: %m");
|
||||
|
||||
if (arg_copy_locale && r == 0) {
|
||||
r = copy_file_atomic_at(AT_FDCWD, etc_locale_conf(), pfd, f, 0644, COPY_REFLINK);
|
||||
r = copy_file_atomic_at(AT_FDCWD, etc_locale_conf(), pfd, f, 0644, /* copy_flags= */ 0);
|
||||
if (r != -ENOENT) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy host's /etc/locale.conf: %m");
|
||||
@@ -526,7 +526,7 @@ static int process_keymap(int rfd, sd_varlink **mute_console_link) {
|
||||
return log_error_errno(r, "Failed to check if directory file descriptor is root: %m");
|
||||
|
||||
if (arg_copy_keymap && r == 0) {
|
||||
r = copy_file_atomic_at(AT_FDCWD, etc_vconsole_conf(), pfd, f, 0644, COPY_REFLINK);
|
||||
r = copy_file_atomic_at(AT_FDCWD, etc_vconsole_conf(), pfd, f, 0644, /* copy_flags= */ 0);
|
||||
if (r != -ENOENT) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy host's /etc/vconsole.conf: %m");
|
||||
|
||||
@@ -229,7 +229,6 @@ static int verb_import_fs(int argc, char *argv[], uintptr_t _data, void *userdat
|
||||
AT_FDCWD, dest,
|
||||
/* override_uid= */ UID_INVALID,
|
||||
/* override_gid= */ GID_INVALID,
|
||||
COPY_REFLINK|
|
||||
COPY_SAME_MOUNT|
|
||||
COPY_HARDLINKS|
|
||||
COPY_SIGINT|
|
||||
|
||||
@@ -319,7 +319,6 @@ static int raw_pull_copy_auxiliary_file(
|
||||
*path,
|
||||
local,
|
||||
0644,
|
||||
COPY_REFLINK |
|
||||
(FLAGS_SET(p->flags, IMPORT_FORCE) ? COPY_REPLACE : 0) |
|
||||
(FLAGS_SET(p->flags, IMPORT_SYNC) ? COPY_FSYNC_FULL : 0));
|
||||
else
|
||||
@@ -392,7 +391,7 @@ static int raw_pull_make_local_copy(RawPull *p) {
|
||||
* since it reduces fragmentation caused by not allowing in-place writes. */
|
||||
(void) import_set_nocow_and_log(dfd, tp);
|
||||
|
||||
r = copy_bytes(p->raw_job->disk_fd, dfd, UINT64_MAX, COPY_REFLINK);
|
||||
r = copy_bytes(p->raw_job->disk_fd, dfd, UINT64_MAX, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make writable copy of image: %m");
|
||||
|
||||
|
||||
@@ -343,7 +343,7 @@ static int tar_pull_make_local_copy(TarPull *p) {
|
||||
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
|
||||
BTRFS_SNAPSHOT_RECURSIVE);
|
||||
else
|
||||
r = copy_tree(p->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS, NULL, NULL);
|
||||
r = copy_tree(p->final_path, t, UID_INVALID, GID_INVALID, COPY_HARDLINKS, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create original download image: %m");
|
||||
}
|
||||
@@ -382,7 +382,6 @@ static int tar_pull_make_local_copy(TarPull *p) {
|
||||
p->settings_path,
|
||||
local_settings,
|
||||
0664,
|
||||
COPY_REFLINK |
|
||||
(FLAGS_SET(p->flags, IMPORT_FORCE) ? COPY_REPLACE : 0) |
|
||||
(FLAGS_SET(p->flags, IMPORT_SYNC) ? COPY_FSYNC_FULL : 0));
|
||||
else
|
||||
|
||||
@@ -567,7 +567,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
|
||||
|
||||
int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
const char *src, *dest, *host_path, *container_path;
|
||||
CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS;
|
||||
CopyFlags copy_flags = COPY_MERGE|COPY_HARDLINKS;
|
||||
Machine *m = ASSERT_PTR(userdata);
|
||||
Manager *manager = m->manager;
|
||||
bool copy_from;
|
||||
|
||||
@@ -966,7 +966,7 @@ int vl_method_copy_internal(sd_varlink *link, sd_json_variant *parameters, sd_va
|
||||
const char *dest = p.dest ?: p.src;
|
||||
const char *container_path = copy_from ? p.src : dest;
|
||||
const char *host_path = copy_from ? dest : p.src;
|
||||
CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS;
|
||||
CopyFlags copy_flags = COPY_MERGE|COPY_HARDLINKS;
|
||||
copy_flags |= p.replace ? COPY_REPLACE : 0;
|
||||
|
||||
Machine *machine;
|
||||
|
||||
+4
-4
@@ -1877,7 +1877,7 @@ static int setup_timezone(const char *dest) {
|
||||
|
||||
case TIMEZONE_COPY:
|
||||
/* If mounting failed, try to copy */
|
||||
r = copy_file_atomic("/etc/localtime", where, 0644, COPY_REFLINK|COPY_REPLACE);
|
||||
r = copy_file_atomic("/etc/localtime", where, 0644, COPY_REPLACE);
|
||||
if (r < 0) {
|
||||
log_full_errno(ERRNO_IS_NEG_FS_WRITE_REFUSED(r) ? LOG_DEBUG : LOG_WARNING, r,
|
||||
"Failed to copy /etc/localtime to %s, ignoring: %m", where);
|
||||
@@ -2005,9 +2005,9 @@ static int setup_resolv_conf(const char *dest) {
|
||||
}
|
||||
|
||||
if (IN_SET(m, RESOLV_CONF_REPLACE_HOST, RESOLV_CONF_REPLACE_STATIC, RESOLV_CONF_REPLACE_UPLINK, RESOLV_CONF_REPLACE_STUB))
|
||||
r = copy_file_atomic(what, where, 0644, COPY_REFLINK|COPY_REPLACE);
|
||||
r = copy_file_atomic(what, where, 0644, COPY_REPLACE);
|
||||
else
|
||||
r = copy_file(what, where, O_TRUNC|O_NOFOLLOW, 0644, COPY_REFLINK);
|
||||
r = copy_file(what, where, O_TRUNC|O_NOFOLLOW, 0644, /* copy_flags= */ 0);
|
||||
if (r < 0) {
|
||||
/* If the file already exists as symlink, let's suppress the warning, under the assumption that
|
||||
* resolved or something similar runs inside and the symlink points there.
|
||||
@@ -6474,7 +6474,7 @@ static int run(int argc, char *argv[]) {
|
||||
{
|
||||
BLOCK_SIGNALS(SIGINT);
|
||||
r = copy_file(arg_image, np, O_EXCL, arg_read_only ? 0400 : 0600,
|
||||
COPY_REFLINK|COPY_CRTIME|COPY_SIGINT|COPY_NOCOW_AFTER);
|
||||
COPY_CRTIME|COPY_SIGINT|COPY_NOCOW_AFTER);
|
||||
}
|
||||
if (r == -EINTR) {
|
||||
log_error_errno(r, "Interrupted while copying image file to %s, removed again.", np);
|
||||
|
||||
@@ -1610,7 +1610,7 @@ static int install_profile_dropin(
|
||||
return -ENOMEM;
|
||||
|
||||
if (flags & PORTABLE_PREFER_COPY) {
|
||||
CopyFlags copy_flags = COPY_REFLINK|COPY_FSYNC;
|
||||
CopyFlags copy_flags = COPY_FSYNC;
|
||||
|
||||
if (flags & PORTABLE_FORCE_ATTACH)
|
||||
copy_flags |= COPY_REPLACE;
|
||||
@@ -1754,7 +1754,7 @@ static int attach_unit_file(
|
||||
if (fd < 0)
|
||||
return log_debug_errno(fd, "Failed to create unit file '%s': %m", path);
|
||||
|
||||
r = copy_bytes(m->fd, fd, UINT64_MAX, COPY_REFLINK);
|
||||
r = copy_bytes(m->fd, fd, UINT64_MAX, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to copy unit file '%s': %m", path);
|
||||
|
||||
@@ -1883,7 +1883,7 @@ static int install_image(
|
||||
target,
|
||||
UID_INVALID,
|
||||
GID_INVALID,
|
||||
COPY_REFLINK | COPY_FSYNC | COPY_FSYNC_FULL | COPY_SYNCFS,
|
||||
COPY_FSYNC | COPY_FSYNC_FULL | COPY_SYNCFS,
|
||||
/* denylist= */ NULL,
|
||||
/* subvolumes= */ NULL);
|
||||
if (r < 0)
|
||||
|
||||
+13
-5
@@ -2129,7 +2129,7 @@ static int config_parse_copy_files(
|
||||
if (!isempty(p))
|
||||
return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Too many arguments: %s", rvalue);
|
||||
|
||||
CopyFlags flags = COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE|COPY_RESTORE_DIRECTORY_TIMESTAMPS;
|
||||
CopyFlags flags = COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE|COPY_RESTORE_DIRECTORY_TIMESTAMPS;
|
||||
for (const char *opts = options;;) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
const char *val;
|
||||
@@ -5382,7 +5382,7 @@ static int partition_target_sync(Context *context, Partition *p, PartitionTarget
|
||||
"Partition %" PRIu64 "'s contents (%s) don't fit in the partition (%s).",
|
||||
p->partno, FORMAT_BYTES(st.st_size), FORMAT_BYTES(p->new_size));
|
||||
|
||||
r = copy_bytes(t->fd, whole_fd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_FSYNC|COPY_SEEK0_SOURCE);
|
||||
r = copy_bytes(t->fd, whole_fd, UINT64_MAX, COPY_HOLES|COPY_FSYNC|COPY_SEEK0_SOURCE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy bytes to partition: %m");
|
||||
} else {
|
||||
@@ -6371,7 +6371,15 @@ static int context_copy_blocks(Context *context) {
|
||||
return log_error_errno(errno, "Failed to seek to copy blocks offset in %s: %m", p->copy_blocks_path);
|
||||
}
|
||||
|
||||
r = copy_bytes_full(p->copy_blocks_fd, partition_target_fd(t), p->copy_blocks_size, COPY_REFLINK, /* ret_remains= */ NULL, /* ret_remains_size= */ NULL, progress_bytes, p);
|
||||
r = copy_bytes_full(
|
||||
p->copy_blocks_fd,
|
||||
partition_target_fd(t),
|
||||
p->copy_blocks_size,
|
||||
/* copy_flags= */ 0,
|
||||
/* ret_remains= */ NULL,
|
||||
/* ret_remains_size= */ NULL,
|
||||
progress_bytes,
|
||||
p);
|
||||
clear_progress_bar(/* prefix= */ NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path);
|
||||
@@ -6895,7 +6903,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
|
||||
if (tfd < 0)
|
||||
return log_error_errno(errno, "Failed to create target file '%s': %m", line->target);
|
||||
|
||||
r = copy_bytes(sfd, tfd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_SIGINT|COPY_TRUNCATE);
|
||||
r = copy_bytes(sfd, tfd, UINT64_MAX, COPY_HOLES|COPY_SIGINT|COPY_TRUNCATE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", line->source, strempty(arg_copy_source), line->target);
|
||||
|
||||
@@ -8168,7 +8176,7 @@ static int context_split(Context *context) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write to split partition %s: %m", p->split_path);
|
||||
} else {
|
||||
r = copy_bytes(fd, fdt, p->new_size, COPY_REFLINK|COPY_HOLES|COPY_TRUNCATE);
|
||||
r = copy_bytes(fd, fdt, p->new_size, COPY_HOLES|COPY_TRUNCATE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy to split partition %s: %m", p->split_path);
|
||||
}
|
||||
|
||||
+1
-1
@@ -608,7 +608,7 @@ static int verb_sign(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
if (!certificate_table)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "File lacks certificate table.");
|
||||
|
||||
r = copy_bytes(srcfd, dstfd, UINT64_MAX, COPY_REFLINK);
|
||||
r = copy_bytes(srcfd, dstfd, UINT64_MAX, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy %s to %s: %m", argv[1], tmp);
|
||||
|
||||
|
||||
@@ -1471,7 +1471,6 @@ int btrfs_subvol_snapshot_at_full(
|
||||
/* override_uid= */ UID_INVALID,
|
||||
/* override_gid= */ GID_INVALID,
|
||||
COPY_MERGE_EMPTY|
|
||||
COPY_REFLINK|
|
||||
COPY_SAME_MOUNT|
|
||||
COPY_HARDLINKS|
|
||||
COPY_ALL_XATTRS|
|
||||
|
||||
+68
-71
@@ -130,6 +130,68 @@ static int create_hole(int fd, off_t size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns positive if the range was cloned, zero if cloning is unavailable, and negative if finalizing a
|
||||
* successful clone failed. If the range was cloned, returns the copy_bytes_full() result in ret_copy_result. */
|
||||
static int try_reflink_copy_bytes(
|
||||
int fdf,
|
||||
int fdt,
|
||||
uint64_t max_bytes,
|
||||
CopyFlags copy_flags,
|
||||
int *ret_copy_result) {
|
||||
|
||||
off_t foffset, toffset;
|
||||
int r;
|
||||
|
||||
assert(fdf >= 0);
|
||||
assert(fdt >= 0);
|
||||
assert(ret_copy_result);
|
||||
|
||||
if (max_bytes == 0)
|
||||
return 0;
|
||||
|
||||
foffset = FLAGS_SET(copy_flags, COPY_SEEK0_SOURCE) ? 0 : lseek(fdf, 0, SEEK_CUR);
|
||||
if (foffset < 0)
|
||||
return 0;
|
||||
|
||||
toffset = FLAGS_SET(copy_flags, COPY_SEEK0_TARGET) ? 0 : lseek(fdt, 0, SEEK_CUR);
|
||||
if (toffset < 0)
|
||||
return 0;
|
||||
|
||||
r = reflink_range(fdf, foffset, fdt, toffset, max_bytes == UINT64_MAX ? 0 : max_bytes);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
|
||||
if (max_bytes == UINT64_MAX) {
|
||||
off_t end;
|
||||
|
||||
end = lseek(fdf, 0, SEEK_END);
|
||||
if (end < 0)
|
||||
return -errno;
|
||||
if (end < foffset)
|
||||
return -ESPIPE;
|
||||
|
||||
if (lseek(fdt, toffset + (end - foffset), SEEK_SET) < 0)
|
||||
return -errno;
|
||||
|
||||
*ret_copy_result = 0; /* We copied to EOF. */
|
||||
} else {
|
||||
if (lseek(fdf, foffset + max_bytes, SEEK_SET) < 0)
|
||||
return -errno;
|
||||
if (lseek(fdt, toffset + max_bytes, SEEK_SET) < 0)
|
||||
return -errno;
|
||||
|
||||
*ret_copy_result = 1; /* We copied the requested range. */
|
||||
}
|
||||
|
||||
if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
|
||||
r = fd_verify_linked(fdf);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int copy_bytes_full(
|
||||
int fdf, int fdt,
|
||||
uint64_t max_bytes,
|
||||
@@ -177,77 +239,12 @@ int copy_bytes_full(
|
||||
lseek(fdt, 0, SEEK_SET) < 0)
|
||||
return -errno;
|
||||
|
||||
/* Try btrfs reflinks first. This only works on regular, seekable files, hence let's check the file offsets of
|
||||
* source and destination first. */
|
||||
if ((copy_flags & COPY_REFLINK)) {
|
||||
off_t foffset;
|
||||
|
||||
/* In reflink mode, we need to know the current file offset, unless we already sought to 0 anyway. */
|
||||
foffset = FLAGS_SET(copy_flags, COPY_SEEK0_SOURCE) ? 0 : lseek(fdf, 0, SEEK_CUR);
|
||||
if (foffset >= 0) {
|
||||
off_t toffset;
|
||||
|
||||
toffset = FLAGS_SET(copy_flags, COPY_SEEK0_TARGET) ? 0 : lseek(fdt, 0, SEEK_CUR);
|
||||
if (toffset >= 0) {
|
||||
|
||||
if (foffset == 0 && toffset == 0 && max_bytes == UINT64_MAX)
|
||||
r = reflink(fdf, fdt); /* full file reflink */
|
||||
else
|
||||
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) {
|
||||
|
||||
/* We cloned to the end of the source file, let's position the read
|
||||
* pointer there, and query it at the same time. */
|
||||
t = lseek(fdf, 0, SEEK_END);
|
||||
if (t < 0)
|
||||
return -errno;
|
||||
if (t < foffset)
|
||||
return -ESPIPE;
|
||||
|
||||
/* Let's adjust the destination file write pointer by the same number
|
||||
* of bytes. */
|
||||
t = lseek(fdt, toffset + (t - foffset), SEEK_SET);
|
||||
if (t < 0)
|
||||
return -errno;
|
||||
|
||||
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)
|
||||
return -errno;
|
||||
|
||||
t = lseek(fdt, toffset + max_bytes, SEEK_SET);
|
||||
if (t < 0)
|
||||
return -errno;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int reflink_result = 0;
|
||||
r = try_reflink_copy_bytes(fdf, fdt, max_bytes, copy_flags, &reflink_result);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return reflink_result;
|
||||
|
||||
usec_t start_timestamp = USEC_INFINITY;
|
||||
if (progress)
|
||||
|
||||
+24
-25
@@ -4,26 +4,25 @@
|
||||
#include "forward.h"
|
||||
|
||||
typedef enum CopyFlags {
|
||||
COPY_REFLINK = 1 << 0, /* Try to reflink */
|
||||
COPY_MERGE = 1 << 1, /* Merge existing trees with our new one to copy */
|
||||
COPY_REPLACE = 1 << 2, /* Replace an existing file if there's one */
|
||||
COPY_SAME_MOUNT = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */
|
||||
COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */
|
||||
COPY_CRTIME = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
|
||||
COPY_SIGINT = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
|
||||
COPY_SIGTERM = 1 << 7, /* ditto, but for SIGTERM */
|
||||
COPY_MAC_CREATE = 1 << 8, /* Create files with the correct MAC label (currently SELinux only) */
|
||||
COPY_HARDLINKS = 1 << 9, /* Try to reproduce hard links */
|
||||
COPY_FSYNC = 1 << 10, /* fsync() after we are done */
|
||||
COPY_FSYNC_FULL = 1 << 11, /* fsync_full() after we are done */
|
||||
COPY_SYNCFS = 1 << 12, /* syncfs() the *top-level* dir after we are done */
|
||||
COPY_ALL_XATTRS = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
|
||||
COPY_HOLES = 1 << 14, /* Copy holes */
|
||||
COPY_GRACEFUL_WARN = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */
|
||||
COPY_TRUNCATE = 1 << 16, /* Truncate to current file offset after copying */
|
||||
COPY_LOCK_BSD = 1 << 17, /* Return a BSD exclusively locked file descriptor referring to the copied image/directory. */
|
||||
COPY_VERIFY_LINKED = 1 << 18, /* Check the source file is still linked after copying. */
|
||||
COPY_RESTORE_DIRECTORY_TIMESTAMPS = 1 << 19, /* Make sure existing directory timestamps don't change during copying. */
|
||||
COPY_MERGE = 1 << 0, /* Merge existing trees with our new one to copy */
|
||||
COPY_REPLACE = 1 << 1, /* Replace an existing file if there's one */
|
||||
COPY_SAME_MOUNT = 1 << 2, /* Don't descend recursively into other file systems, across mount point boundaries */
|
||||
COPY_MERGE_EMPTY = 1 << 3, /* Merge an existing, empty directory with our new tree to copy */
|
||||
COPY_CRTIME = 1 << 4, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
|
||||
COPY_SIGINT = 1 << 5, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
|
||||
COPY_SIGTERM = 1 << 6, /* ditto, but for SIGTERM */
|
||||
COPY_MAC_CREATE = 1 << 7, /* Create files with the correct MAC label (currently SELinux only) */
|
||||
COPY_HARDLINKS = 1 << 8, /* Try to reproduce hard links */
|
||||
COPY_FSYNC = 1 << 9, /* fsync() after we are done */
|
||||
COPY_FSYNC_FULL = 1 << 10, /* fsync_full() after we are done */
|
||||
COPY_SYNCFS = 1 << 11, /* syncfs() the *top-level* dir after we are done */
|
||||
COPY_ALL_XATTRS = 1 << 12, /* Preserve all xattrs when copying, not just those in the user namespace */
|
||||
COPY_HOLES = 1 << 13, /* Copy holes */
|
||||
COPY_GRACEFUL_WARN = 1 << 14, /* Skip copying file types that aren't supported by the target filesystem */
|
||||
COPY_TRUNCATE = 1 << 15, /* Truncate to current file offset after copying */
|
||||
COPY_LOCK_BSD = 1 << 16, /* Return a BSD exclusively locked file descriptor referring to the copied image/directory. */
|
||||
COPY_VERIFY_LINKED = 1 << 17, /* Check the source file is still linked after copying. */
|
||||
COPY_RESTORE_DIRECTORY_TIMESTAMPS = 1 << 18, /* Make sure existing directory timestamps don't change during copying. */
|
||||
/* A root image might be subject to lots of random writes so we provide a flag to try to disable COW
|
||||
* on a copied file which tends to not perform well in combination with lots of random writes.
|
||||
*
|
||||
@@ -31,11 +30,11 @@ typedef enum CopyFlags {
|
||||
* least makes the intention clear. We don't want to unconditionally set the flag before doing the
|
||||
* copy because reflinking from COW to NOCOW files is not supported.
|
||||
*/
|
||||
COPY_NOCOW_AFTER = 1 << 20,
|
||||
COPY_PRESERVE_FS_VERITY = 1 << 21, /* Preserve fs-verity when copying. */
|
||||
COPY_MERGE_APPLY_STAT = 1 << 22, /* When we reuse an existing directory inode, apply source ownership/mode/xattrs/timestamps */
|
||||
COPY_SEEK0_SOURCE = 1 << 23, /* Seek back to start of source file before copying */
|
||||
COPY_SEEK0_TARGET = 1 << 24, /* Seek back to start of target file before copying */
|
||||
COPY_NOCOW_AFTER = 1 << 19,
|
||||
COPY_PRESERVE_FS_VERITY = 1 << 20, /* Preserve fs-verity when copying. */
|
||||
COPY_MERGE_APPLY_STAT = 1 << 21, /* When we reuse an existing directory inode, apply source ownership/mode/xattrs/timestamps */
|
||||
COPY_SEEK0_SOURCE = 1 << 22, /* Seek back to start of source file before copying */
|
||||
COPY_SEEK0_TARGET = 1 << 23, /* Seek back to start of target file before copying */
|
||||
} CopyFlags;
|
||||
|
||||
typedef enum DenyType {
|
||||
|
||||
@@ -50,7 +50,7 @@ int copy_data_fd(int fd) {
|
||||
if (copy_fd < 0)
|
||||
return copy_fd;
|
||||
|
||||
r = copy_bytes(fd, copy_fd, DATA_FD_MEMORY_LIMIT, COPY_REFLINK);
|
||||
r = copy_bytes(fd, copy_fd, DATA_FD_MEMORY_LIMIT, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -80,14 +80,14 @@ int copy_data_fd(int fd) {
|
||||
/* If we tried a memfd first and it ended up being too large, then copy this into the
|
||||
* temporary file first. */
|
||||
|
||||
r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
|
||||
r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(r == 0);
|
||||
}
|
||||
|
||||
r = copy_bytes(fd, tmp_fd, DATA_FD_TMP_LIMIT - DATA_FD_MEMORY_LIMIT, COPY_REFLINK);
|
||||
r = copy_bytes(fd, tmp_fd, DATA_FD_TMP_LIMIT - DATA_FD_MEMORY_LIMIT, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
@@ -115,7 +115,7 @@ int copy_data_fd(int fd) {
|
||||
if (copy_fd >= 0) {
|
||||
/* If we tried a memfd first, or a file in /tmp/, and it ended up being too large, than copy this
|
||||
* into the temporary file first. */
|
||||
r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
|
||||
r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -123,7 +123,7 @@ int copy_data_fd(int fd) {
|
||||
}
|
||||
|
||||
/* Copy in the rest */
|
||||
r = copy_bytes(fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
|
||||
r = copy_bytes(fd, tmp_fd, UINT64_MAX, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -164,7 +164,7 @@ int memfd_clone_fd(int fd, const char *name, int mode) {
|
||||
if (mfd < 0)
|
||||
return mfd;
|
||||
|
||||
r = copy_bytes(fd, mfd, UINT64_MAX, COPY_REFLINK);
|
||||
r = copy_bytes(fd, mfd, UINT64_MAX, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -1625,7 +1625,7 @@ static int clone_auxiliary_file(const char *path, const char *new_name, const ch
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return copy_file_atomic(path, rs, 0664, COPY_REFLINK);
|
||||
return copy_file_atomic(path, rs, 0664, /* copy_flags= */ 0);
|
||||
}
|
||||
|
||||
static int get_pool_directory(
|
||||
@@ -1805,7 +1805,7 @@ int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope sco
|
||||
return r;
|
||||
|
||||
r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644,
|
||||
COPY_REFLINK|COPY_CRTIME|COPY_NOCOW_AFTER);
|
||||
COPY_CRTIME|COPY_NOCOW_AFTER);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -5611,7 +5611,7 @@ int copy_tree_at_foreign(int source_fd, int target_fd, int userns_fd) {
|
||||
target_fd, /* to= */ NULL,
|
||||
/* override_uid= */ UID_INVALID,
|
||||
/* override_gid= */ GID_INVALID,
|
||||
COPY_REFLINK|COPY_HARDLINKS|COPY_MERGE_EMPTY|COPY_MERGE_APPLY_STAT|COPY_SAME_MOUNT|COPY_ALL_XATTRS,
|
||||
COPY_HARDLINKS|COPY_MERGE_EMPTY|COPY_MERGE_APPLY_STAT|COPY_SAME_MOUNT|COPY_ALL_XATTRS,
|
||||
/* denylist= */ NULL,
|
||||
/* subvolumes= */ NULL);
|
||||
if (r < 0) {
|
||||
|
||||
@@ -183,7 +183,7 @@ static int populate_edit_temp_file(EditFile *e, FILE *f, const char *filename) {
|
||||
}
|
||||
}
|
||||
} else if (source) {
|
||||
r = copy_file_fd(source, fileno(f), COPY_REFLINK);
|
||||
r = copy_file_fd(source, fileno(f), /* copy_flags= */ 0);
|
||||
if (r < 0) {
|
||||
assert(r != -ENOENT);
|
||||
return log_error_errno(r, "Failed to copy file '%s' to temporary file '%s': %m",
|
||||
@@ -444,7 +444,7 @@ static int edit_file_install_one_stdin(EditFile *e, const char *contents, size_t
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create parent directories for '%s': %m", e->path);
|
||||
|
||||
r = copy_file_atomic_at(*fd, NULL, AT_FDCWD, e->path, 0644, COPY_REFLINK|COPY_REPLACE|COPY_MAC_CREATE);
|
||||
r = copy_file_atomic_at(*fd, NULL, AT_FDCWD, e->path, 0644, COPY_REPLACE|COPY_MAC_CREATE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy stdin contents to '%s': %m", e->path);
|
||||
|
||||
|
||||
@@ -333,7 +333,7 @@ static int make_backup(const char *target, const char *x) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = copy_bytes(src, fileno(dst), UINT64_MAX, COPY_REFLINK);
|
||||
r = copy_bytes(src, fileno(dst), UINT64_MAX, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
+44
-26
@@ -46,7 +46,7 @@ TEST(copy_file) {
|
||||
|
||||
assert_se(write_string_file(fn, "foo bar bar bar foo", WRITE_STRING_FILE_CREATE) == 0);
|
||||
|
||||
assert_se(copy_file(fn, fn_copy, 0, 0644, COPY_REFLINK) == 0);
|
||||
assert_se(copy_file(fn, fn_copy, 0, 0644, /* copy_flags= */ 0) == 0);
|
||||
|
||||
assert_se(read_full_file(fn_copy, &buf, &sz) == 0);
|
||||
ASSERT_STREQ(buf, "foo bar bar bar foo\n");
|
||||
@@ -71,11 +71,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, NULL, NULL) == -EEXIST);
|
||||
assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, /* copy_flags= */ 0, NULL, 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, NULL, NULL) == 0);
|
||||
assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REPLACE, NULL, NULL) == 0);
|
||||
|
||||
assert_se(read_file_at_and_streq(AT_FDCWD, dst, "bar bar\n"));
|
||||
}
|
||||
@@ -96,14 +96,21 @@ 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, NULL, NULL) == -EEXIST);
|
||||
assert_se(copy_tree_at(
|
||||
src, ".",
|
||||
dst, ".",
|
||||
UID_INVALID,
|
||||
GID_INVALID,
|
||||
/* copy_flags= */ 0,
|
||||
/* denylist= */ NULL,
|
||||
/* subvolumes= */ 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, NULL, NULL) == 0);
|
||||
assert_se(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REPLACE|COPY_MERGE, NULL, 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"));
|
||||
@@ -124,8 +131,8 @@ TEST(copy_file_fd) {
|
||||
assert_se(out_fd >= 0);
|
||||
|
||||
assert_se(write_string_file(in_fn, text, WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd, COPY_REFLINK) < 0);
|
||||
assert_se(copy_file_fd(in_fn, out_fd, COPY_REFLINK) >= 0);
|
||||
assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd, /* copy_flags= */ 0) < 0);
|
||||
assert_se(copy_file_fd(in_fn, out_fd, /* copy_flags= */ 0) >= 0);
|
||||
assert_se(lseek(out_fd, 0, SEEK_SET) == 0);
|
||||
|
||||
assert_se(read(out_fd, buf, sizeof buf) == (ssize_t) strlen(text));
|
||||
@@ -203,7 +210,7 @@ TEST(copy_tree) {
|
||||
assert_se(hashmap_ensure_put(&denylist, &inode_hash_ops, cp, INT_TO_PTR(DENY_CONTENTS)) >= 0);
|
||||
TAKE_PTR(cp);
|
||||
|
||||
assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS, denylist, NULL) == 0);
|
||||
assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_MERGE|COPY_HARDLINKS, denylist, NULL) == 0);
|
||||
|
||||
STRV_FOREACH(p, files) {
|
||||
_cleanup_free_ char *buf = NULL, *f = NULL, *c = NULL;
|
||||
@@ -255,8 +262,22 @@ 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, denylist, NULL) < 0);
|
||||
assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK, denylist, NULL) < 0);
|
||||
assert_se(copy_tree(
|
||||
original_dir,
|
||||
copy_dir,
|
||||
UID_INVALID,
|
||||
GID_INVALID,
|
||||
/* copy_flags= */ 0,
|
||||
denylist,
|
||||
/* subvolumes= */ NULL) < 0);
|
||||
assert_se(copy_tree(
|
||||
"/tmp/inexistent/foo/bar/fsdoi",
|
||||
copy_dir,
|
||||
UID_INVALID,
|
||||
GID_INVALID,
|
||||
/* copy_flags= */ 0,
|
||||
denylist,
|
||||
/* subvolumes= */ NULL) < 0);
|
||||
|
||||
ignorep = strjoina(copy_dir, "ignore/file");
|
||||
assert_se(RET_NERRNO(access(ignorep, F_OK)) == -ENOENT);
|
||||
@@ -411,14 +432,14 @@ TEST_RET(copy_bytes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_copy_bytes_regular_file_one(const char *src, bool try_reflink, uint64_t max_bytes) {
|
||||
static void test_copy_bytes_regular_file_one(const char *src, uint64_t max_bytes) {
|
||||
_cleanup_(unlink_tempfilep) char fn2[] = "/tmp/test-copy-file-XXXXXX";
|
||||
_cleanup_(unlink_tempfilep) char fn3[] = "/tmp/test-copy-file-XXXXXX";
|
||||
_cleanup_close_ int fd = -EBADF, fd2 = -EBADF, fd3 = -EBADF;
|
||||
int r;
|
||||
struct stat buf, buf2, buf3;
|
||||
|
||||
log_info("%s try_reflink=%s max_bytes=%" PRIu64, __func__, yes_no(try_reflink), max_bytes);
|
||||
log_info("%s max_bytes=%" PRIu64, __func__, max_bytes);
|
||||
|
||||
fd = open(src, O_CLOEXEC | O_PATH);
|
||||
assert_se(fd >= 0);
|
||||
@@ -429,7 +450,7 @@ static void test_copy_bytes_regular_file_one(const char *src, bool try_reflink,
|
||||
fd3 = mkostemp_safe(fn3);
|
||||
assert_se(fd3 >= 0);
|
||||
|
||||
r = copy_bytes(fd, fd2, max_bytes, try_reflink ? COPY_REFLINK : 0);
|
||||
r = copy_bytes(fd, fd2, max_bytes, /* copy_flags= */ 0);
|
||||
if (max_bytes == UINT64_MAX)
|
||||
assert_se(r == 0);
|
||||
else
|
||||
@@ -443,7 +464,7 @@ static void test_copy_bytes_regular_file_one(const char *src, bool try_reflink,
|
||||
/* Make sure the file is now higher than max_bytes */
|
||||
assert_se(ftruncate(fd2, max_bytes + 1) == 0);
|
||||
|
||||
r = copy_bytes(fd2, fd3, max_bytes, COPY_SEEK0_SOURCE | (try_reflink ? COPY_REFLINK : 0));
|
||||
r = copy_bytes(fd2, fd3, max_bytes, COPY_SEEK0_SOURCE);
|
||||
if (max_bytes == UINT64_MAX)
|
||||
assert_se(r == 0);
|
||||
else
|
||||
@@ -463,12 +484,9 @@ static void test_copy_bytes_regular_file_one(const char *src, bool try_reflink,
|
||||
}
|
||||
|
||||
TEST(copy_bytes_regular_file) {
|
||||
test_copy_bytes_regular_file_one(saved_argv[0], false, UINT64_MAX);
|
||||
test_copy_bytes_regular_file_one(saved_argv[0], true, UINT64_MAX);
|
||||
test_copy_bytes_regular_file_one(saved_argv[0], false, 1000); /* smaller than copy buffer size */
|
||||
test_copy_bytes_regular_file_one(saved_argv[0], true, 1000);
|
||||
test_copy_bytes_regular_file_one(saved_argv[0], false, 32000); /* larger than copy buffer size */
|
||||
test_copy_bytes_regular_file_one(saved_argv[0], true, 32000);
|
||||
test_copy_bytes_regular_file_one(saved_argv[0], UINT64_MAX);
|
||||
test_copy_bytes_regular_file_one(saved_argv[0], 1000); /* smaller than copy buffer size */
|
||||
test_copy_bytes_regular_file_one(saved_argv[0], 32000); /* larger than copy buffer size */
|
||||
}
|
||||
|
||||
TEST(copy_atomic) {
|
||||
@@ -480,11 +498,11 @@ TEST(copy_atomic) {
|
||||
|
||||
q = strjoina(p, "/fstab");
|
||||
|
||||
r = copy_file_atomic("/etc/fstab", q, 0644, COPY_REFLINK);
|
||||
r = copy_file_atomic("/etc/fstab", q, 0644, /* copy_flags= */ 0);
|
||||
if (r == -ENOENT || ERRNO_IS_PRIVILEGE(r))
|
||||
return;
|
||||
|
||||
assert_se(copy_file_atomic("/etc/fstab", q, 0644, COPY_REFLINK) == -EEXIST);
|
||||
assert_se(copy_file_atomic("/etc/fstab", q, 0644, /* copy_flags= */ 0) == -EEXIST);
|
||||
|
||||
assert_se(copy_file_atomic("/etc/fstab", q, 0644, COPY_REPLACE) >= 0);
|
||||
}
|
||||
@@ -746,7 +764,7 @@ TEST_RET(copy_with_verity) {
|
||||
assert_no_fsverity(dst, *file);
|
||||
|
||||
/* Copy *with* fs-verity enabled and make sure it works properly */
|
||||
int r = copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE|COPY_PRESERVE_FS_VERITY, NULL, NULL);
|
||||
int r = copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REPLACE|COPY_MERGE|COPY_PRESERVE_FS_VERITY, NULL, NULL);
|
||||
if (r == -ESOCKTNOSUPPORT)
|
||||
/* This can happen on some versions of btrfs, for example */
|
||||
return log_tests_skipped_errno(errno, "/var/tmp: fs-verity supported, but not reading metadata");
|
||||
@@ -768,14 +786,14 @@ TEST_RET(copy_with_verity) {
|
||||
|
||||
/* Copy from our non-verity filesystem into dst, requesting verity and making sure we notice that
|
||||
* we failed to read verity from the source. */
|
||||
ASSERT_ERROR(copy_tree_at(badsrc, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE|COPY_PRESERVE_FS_VERITY, NULL, NULL), ESOCKTNOSUPPORT);
|
||||
ASSERT_ERROR(copy_tree_at(badsrc, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REPLACE|COPY_MERGE|COPY_PRESERVE_FS_VERITY, NULL, NULL), ESOCKTNOSUPPORT);
|
||||
|
||||
/* Copy from our verity filesystem into our baddst, requesting verity and making sure we notice that
|
||||
* we failed to set verity on the destination. */
|
||||
ASSERT_ERROR(copy_tree_at(src, ".", baddst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE|COPY_PRESERVE_FS_VERITY, NULL, NULL), ESOCKTNOSUPPORT);
|
||||
ASSERT_ERROR(copy_tree_at(src, ".", baddst, ".", UID_INVALID, GID_INVALID, COPY_REPLACE|COPY_MERGE|COPY_PRESERVE_FS_VERITY, NULL, NULL), ESOCKTNOSUPPORT);
|
||||
|
||||
/* Of course this should fail too... */
|
||||
ASSERT_ERROR(copy_tree_at(badsrc, ".", baddst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE|COPY_PRESERVE_FS_VERITY, NULL, NULL), ESOCKTNOSUPPORT);
|
||||
ASSERT_ERROR(copy_tree_at(badsrc, ".", baddst, ".", UID_INVALID, GID_INVALID, COPY_REPLACE|COPY_MERGE|COPY_PRESERVE_FS_VERITY, NULL, NULL), ESOCKTNOSUPPORT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -434,7 +434,7 @@ TEST(conservative_rename) {
|
||||
assert_se(access(q, F_OK) < 0 && errno == ENOENT);
|
||||
|
||||
/* Check that a manual copy is detected */
|
||||
assert_se(copy_file(p, q, 0, MODE_INVALID, COPY_REFLINK) >= 0);
|
||||
assert_se(copy_file(p, q, 0, MODE_INVALID, /* copy_flags= */ 0) >= 0);
|
||||
assert_se(conservative_renameat(AT_FDCWD, q, AT_FDCWD, p) == 0);
|
||||
assert_se(access(q, F_OK) < 0 && errno == ENOENT);
|
||||
|
||||
|
||||
@@ -2236,7 +2236,7 @@ static int copy_files(Context *c, Item *i) {
|
||||
dfd, bn,
|
||||
i->uid_set ? i->uid : UID_INVALID,
|
||||
i->gid_set ? i->gid : GID_INVALID,
|
||||
COPY_REFLINK | ((i->append_or_force) ? COPY_MERGE : COPY_MERGE_EMPTY) | COPY_MAC_CREATE | COPY_HARDLINKS,
|
||||
((i->append_or_force) ? COPY_MERGE : COPY_MERGE_EMPTY) | COPY_MAC_CREATE | COPY_HARDLINKS,
|
||||
NULL, NULL);
|
||||
|
||||
fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
|
||||
|
||||
@@ -1505,7 +1505,7 @@ static int load_credential_one(
|
||||
return log_error_errno(errno, "Failed to chown %s: %m", hd);
|
||||
|
||||
r = copy_tree(user_record_skeleton_directory(ur), hd, ur->uid, user_record_gid(ur),
|
||||
COPY_REFLINK|COPY_MERGE, /* denylist= */ NULL, /* subvolumes= */ NULL);
|
||||
COPY_MERGE, /* denylist= */ NULL, /* subvolumes= */ NULL);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to copy skeleton directory to %s: %m", hd);
|
||||
}
|
||||
|
||||
@@ -2025,7 +2025,7 @@ static int merge_initrds(char **ret) {
|
||||
if (ifd < 0)
|
||||
return log_error_errno(errno, "Failed to open %s: %m", *i);
|
||||
|
||||
r = copy_bytes(ifd, ofd, UINT64_MAX, COPY_REFLINK);
|
||||
r = copy_bytes(ifd, ofd, UINT64_MAX, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy bytes from %s to %s: %m", *i, merged_initrd);
|
||||
}
|
||||
@@ -2254,7 +2254,7 @@ static int cmdline_add_ovmf(FILE *config_file, const OvmfConfig *ovmf_config, ch
|
||||
if (source_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open OVMF vars file %s: %m", vars_source);
|
||||
|
||||
r = copy_bytes(source_fd, target_fd, UINT64_MAX, COPY_REFLINK);
|
||||
r = copy_bytes(source_fd, target_fd, UINT64_MAX, /* copy_flags= */ 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy bytes from %s to %s: %m", vars_source, state);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user