summaryrefslogtreecommitdiffstats
path: root/src/shared
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/base-filesystem.c4
-rw-r--r--src/shared/blockdev-util.c56
-rw-r--r--src/shared/bpf-dlopen.c11
-rw-r--r--src/shared/btrfs-util.c10
-rw-r--r--src/shared/copy.c123
-rw-r--r--src/shared/copy.h1
-rw-r--r--src/shared/creds-util.c4
-rw-r--r--src/shared/data-fd-util.h2
-rw-r--r--src/shared/dlfcn-util.c2
-rw-r--r--src/shared/hibernate-util.c8
-rw-r--r--src/shared/idn-util.c5
-rw-r--r--src/shared/install.c7
-rw-r--r--src/shared/journal-file-util.c15
-rw-r--r--src/shared/logs-show.c3
-rw-r--r--src/shared/loop-util.c8
-rw-r--r--src/shared/open-file.c2
-rw-r--r--src/shared/serialize.c55
-rw-r--r--src/shared/tpm2-util.c40
-rw-r--r--src/shared/tpm2-util.h1
-rw-r--r--src/shared/verbs.c29
-rw-r--r--src/shared/watchdog.c16
21 files changed, 278 insertions, 124 deletions
diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c
index 569ef46..a4e2dae 100644
--- a/src/shared/base-filesystem.c
+++ b/src/shared/base-filesystem.c
@@ -120,13 +120,13 @@ static const BaseFilesystem table[] = {
# else
# error "Unknown RISC-V ABI"
# endif
-#elif defined(__s390__)
- /* s390-linux-gnu */
#elif defined(__s390x__)
{ "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0"
"usr/lib64\0"
"usr/lib\0", "ld-lsb-s390x.so.3" },
# define KNOW_LIB64_DIRS 1
+#elif defined(__s390__)
+ /* s390-linux-gnu */
#elif defined(__sparc__)
#endif
/* gcc doesn't allow pragma to be used within constructs, hence log about this separately below */
diff --git a/src/shared/blockdev-util.c b/src/shared/blockdev-util.c
index c906aec..7a2dd1c 100644
--- a/src/shared/blockdev-util.c
+++ b/src/shared/blockdev-util.c
@@ -11,6 +11,7 @@
#include "alloc-util.h"
#include "blockdev-util.h"
#include "btrfs-util.h"
+#include "device-private.h"
#include "device-util.h"
#include "devnum-util.h"
#include "dirent-util.h"
@@ -367,24 +368,36 @@ int lock_whole_block_device(dev_t devt, int operation) {
}
int blockdev_partscan_enabled(int fd) {
- _cleanup_free_ char *p = NULL, *buf = NULL;
- unsigned long long ull;
- struct stat st;
- int r;
-
- /* Checks if partition scanning is correctly enabled on the block device */
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ unsigned capability;
+ int r, ext_range;
- if (fstat(fd, &st) < 0)
- return -errno;
+ /* Checks if partition scanning is correctly enabled on the block device.
+ *
+ * The 'GENHD_FL_NO_PART_SCAN' flag was introduced by
+ * https://github.com/torvalds/linux/commit/d27769ec3df1a8de9ca450d2dcd72d1ab259ba32 (v3.2).
+ * But at that time, the flag is also effectively implied when 'minors' element of 'struct gendisk'
+ * is 1, which can be check with 'ext_range' sysfs attribute. Explicit flag ('GENHD_FL_NO_PART_SCAN')
+ * can be obtained from 'capability' sysattr.
+ *
+ * With https://github.com/torvalds/linux/commit/1ebe2e5f9d68e94c524aba876f27b945669a7879 (v5.17), we
+ * can check the flag from 'ext_range' sysfs attribute directly.
+ *
+ * With https://github.com/torvalds/linux/commit/e81cd5a983bb35dabd38ee472cf3fea1c63e0f23 (v6.3),
+ * the 'capability' sysfs attribute is deprecated, hence we cannot check the flag from it.
+ *
+ * To support both old and new kernels, we need to do the following: first check 'ext_range' sysfs
+ * attribute, and if '1' we can conclude partition scanning is disabled, otherwise check 'capability'
+ * sysattr for older version. */
- if (!S_ISBLK(st.st_mode))
- return -ENOTBLK;
+ assert(fd >= 0);
- if (asprintf(&p, "/sys/dev/block/%u:%u/capability", major(st.st_rdev), minor(st.st_rdev)) < 0)
- return -ENOMEM;
+ r = block_device_new_from_fd(fd, 0, &dev);
+ if (r < 0)
+ return r;
- r = read_one_line_file(p, &buf);
- if (r == -ENOENT) /* If the capability file doesn't exist then we are most likely looking at a
+ r = device_get_sysattr_int(dev, "ext_range", &ext_range);
+ if (r == -ENOENT) /* If the ext_range file doesn't exist then we are most likely looking at a
* partition block device, not the whole block device. And that means we have no
* partition scanning on for it (we do for its parent, but not for the partition
* itself). */
@@ -392,7 +405,13 @@ int blockdev_partscan_enabled(int fd) {
if (r < 0)
return r;
- r = safe_atollu_full(buf, 16, &ull);
+ if (ext_range <= 1) /* The valus should be always positive, but the kernel uses '%d' for the
+ * attribute. Let's gracefully handle zero or negative. */
+ return false;
+
+ r = device_get_sysattr_unsigned_full(dev, "capability", 16, &capability);
+ if (r == -ENOENT)
+ return false;
if (r < 0)
return r;
@@ -400,7 +419,12 @@ int blockdev_partscan_enabled(int fd) {
#define GENHD_FL_NO_PART_SCAN (0x0200)
#endif
- return !FLAGS_SET(ull, GENHD_FL_NO_PART_SCAN);
+ /* If 0x200 is set, part scanning is definitely off. */
+ if (FLAGS_SET(capability, GENHD_FL_NO_PART_SCAN))
+ return false;
+
+ /* Otherwise, assume part scanning is on, we have no further checks available. Assume the best. */
+ return true;
}
static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) {
diff --git a/src/shared/bpf-dlopen.c b/src/shared/bpf-dlopen.c
index 15301ae..f00dbea 100644
--- a/src/shared/bpf-dlopen.c
+++ b/src/shared/bpf-dlopen.c
@@ -74,18 +74,23 @@ int dlopen_bpf(void) {
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"neither libbpf.so.1 nor libbpf.so.0 are installed: %s", dlerror());
+ log_debug("Loaded 'libbpf.so.0' via dlopen()");
+
/* symbols deprecated in 1.0 we use as compat */
r = dlsym_many_or_warn(
dl, LOG_DEBUG,
#if MODERN_LIBBPF
/* Don't exist anymore in new libbpf, hence cannot type check them */
DLSYM_ARG_FORCE(bpf_create_map),
- DLSYM_ARG_FORCE(bpf_probe_prog_type));
+ DLSYM_ARG_FORCE(bpf_probe_prog_type)
#else
DLSYM_ARG(bpf_create_map),
- DLSYM_ARG(bpf_probe_prog_type));
+ DLSYM_ARG(bpf_probe_prog_type)
#endif
+ );
} else {
+ log_debug("Loaded 'libbpf.so.1' via dlopen()");
+
/* symbols available from 0.7.0 */
r = dlsym_many_or_warn(
dl, LOG_DEBUG,
@@ -99,6 +104,8 @@ int dlopen_bpf(void) {
#endif
);
}
+ if (r < 0)
+ return r;
r = dlsym_many_or_warn(
dl, LOG_DEBUG,
diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c
index b3e4b50..2ed6bf2 100644
--- a/src/shared/btrfs-util.c
+++ b/src/shared/btrfs-util.c
@@ -65,7 +65,7 @@ int btrfs_subvol_set_read_only_at(int dir_fd, const char *path, bool b) {
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
- fd = xopenat(dir_fd, path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY, /* xopen_flags = */ 0, /* mode = */ 0);
+ fd = xopenat(dir_fd, path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
if (fd < 0)
return fd;
@@ -113,7 +113,7 @@ int btrfs_get_block_device_at(int dir_fd, const char *path, dev_t *ret) {
assert(path);
assert(ret);
- fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0);
+ fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0)
return fd;
@@ -1276,8 +1276,6 @@ static int subvol_snapshot_children(
if (FLAGS_SET(flags, BTRFS_SNAPSHOT_LOCK_BSD)) {
subvolume_fd = xopenat_lock(new_fd, subvolume,
O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW,
- /* xopen_flags = */ 0,
- /* mode = */ 0,
LOCK_BSD,
LOCK_EX);
if (subvolume_fd < 0)
@@ -1445,7 +1443,7 @@ int btrfs_subvol_snapshot_at_full(
assert(dir_fdt >= 0 || dir_fdt == AT_FDCWD);
assert(to);
- old_fd = xopenat(dir_fdf, from, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY, /* xopen_flags = */ 0, /* mode = */ 0);
+ old_fd = xopenat(dir_fdf, from, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
if (old_fd < 0)
return old_fd;
@@ -1482,8 +1480,6 @@ int btrfs_subvol_snapshot_at_full(
if (FLAGS_SET(flags, BTRFS_SNAPSHOT_LOCK_BSD)) {
subvolume_fd = xopenat_lock(new_fd, subvolume,
O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW,
- /* xopen_flags = */ 0,
- /* mode = */ 0,
LOCK_BSD,
LOCK_EX);
if (subvolume_fd < 0)
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));
diff --git a/src/shared/copy.h b/src/shared/copy.h
index d842edd..b8fb28a 100644
--- a/src/shared/copy.h
+++ b/src/shared/copy.h
@@ -30,6 +30,7 @@ typedef enum CopyFlags {
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. */
} CopyFlags;
typedef enum DenyType {
diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c
index 7cc8889..fa8ebe0 100644
--- a/src/shared/creds-util.c
+++ b/src/shared/creds-util.c
@@ -826,9 +826,9 @@ int encrypt_credential_and_warn(
tpm2_pubkey_pcr_mask = 0;
_cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
- r = tpm2_context_new(tpm2_device, &tpm2_context);
+ r = tpm2_context_new_or_warn(tpm2_device, &tpm2_context);
if (r < 0)
- return log_error_errno(r, "Failed to create TPM2 context: %m");
+ return r;
r = tpm2_get_best_pcr_bank(tpm2_context, tpm2_hash_pcr_mask | tpm2_pubkey_pcr_mask, &tpm2_pcr_bank);
if (r < 0)
diff --git a/src/shared/data-fd-util.h b/src/shared/data-fd-util.h
index 4f3d8b8..6d99209 100644
--- a/src/shared/data-fd-util.h
+++ b/src/shared/data-fd-util.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include <inttypes.h>
+#include <stddef.h>
enum {
ACQUIRE_NO_DEV_NULL = 1 << 0,
diff --git a/src/shared/dlfcn-util.c b/src/shared/dlfcn-util.c
index a321df3..8022f55 100644
--- a/src/shared/dlfcn-util.c
+++ b/src/shared/dlfcn-util.c
@@ -49,6 +49,8 @@ int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_l
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"%s is not installed: %s", filename, dlerror());
+ log_debug("Loaded '%s' via dlopen()", filename);
+
va_list ap;
va_start(ap, log_level);
r = dlsym_many_or_warnv(dl, log_level, ap);
diff --git a/src/shared/hibernate-util.c b/src/shared/hibernate-util.c
index 0d215e8..c3991cf 100644
--- a/src/shared/hibernate-util.c
+++ b/src/shared/hibernate-util.c
@@ -23,6 +23,7 @@
#include "log.h"
#include "parse-util.h"
#include "path-util.h"
+#include "proc-cmdline.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
@@ -129,6 +130,13 @@ static int read_resume_config(dev_t *ret_devno, uint64_t *ret_offset) {
assert(ret_devno);
assert(ret_offset);
+ r = proc_cmdline_get_key("noresume", /* flags = */ 0, /* ret_value = */ NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to check if 'noresume' kernel command line option is set: %m");
+ if (r > 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "'noresume' kernel command line option is set, refusing hibernation device lookup.");
+
r = read_one_line_file("/sys/power/resume", &devno_str);
if (r < 0)
return log_debug_errno(r, "Failed to read /sys/power/resume: %m");
diff --git a/src/shared/idn-util.c b/src/shared/idn-util.c
index 6f36688..26a9d60 100644
--- a/src/shared/idn-util.c
+++ b/src/shared/idn-util.c
@@ -50,7 +50,10 @@ int dlopen_idn(void) {
if (!dl)
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"libidn support is not installed: %s", dlerror());
- }
+ log_debug("Loaded 'libidn.so.11' via dlopen()");
+ } else
+ log_debug("Loaded 'libidn.so.12' via dlopen()");
+
r = dlsym_many_or_warn(
dl,
diff --git a/src/shared/install.c b/src/shared/install.c
index 0f4dab4..27a421e 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -340,9 +340,12 @@ void install_changes_dump(int r, const char *verb, const InstallChange *changes,
assert(verb || r >= 0);
for (size_t i = 0; i < n_changes; i++) {
- if (changes[i].type < 0)
- assert(verb);
assert(changes[i].path);
+ /* This tries to tell the compiler that it's safe to use 'verb' in a string format if there
+ * was an error, but the compiler doesn't care and fails anyway, so strna(verb) is used
+ * too. */
+ assert(verb || changes[i].type >= 0);
+ verb = strna(verb);
/* When making changes here, make sure to also change install_error() in dbus-manager.c. */
diff --git a/src/shared/journal-file-util.c b/src/shared/journal-file-util.c
index e444a2b..bdceac4 100644
--- a/src/shared/journal-file-util.c
+++ b/src/shared/journal-file-util.c
@@ -210,11 +210,16 @@ static void journal_file_set_offline_internal(JournalFile *f) {
log_debug_errno(r, "Failed to re-enable copy-on-write for %s: %m, rewriting file", f->path);
- r = copy_file_atomic_full(FORMAT_PROC_FD_PATH(f->fd), f->path, f->mode,
- 0,
- FS_NOCOW_FL,
- COPY_REPLACE | COPY_FSYNC | COPY_HOLES | COPY_ALL_XATTRS,
- NULL, NULL);
+ /* Here, setting COPY_VERIFY_LINKED flag is crucial. Otherwise, a broken
+ * journal file may be created, if journal_directory_vacuum() ->
+ * unlinkat_deallocate() is called in the main thread while this thread is
+ * copying the file. See issue #24150 and #31222. */
+ r = copy_file_atomic_at_full(
+ f->fd, NULL, AT_FDCWD, f->path, f->mode,
+ 0,
+ FS_NOCOW_FL,
+ COPY_REPLACE | COPY_FSYNC | COPY_HOLES | COPY_ALL_XATTRS | COPY_VERIFY_LINKED,
+ NULL, NULL);
if (r < 0) {
log_debug_errno(r, "Failed to rewrite %s: %m", f->path);
continue;
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index a5d0400..0a31be3 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -2088,7 +2088,7 @@ int journal_get_boots(sd_journal *j, BootId **ret_boots, size_t *ret_n_boots) {
if (sd_id128_equal(i->id, boot.id))
/* The boot id is already stored, something wrong with the journal files.
* Exiting as otherwise this problem would cause an infinite loop. */
- break;
+ goto finish;
if (!GREEDY_REALLOC(boots, n_boots + 1))
return -ENOMEM;
@@ -2096,6 +2096,7 @@ int journal_get_boots(sd_journal *j, BootId **ret_boots, size_t *ret_n_boots) {
boots[n_boots++] = boot;
}
+ finish:
*ret_boots = TAKE_PTR(boots);
*ret_n_boots = n_boots;
return n_boots > 0;
diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c
index 5860303..6d55df7 100644
--- a/src/shared/loop-util.c
+++ b/src/shared/loop-util.c
@@ -702,9 +702,9 @@ int loop_device_make_by_path_at(
direct_flags = FLAGS_SET(loop_flags, LO_FLAGS_DIRECT_IO) ? O_DIRECT : 0;
rdwr_flags = open_flags >= 0 ? open_flags : O_RDWR;
- fd = xopenat(dir_fd, path, basic_flags|direct_flags|rdwr_flags, /* xopen_flags = */ 0, /* mode = */ 0);
+ fd = xopenat(dir_fd, path, basic_flags|direct_flags|rdwr_flags);
if (fd < 0 && direct_flags != 0) /* If we had O_DIRECT on, and things failed with that, let's immediately try again without */
- fd = xopenat(dir_fd, path, basic_flags|rdwr_flags, /* xopen_flags = */ 0, /* mode = */ 0);
+ fd = xopenat(dir_fd, path, basic_flags|rdwr_flags);
else
direct = direct_flags != 0;
if (fd < 0) {
@@ -714,9 +714,9 @@ int loop_device_make_by_path_at(
if (open_flags >= 0 || !(ERRNO_IS_PRIVILEGE(r) || r == -EROFS))
return r;
- fd = xopenat(dir_fd, path, basic_flags|direct_flags|O_RDONLY, /* xopen_flags = */ 0, /* mode = */ 0);
+ fd = xopenat(dir_fd, path, basic_flags|direct_flags|O_RDONLY);
if (fd < 0 && direct_flags != 0) /* as above */
- fd = xopenat(dir_fd, path, basic_flags|O_RDONLY, /* xopen_flags = */ 0, /* mode = */ 0);
+ fd = xopenat(dir_fd, path, basic_flags|O_RDONLY);
else
direct = direct_flags != 0;
if (fd < 0)
diff --git a/src/shared/open-file.c b/src/shared/open-file.c
index 42772bd..7d7a8a9 100644
--- a/src/shared/open-file.c
+++ b/src/shared/open-file.c
@@ -96,7 +96,7 @@ int open_file_to_string(const OpenFile *of, char **ret) {
assert(of);
assert(ret);
- s = shell_escape(of->path, ":");
+ s = xescape(of->path, ":");
if (!s)
return -ENOMEM;
diff --git a/src/shared/serialize.c b/src/shared/serialize.c
index 483cbc7..344b102 100644
--- a/src/shared/serialize.c
+++ b/src/shared/serialize.c
@@ -180,7 +180,7 @@ int serialize_strv(FILE *f, const char *key, char **l) {
}
int serialize_pidref(FILE *f, FDSet *fds, const char *key, PidRef *pidref) {
- int copy;
+ int r;
assert(f);
assert(fds);
@@ -188,17 +188,23 @@ int serialize_pidref(FILE *f, FDSet *fds, const char *key, PidRef *pidref) {
if (!pidref_is_set(pidref))
return 0;
- /* If we have a pidfd we serialize the fd and encode the fd number prefixed by "@" in the
- * serialization. Otherwise we serialize the numeric PID as it is. */
+ /* We always serialize the pid separately, to keep downgrades mostly working (older versions will
+ * deserialize the pid and silently fail to deserialize the pidfd). If we also have a pidfd, we
+ * serialize both the pid and pidfd, so that we can construct the exact same pidref after
+ * deserialization (this doesn't work with only the pidfd, as we can't retrieve the original pid
+ * from the pidfd anymore if the process is reaped). */
- if (pidref->fd < 0)
- return serialize_item_format(f, key, PID_FMT, pidref->pid);
+ if (pidref->fd >= 0) {
+ int copy = fdset_put_dup(fds, pidref->fd);
+ if (copy < 0)
+ return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
- copy = fdset_put_dup(fds, pidref->fd);
- if (copy < 0)
- return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
+ r = serialize_item_format(f, key, "@%i:" PID_FMT, copy, pidref->pid);
+ if (r < 0)
+ return r;
+ }
- return serialize_item_format(f, key, "@%i", copy);
+ return serialize_item_format(f, key, PID_FMT, pidref->pid);
}
int serialize_ratelimit(FILE *f, const char *key, const RateLimit *rl) {
@@ -476,12 +482,39 @@ int deserialize_pidref(FDSet *fds, const char *value, PidRef *ret) {
e = startswith(value, "@");
if (e) {
- int fd = deserialize_fd(fds, e);
+ _cleanup_free_ char *fdstr = NULL, *pidstr = NULL;
+ _cleanup_close_ int fd = -EBADF;
+
+ r = extract_many_words(&e, ":", /* flags = */ 0, &fdstr, &pidstr, NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to deserialize pidref '%s': %m", e);
+ if (r == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot deserialize pidref from empty string.");
+
+ assert(r <= 2);
+ fd = deserialize_fd(fds, fdstr);
if (fd < 0)
return fd;
- r = pidref_set_pidfd_consume(ret, fd);
+ /* The serialization format changed after 255.4. In systemd <= 255.4 only pidfd is
+ * serialized, but that causes problems when reconstructing pidref (see serialize_pidref for
+ * details). After 255.4 the pid is serialized as well even if we have a pidfd, but we still
+ * need to support older format as we might be upgrading from a version that still uses the
+ * old format. */
+ if (pidstr) {
+ pid_t pid;
+
+ r = parse_pid(pidstr, &pid);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse PID: %s", pidstr);
+
+ *ret = (PidRef) {
+ .pid = pid,
+ .fd = TAKE_FD(fd),
+ };
+ } else
+ r = pidref_set_pidfd_consume(ret, TAKE_FD(fd));
} else {
pid_t pid;
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 30b4f57..c7e0b24 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -664,7 +664,9 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
context->tcti_dl = dlopen(fn, RTLD_NOW);
if (!context->tcti_dl)
- return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to load %s: %s", fn, dlerror());
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to load %s: %s", fn, dlerror());
+
+ log_debug("Loaded '%s' via dlopen()", fn);
func = dlsym(context->tcti_dl, TSS2_TCTI_INFO_SYMBOL);
if (!func)
@@ -678,7 +680,7 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
log_debug("Loaded TCTI module '%s' (%s) [Version %" PRIu32 "]", info->name, info->description, info->version);
- rc = info->init(NULL, &sz, NULL);
+ rc = info->init(/* context= */ NULL, &sz, /* param= */ NULL);
if (rc != TPM2_RC_SUCCESS)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to initialize TCTI context: %s", sym_Tss2_RC_Decode(rc));
@@ -713,19 +715,37 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
/* We require AES and CFB support for session encryption. */
if (!tpm2_supports_alg(context, TPM2_ALG_AES))
- return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES.");
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM does not support AES.");
if (!tpm2_supports_alg(context, TPM2_ALG_CFB))
- return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support CFB.");
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM does not support CFB.");
if (!tpm2_supports_tpmt_sym_def(context, &SESSION_TEMPLATE_SYM_AES_128_CFB))
- return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES-128-CFB.");
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM does not support AES-128-CFB.");
*ret_context = TAKE_PTR(context);
return 0;
}
+int tpm2_context_new_or_warn(const char *device, Tpm2Context **ret_context) {
+ int r;
+
+ assert(ret_context);
+
+ r = tpm2_context_new(device, ret_context);
+ if (r == -EOPNOTSUPP)
+ return log_error_errno(r, "TPM device not usable as it does not support the required functionality (AES-128-CFB missing?).");
+ if (r == -ENOPKG)
+ return log_error_errno(r, "TPM TCTI driver not available.");
+ if (r == -ENOENT)
+ return log_error_errno(r, "TPM device not found.");
+ if (r < 0)
+ return log_error_errno(r, "Failed to create TPM2 context: %m");
+
+ return 0;
+}
+
static void tpm2_handle_cleanup(ESYS_CONTEXT *esys_context, ESYS_TR esys_handle, bool flush) {
TSS2_RC rc;
@@ -5540,13 +5560,13 @@ int tpm2_unseal(Tpm2Context *c,
if (r < 0)
return r;
- _cleanup_(tpm2_handle_freep) Tpm2Handle *encryption_session = NULL;
- r = tpm2_make_encryption_session(c, primary_handle, hmac_key, &encryption_session);
- if (r < 0)
- return r;
-
_cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *encryption_session = NULL;
+ r = tpm2_make_encryption_session(c, primary_handle, hmac_key, &encryption_session);
+ if (r < 0)
+ return r;
+
_cleanup_(tpm2_handle_freep) Tpm2Handle *policy_session = NULL;
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
r = tpm2_make_policy_session(
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 55d7481..911a3c7 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -72,6 +72,7 @@ typedef struct {
} Tpm2Context;
int tpm2_context_new(const char *device, Tpm2Context **ret_context);
+int tpm2_context_new_or_warn(const char *device, Tpm2Context **ret_context);
Tpm2Context *tpm2_context_ref(Tpm2Context *context);
Tpm2Context *tpm2_context_unref(Tpm2Context *context);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Context*, tpm2_context_unref);
diff --git a/src/shared/verbs.c b/src/shared/verbs.c
index a010952..a38591d 100644
--- a/src/shared/verbs.c
+++ b/src/shared/verbs.c
@@ -13,22 +13,21 @@
#include "verbs.h"
#include "virt.h"
-/* Wraps running_in_chroot() which is used in various places, but also adds an environment variable check so external
- * processes can reliably force this on.
- */
+/* Wraps running_in_chroot() which is used in various places, but also adds an environment variable check
+ * so external processes can reliably force this on. */
bool running_in_chroot_or_offline(void) {
int r;
- /* Added to support use cases like rpm-ostree, where from %post scripts we only want to execute "preset", but
- * not "start"/"restart" for example.
+ /* Added to support use cases like rpm-ostree, where from %post scripts we only want to execute "preset",
+ * but not "start"/"restart" for example.
*
* See docs/ENVIRONMENT.md for docs.
*/
r = getenv_bool("SYSTEMD_OFFLINE");
- if (r < 0 && r != -ENXIO)
- log_debug_errno(r, "Failed to parse $SYSTEMD_OFFLINE: %m");
- else if (r >= 0)
+ if (r >= 0)
return r > 0;
+ if (r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_OFFLINE, ignoring: %m");
/* We've had this condition check for a long time which basically checks for legacy chroot case like Fedora's
* "mock", which is used for package builds. We don't want to try to start systemd services there, since
@@ -40,8 +39,7 @@ bool running_in_chroot_or_offline(void) {
*/
r = running_in_chroot();
if (r < 0)
- log_debug_errno(r, "running_in_chroot(): %m");
-
+ log_debug_errno(r, "Failed to check if we're running in chroot, assuming not: %m");
return r > 0;
}
@@ -145,6 +143,17 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown command verb '%s'.", name);
}
+ _cleanup_free_ char *verb_list = NULL;
+ size_t i;
+
+ for (i = 0; verbs[i].dispatch; i++)
+ if (!strextend_with_separator(&verb_list, ", ", verbs[i].verb))
+ return log_oom();
+
+ if (i > 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Command verb required (one of %s).", verb_list);
+
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Command verb required.");
}
diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c
index 2d79f71..99ccefb 100644
--- a/src/shared/watchdog.c
+++ b/src/shared/watchdog.c
@@ -95,7 +95,7 @@ static int set_pretimeout_governor(const char *governor) {
governor,
WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE);
if (r < 0)
- return log_error_errno(r, "Failed to set pretimeout_governor to '%s': %m", governor);
+ return log_error_errno(r, "Failed to set watchdog pretimeout_governor to '%s': %m", governor);
return r;
}
@@ -157,7 +157,7 @@ static int watchdog_read_pretimeout(void) {
if (ioctl(watchdog_fd, WDIOC_GETPRETIMEOUT, &sec) < 0) {
watchdog_pretimeout = 0;
- return log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno, "Failed to get pretimeout value, ignoring: %m");
+ return log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno, "Failed to get watchdog pretimeout value, ignoring: %m");
}
watchdog_pretimeout = sec * USEC_PER_SEC;
@@ -181,7 +181,7 @@ static int watchdog_set_pretimeout(void) {
return 0;
}
- return log_error_errno(errno, "Failed to set pretimeout to %s: %m", FORMAT_TIMESPAN(sec, USEC_PER_SEC));
+ return log_error_errno(errno, "Failed to set watchdog pretimeout to %s: %m", FORMAT_TIMESPAN(sec, USEC_PER_SEC));
}
/* The set ioctl does not return the actual value set so get it now. */
@@ -274,10 +274,10 @@ static int update_timeout(void) {
r = watchdog_set_timeout();
if (r < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(r))
- return log_error_errno(r, "Failed to set timeout to %s: %m",
+ return log_error_errno(r, "Failed to set watchdog hardware timeout to %s: %m",
FORMAT_TIMESPAN(watchdog_timeout, 0));
- log_info("Modifying watchdog timeout is not supported, reusing the programmed timeout.");
+ log_info("Modifying watchdog hardware timeout is not supported, reusing the programmed timeout.");
watchdog_timeout = USEC_INFINITY;
}
}
@@ -286,8 +286,8 @@ static int update_timeout(void) {
r = watchdog_read_timeout();
if (r < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(r))
- return log_error_errno(r, "Failed to query watchdog HW timeout: %m");
- log_info("Reading watchdog timeout is not supported, reusing the configured timeout.");
+ return log_error_errno(r, "Failed to query watchdog hardware timeout: %m");
+ log_info("Reading watchdog hardware timeout is not supported, reusing the configured timeout.");
watchdog_timeout = previous_timeout;
}
}
@@ -302,7 +302,7 @@ static int update_timeout(void) {
if (r < 0)
return r;
- log_info("Watchdog running with a timeout of %s.", FORMAT_TIMESPAN(watchdog_timeout, 0));
+ log_info("Watchdog running with a hardware timeout of %s.", FORMAT_TIMESPAN(watchdog_timeout, 0));
return watchdog_ping_now();
}