1
0
mirror of https://github.com/systemd/systemd synced 2026-03-24 15:55:00 +01:00

Compare commits

...

11 Commits

Author SHA1 Message Date
Egor Ignatov
b10abe4bba time-set: adjust system clock if rtc is far in future 2021-08-02 20:33:01 +01:00
Luca Boccassi
f121bd7818
Merge pull request #20352 from poettering/copy-tweaks
various tweaks to copy.c
2021-08-02 20:31:42 +01:00
David Seifert
2b6c0bb2a3 Use correct <poll.h> include
* `<sys/poll.h>` is not specified in POSIX
2021-08-02 17:31:32 +01:00
Lennart Poettering
28ba7e36d0 btrfs-util: expose COPY_SIGTERM for btrfs_snapshot() too 2021-08-02 17:24:23 +02:00
Lennart Poettering
1ac404cac0 copy: add COPY_SIGTERM, matching the existing COPY_SIGINT 2021-08-02 17:24:18 +02:00
Lennart Poettering
864e406256 copy: add COPY_SYNCFS flag
When copying large directory trees it should be a better idea to sync
the whole fs once when we are done instead of individually for each
file, hence add COPY_SYNCFS.

As opposed to COPY_FSYNC/COPY_FSYNC_FULL this only really applies to the
top-level directory, after completion of the whole copy.
2021-08-02 17:24:09 +02:00
Lennart Poettering
06a40b52d9 copy: optionally fsync() files after copying them
As a safety precaution it makes sense to fsync() files after copying
them, and maybe even the directories they are contained in. Let's add a
flag for these two cases.
2021-08-02 17:24:03 +02:00
Lennart Poettering
2f78204498 copy: tighten destination checks when copying files
let's make sure we only operate on regular files when copying files.

Also, make sure to copy file attributes only over if target is a regular
file (so that copying a file to /dev/null won't alter the access
mode/ownership of that device node...)
2021-08-02 17:23:58 +02:00
Lennart Poettering
5c9d961e79 copy: move to single clean-up path
(This might not look like a big improvement, but will shortly, when we
add fsync() support to the copy logic, at which point there are more
error paths we can unify that way.)

While we are at it, tweak a clean-up path: only unlink a copied file if
we are definitely the ones who created them, i.e. if O_EXCL is set.
2021-08-02 17:23:52 +02:00
David Tardon
feac9a1d1b machined-varlink: fix double free
Fixes: #18599
2021-08-02 16:19:17 +01:00
David Seifert
f8d54f7810 Use correct <fcntl.h> include
* `<sys/fcntl.h>` is not specified in POSIX
2021-08-02 14:51:50 +02:00
13 changed files with 274 additions and 71 deletions

View File

@ -722,6 +722,8 @@ if time_epoch == -1
endif endif
conf.set('TIME_EPOCH', time_epoch) conf.set('TIME_EPOCH', time_epoch)
conf.set('CLOCK_VALID_RANGE_USEC_MAX', get_option('clock-valid-range-usec-max'))
foreach tuple : [['system-alloc-uid-min', 'SYS_UID_MIN', 1], # Also see login.defs(5). foreach tuple : [['system-alloc-uid-min', 'SYS_UID_MIN', 1], # Also see login.defs(5).
['system-uid-max', 'SYS_UID_MAX', 999], ['system-uid-max', 'SYS_UID_MAX', 999],
['system-alloc-gid-min', 'SYS_GID_MIN', 1], ['system-alloc-gid-min', 'SYS_GID_MIN', 1],

View File

@ -208,6 +208,8 @@ option('status-unit-format-default', type : 'combo',
description : 'use unit name or description in messages by default') description : 'use unit name or description in messages by default')
option('time-epoch', type : 'integer', value : '-1', option('time-epoch', type : 'integer', value : '-1',
description : 'time epoch for time clients') description : 'time epoch for time clients')
option('clock-valid-range-usec-max', type : 'integer', value : '473364000000000', # 15 years
description : 'maximum value in microseconds for the difference between RTC and epoch, exceeding which is considered an RTC error')
option('system-alloc-uid-min', type : 'integer', value : '-1', option('system-alloc-uid-min', type : 'integer', value : '-1',
description : 'minimum system UID used when allocating') description : 'minimum system UID used when allocating')

View File

@ -2,11 +2,11 @@
#pragma once #pragma once
#include <dirent.h> #include <dirent.h>
#include <fcntl.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/fcntl.h>
#include <sys/types.h> #include <sys/types.h>
#include "macro.h" #include "macro.h"

View File

@ -83,6 +83,7 @@
#include "switch-root.h" #include "switch-root.h"
#include "sysctl-util.h" #include "sysctl-util.h"
#include "terminal-util.h" #include "terminal-util.h"
#include "time-util.h"
#include "umask-util.h" #include "umask-util.h"
#include "user-util.h" #include "user-util.h"
#include "util.h" #include "util.h"
@ -1598,11 +1599,18 @@ static void initialize_clock(void) {
*/ */
(void) clock_reset_timewarp(); (void) clock_reset_timewarp();
r = clock_apply_epoch(); ClockChangeDirection change_dir;
if (r < 0) r = clock_apply_epoch(&change_dir);
log_error_errno(r, "Current system time is before build time, but cannot correct: %m"); if (r > 0 && change_dir == CLOCK_CHANGE_FORWARD)
else if (r > 0)
log_info("System time before build time, advancing clock."); log_info("System time before build time, advancing clock.");
else if (r > 0 && change_dir == CLOCK_CHANGE_BACKWARD)
log_info("System time is further ahead than %s after build time, resetting clock to build time.",
FORMAT_TIMESPAN(CLOCK_VALID_RANGE_USEC_MAX, USEC_PER_DAY));
else if (r < 0 && change_dir == CLOCK_CHANGE_FORWARD)
log_error_errno(r, "Current system time is before build time, but cannot correct: %m");
else if (r < 0 && change_dir == CLOCK_CHANGE_BACKWARD)
log_error_errno(r, "Current system time is further ahead %s after build time, but cannot correct: %m",
FORMAT_TIMESPAN(CLOCK_VALID_RANGE_USEC_MAX, USEC_PER_DAY));
} }
static void apply_clock_update(void) { static void apply_clock_update(void) {

View File

@ -297,7 +297,7 @@ static int group_lookup_name(Manager *m, const char *name, gid_t *ret_gid, char
desc = mfree(desc); desc = mfree(desc);
*ret_gid = converted_gid; *ret_gid = converted_gid;
*ret_description = desc; *ret_description = TAKE_PTR(desc);
return 0; return 0;
} }

View File

@ -1613,7 +1613,7 @@ int btrfs_subvol_snapshot_fd_full(
return -EISDIR; return -EISDIR;
r = btrfs_subvol_make(new_path); r = btrfs_subvol_make(new_path);
if (r == -ENOTTY && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) { if (ERRNO_IS_NOT_SUPPORTED(r) && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) {
/* If the destination doesn't support subvolumes, then use a plain directory, if that's requested. */ /* If the destination doesn't support subvolumes, then use a plain directory, if that's requested. */
if (mkdir(new_path, 0755) < 0) if (mkdir(new_path, 0755) < 0)
return -errno; return -errno;
@ -1624,8 +1624,15 @@ int btrfs_subvol_snapshot_fd_full(
r = copy_directory_fd_full( r = copy_directory_fd_full(
old_fd, new_path, old_fd, new_path,
COPY_MERGE|COPY_REFLINK|COPY_SAME_MOUNT|COPY_HARDLINKS|(FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGINT) ? COPY_SIGINT : 0), COPY_MERGE_EMPTY|
progress_path, progress_bytes, userdata); COPY_REFLINK|
COPY_SAME_MOUNT|
COPY_HARDLINKS|
(FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGINT) ? COPY_SIGINT : 0)|
(FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGTERM) ? COPY_SIGTERM : 0),
progress_path,
progress_bytes,
userdata);
if (r < 0) if (r < 0)
goto fallback_fail; goto fallback_fail;

View File

@ -35,6 +35,7 @@ typedef enum BtrfsSnapshotFlags {
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY = 1 << 4, /* If the destination doesn't support subvolumes, reflink/copy instead */ BTRFS_SNAPSHOT_FALLBACK_DIRECTORY = 1 << 4, /* If the destination doesn't support subvolumes, reflink/copy instead */
BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE = 1 << 5, /* When we can't create a subvolume, use the FS_IMMUTABLE attribute for indicating read-only */ BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE = 1 << 5, /* When we can't create a subvolume, use the FS_IMMUTABLE attribute for indicating read-only */
BTRFS_SNAPSHOT_SIGINT = 1 << 6, /* Check for SIGINT regularly, and return EINTR if seen */ BTRFS_SNAPSHOT_SIGINT = 1 << 6, /* Check for SIGINT regularly, and return EINTR if seen */
BTRFS_SNAPSHOT_SIGTERM = 1 << 7, /* Ditto, but for SIGTERM */
} BtrfsSnapshotFlags; } BtrfsSnapshotFlags;
typedef enum BtrfsRemoveFlags { typedef enum BtrfsRemoveFlags {

View File

@ -139,10 +139,15 @@ int clock_reset_timewarp(void) {
#define EPOCH_FILE "/usr/lib/clock-epoch" #define EPOCH_FILE "/usr/lib/clock-epoch"
int clock_apply_epoch(void) { int clock_apply_epoch(ClockChangeDirection *ret_attempted_change) {
struct stat st; struct stat st;
struct timespec ts; struct timespec ts;
usec_t epoch_usec; usec_t epoch_usec, now_usec;
/* NB: we update *ret_attempted_change in *all* cases, both
* on success and failure, to indicate what we intended to do! */
assert(ret_attempted_change);
if (stat(EPOCH_FILE, &st) < 0) { if (stat(EPOCH_FILE, &st) < 0) {
if (errno != ENOENT) if (errno != ENOENT)
@ -152,8 +157,15 @@ int clock_apply_epoch(void) {
} else } else
epoch_usec = timespec_load(&st.st_mtim); epoch_usec = timespec_load(&st.st_mtim);
if (now(CLOCK_REALTIME) >= epoch_usec) now_usec = now(CLOCK_REALTIME);
if (now_usec < epoch_usec)
*ret_attempted_change = CLOCK_CHANGE_FORWARD;
else if (now_usec > usec_add(epoch_usec, CLOCK_VALID_RANGE_USEC_MAX))
*ret_attempted_change = CLOCK_CHANGE_BACKWARD;
else {
*ret_attempted_change = CLOCK_CHANGE_NOOP;
return 0; return 0;
}
if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, epoch_usec)) < 0) if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, epoch_usec)) < 0)
return -errno; return -errno;

View File

@ -1,11 +1,20 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once #pragma once
#include <errno.h>
#include <time.h> #include <time.h>
typedef enum ClockChangeDirection {
CLOCK_CHANGE_NOOP,
CLOCK_CHANGE_FORWARD,
CLOCK_CHANGE_BACKWARD,
_CLOCK_CHANGE_MAX,
_CLOCK_CHANGE_INVALID = -EINVAL,
} ClockChangeDirection;
int clock_is_localtime(const char* adjtime_path); int clock_is_localtime(const char* adjtime_path);
int clock_set_timezone(int *ret_minutesdelta); int clock_set_timezone(int *ret_minutesdelta);
int clock_reset_timewarp(void); int clock_reset_timewarp(void);
int clock_get_hwclock(struct tm *tm); int clock_get_hwclock(struct tm *tm);
int clock_set_hwclock(const struct tm *tm); int clock_set_hwclock(const struct tm *tm);
int clock_apply_epoch(void); int clock_apply_epoch(ClockChangeDirection *ret_attempted_change);

View File

@ -88,6 +88,23 @@ static int fd_is_nonblock_pipe(int fd) {
return FLAGS_SET(flags, O_NONBLOCK) ? FD_IS_NONBLOCKING_PIPE : FD_IS_BLOCKING_PIPE; return FLAGS_SET(flags, O_NONBLOCK) ? FD_IS_NONBLOCKING_PIPE : FD_IS_BLOCKING_PIPE;
} }
static int look_for_signals(CopyFlags copy_flags) {
int r;
if ((copy_flags & (COPY_SIGINT|COPY_SIGTERM)) == 0)
return 0;
r = pop_pending_signal(copy_flags & COPY_SIGINT ? SIGINT : 0,
copy_flags & COPY_SIGTERM ? SIGTERM : 0);
if (r < 0)
return r;
if (r != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EINTR),
"Got %s, cancelling copy operation.", signal_to_string(r));
return 0;
}
int copy_bytes_full( int copy_bytes_full(
int fdf, int fdt, int fdf, int fdt,
uint64_t max_bytes, uint64_t max_bytes,
@ -176,13 +193,9 @@ int copy_bytes_full(
if (max_bytes <= 0) if (max_bytes <= 0)
return 1; /* return > 0 if we hit the max_bytes limit */ return 1; /* return > 0 if we hit the max_bytes limit */
if (FLAGS_SET(copy_flags, COPY_SIGINT)) { r = look_for_signals(copy_flags);
r = pop_pending_signal(SIGINT);
if (r < 0) if (r < 0)
return r; return r;
if (r > 0)
return -EINTR;
}
if (max_bytes != UINT64_MAX && m > max_bytes) if (max_bytes != UINT64_MAX && m > max_bytes)
m = max_bytes; m = max_bytes;
@ -627,10 +640,8 @@ static int fd_copy_regular(
return -errno; return -errno;
r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress, userdata); r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress, userdata);
if (r < 0) { if (r < 0)
(void) unlinkat(dt, to, 0); goto fail;
return r;
}
if (fchown(fdt, if (fchown(fdt,
uid_is_valid(override_uid) ? override_uid : st->st_uid, uid_is_valid(override_uid) ? override_uid : st->st_uid,
@ -643,16 +654,25 @@ static int fd_copy_regular(
(void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim }); (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
(void) copy_xattr(fdf, fdt); (void) copy_xattr(fdf, fdt);
q = close(fdt); if (copy_flags & COPY_FSYNC) {
fdt = -1; if (fsync(fdt) < 0) {
if (q < 0) {
r = -errno; r = -errno;
(void) unlinkat(dt, to, 0); goto fail;
}
}
q = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
if (q < 0) {
r = q;
goto fail;
} }
(void) memorize_hardlink(hardlink_context, st, dt, to); (void) memorize_hardlink(hardlink_context, st, dt, to);
return r; return r;
fail:
(void) unlinkat(dt, to, 0);
return r;
} }
static int fd_copy_fifo( static int fd_copy_fifo(
@ -845,13 +865,9 @@ static int fd_copy_directory(
if (dot_or_dot_dot(de->d_name)) if (dot_or_dot_dot(de->d_name))
continue; continue;
if (FLAGS_SET(copy_flags, COPY_SIGINT)) { r = look_for_signals(copy_flags);
r = pop_pending_signal(SIGINT);
if (r < 0) if (r < 0)
return r; return r;
if (r > 0)
return -EINTR;
}
if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) { if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
r = -errno; r = -errno;
@ -912,7 +928,7 @@ static int fd_copy_directory(
else else
q = -EOPNOTSUPP; q = -EOPNOTSUPP;
if (q == -EINTR) /* Propagate SIGINT up instantly */ if (q == -EINTR) /* Propagate SIGINT/SIGTERM up instantly */
return q; return q;
if (q == -EEXIST && (copy_flags & COPY_MERGE)) if (q == -EEXIST && (copy_flags & COPY_MERGE))
q = 0; q = 0;
@ -933,6 +949,11 @@ static int fd_copy_directory(
(void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim }); (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
} }
if (copy_flags & COPY_FSYNC_FULL) {
if (fsync(fdt) < 0)
return -errno;
}
return r; return r;
} }
@ -949,6 +970,7 @@ int copy_tree_at_full(
void *userdata) { void *userdata) {
struct stat st; struct stat st;
int r;
assert(from); assert(from);
assert(to); assert(to);
@ -957,17 +979,47 @@ int copy_tree_at_full(
return -errno; return -errno;
if (S_ISREG(st.st_mode)) if (S_ISREG(st.st_mode))
return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL, progress_bytes, userdata); r = fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL, progress_bytes, userdata);
else if (S_ISDIR(st.st_mode)) else if (S_ISDIR(st.st_mode))
return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata); r = fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
else if (S_ISLNK(st.st_mode)) else if (S_ISLNK(st.st_mode))
return fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags); r = fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else if (S_ISFIFO(st.st_mode)) else if (S_ISFIFO(st.st_mode))
return fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL); r = fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode)) 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, override_uid, override_gid, copy_flags, NULL); r = fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
else else
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (r < 0)
return r;
if (S_ISDIR(st.st_mode) && (copy_flags & COPY_SYNCFS)) {
/* If the top-level inode is a directory run syncfs() now. */
r = syncfs_path(fdt, to);
if (r < 0)
return r;
} else if ((copy_flags & (COPY_FSYNC_FULL|COPY_SYNCFS)) != 0) {
/* fsync() the parent dir of what we just copied if COPY_FSYNC_FULL is set. Also do this in
* case COPY_SYNCFS is set but the top-level inode wasn't actually a directory. We do this so that
* COPY_SYNCFS provides reasonable synchronization semantics on any kind of inode: when the
* copy operation is done the whole inode regardless of its type and all its children
* will be synchronized to disk. */
r = fsync_parent_at(fdt, to);
if (r < 0)
return r;
}
return 0;
}
static int sync_dir_by_flags(const char *path, CopyFlags copy_flags) {
if (copy_flags & COPY_SYNCFS)
return syncfs_path(AT_FDCWD, path);
if (copy_flags & COPY_FSYNC_FULL)
return fsync_parent_at(AT_FDCWD, path);
return 0;
} }
int copy_directory_fd_full( int copy_directory_fd_full(
@ -991,7 +1043,26 @@ int copy_directory_fd_full(
if (r < 0) if (r < 0)
return r; return r;
return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata); r = fd_copy_directory(
dirfd, NULL,
&st,
AT_FDCWD, to,
st.st_dev,
COPY_DEPTH_MAX,
UID_INVALID, GID_INVALID,
copy_flags,
NULL, NULL,
progress_path,
progress_bytes,
userdata);
if (r < 0)
return r;
r = sync_dir_by_flags(to, copy_flags);
if (r < 0)
return r;
return 0;
} }
int copy_directory_full( int copy_directory_full(
@ -1015,7 +1086,26 @@ int copy_directory_full(
if (r < 0) if (r < 0)
return r; return r;
return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata); r = fd_copy_directory(
AT_FDCWD, from,
&st,
AT_FDCWD, to,
st.st_dev,
COPY_DEPTH_MAX,
UID_INVALID, GID_INVALID,
copy_flags,
NULL, NULL,
progress_path,
progress_bytes,
userdata);
if (r < 0)
return r;
r = sync_dir_by_flags(to, copy_flags);
if (r < 0)
return r;
return 0;
} }
int copy_file_fd_full( int copy_file_fd_full(
@ -1026,6 +1116,7 @@ int copy_file_fd_full(
void *userdata) { void *userdata) {
_cleanup_close_ int fdf = -1; _cleanup_close_ int fdf = -1;
struct stat st;
int r; int r;
assert(from); assert(from);
@ -1035,12 +1126,32 @@ int copy_file_fd_full(
if (fdf < 0) if (fdf < 0)
return -errno; return -errno;
r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress_bytes, userdata); r = fd_verify_regular(fdf);
if (r < 0)
return r;
if (fstat(fdt, &st) < 0)
return -errno;
r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress_bytes, userdata);
if (r < 0)
return r;
if (S_ISREG(fdt)) {
(void) copy_times(fdf, fdt, copy_flags); (void) copy_times(fdf, fdt, copy_flags);
(void) copy_xattr(fdf, fdt); (void) copy_xattr(fdf, fdt);
}
if (copy_flags & COPY_FSYNC_FULL) {
r = fsync_full(fdt);
if (r < 0)
return r; return r;
} else if (copy_flags & COPY_FSYNC) {
if (fsync(fdt) < 0)
return -errno;
}
return 0;
} }
int copy_file_full( int copy_file_full(
@ -1054,9 +1165,9 @@ int copy_file_full(
copy_progress_bytes_t progress_bytes, copy_progress_bytes_t progress_bytes,
void *userdata) { void *userdata) {
_cleanup_close_ int fdf = -1; _cleanup_close_ int fdf = -1, fdt = -1;
struct stat st; struct stat st;
int r, fdt = -1; /* avoid false maybe-uninitialized warning */ int r;
assert(from); assert(from);
assert(to); assert(to);
@ -1065,10 +1176,13 @@ int copy_file_full(
if (fdf < 0) if (fdf < 0)
return -errno; return -errno;
if (mode == MODE_INVALID)
if (fstat(fdf, &st) < 0) if (fstat(fdf, &st) < 0)
return -errno; return -errno;
r = stat_verify_regular(&st);
if (r < 0)
return r;
RUN_WITH_UMASK(0000) { RUN_WITH_UMASK(0000) {
if (copy_flags & COPY_MAC_CREATE) { if (copy_flags & COPY_MAC_CREATE) {
r = mac_selinux_create_file_prepare(to, S_IFREG); r = mac_selinux_create_file_prepare(to, S_IFREG);
@ -1083,15 +1197,18 @@ int copy_file_full(
return -errno; return -errno;
} }
if (!FLAGS_SET(flags, O_EXCL)) { /* if O_EXCL was used we created the thing as regular file, no need to check again */
r = fd_verify_regular(fdt);
if (r < 0)
goto fail;
}
if (chattr_mask != 0) if (chattr_mask != 0)
(void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL); (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL);
r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress_bytes, userdata); r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress_bytes, userdata);
if (r < 0) { if (r < 0)
close(fdt); goto fail;
(void) unlink(to);
return r;
}
(void) copy_times(fdf, fdt, copy_flags); (void) copy_times(fdf, fdt, copy_flags);
(void) copy_xattr(fdf, fdt); (void) copy_xattr(fdf, fdt);
@ -1099,12 +1216,31 @@ int copy_file_full(
if (chattr_mask != 0) if (chattr_mask != 0)
(void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL); (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
if (close(fdt) < 0) { if (copy_flags & (COPY_FSYNC|COPY_FSYNC_FULL)) {
unlink_noerrno(to); if (fsync(fdt) < 0) {
return -errno; r = -errno;
goto fail;
}
}
r = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
if (r < 0)
goto fail;
if (copy_flags & COPY_FSYNC_FULL) {
r = fsync_parent_at(AT_FDCWD, to);
if (r < 0)
goto fail;
} }
return 0; return 0;
fail:
/* Only unlink if we definitely are the ones who created the file */
if (FLAGS_SET(flags, O_EXCL))
(void) unlink(to);
return r;
} }
int copy_file_atomic_full( int copy_file_atomic_full(
@ -1172,6 +1308,12 @@ int copy_file_atomic_full(
if (fchmod(fdt, mode) < 0) if (fchmod(fdt, mode) < 0)
return -errno; return -errno;
if ((copy_flags & (COPY_FSYNC|COPY_FSYNC_FULL))) {
/* Sync the file */
if (fsync(fdt) < 0)
return -errno;
}
if (copy_flags & COPY_REPLACE) { if (copy_flags & COPY_REPLACE) {
if (renameat(AT_FDCWD, t, AT_FDCWD, to) < 0) if (renameat(AT_FDCWD, t, AT_FDCWD, to) < 0)
return -errno; return -errno;
@ -1181,11 +1323,27 @@ int copy_file_atomic_full(
return r; return r;
} }
t = mfree(t);
if (chattr_mask != 0) if (chattr_mask != 0)
(void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL); (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
t = mfree(t); r = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
if (r < 0)
goto fail;
if (copy_flags & COPY_FSYNC_FULL) {
/* Sync the parent directory */
r = fsync_parent_at(AT_FDCWD, to);
if (r < 0)
goto fail;
}
return 0; return 0;
fail:
(void) unlink(to);
return r;
} }
int copy_times(int fdf, int fdt, CopyFlags flags) { int copy_times(int fdf, int fdt, CopyFlags flags) {

View File

@ -17,8 +17,12 @@ typedef enum CopyFlags {
COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */ 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_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_SIGINT = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
COPY_MAC_CREATE = 1 << 7, /* Create files with the correct MAC label (currently SELinux only) */ COPY_SIGTERM = 1 << 7, /* ditto, but for SIGTERM */
COPY_HARDLINKS = 1 << 8, /* Try to reproduce hard links */ 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 */
} CopyFlags; } CopyFlags;
typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata); typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);

View File

@ -1,5 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <sys/poll.h> #include <poll.h>
#include "fd-util.h" #include "fd-util.h"
#include "io-util.h" #include "io-util.h"

View File

@ -1,7 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <malloc.h> #include <malloc.h>
#include <sys/poll.h> #include <poll.h>
#include "alloc-util.h" #include "alloc-util.h"
#include "errno-util.h" #include "errno-util.h"