summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/basic/chase.c54
-rw-r--r--src/basic/chase.h1
-rw-r--r--src/basic/chattr-util.c28
-rw-r--r--src/basic/chattr-util.h13
-rw-r--r--src/basic/fd-util.c13
-rw-r--r--src/basic/fd-util.h7
-rw-r--r--src/basic/fs-util.c1
-rw-r--r--src/basic/stat-util.c18
-rw-r--r--src/basic/stat-util.h9
-rw-r--r--src/boot/efi/meson.build6
-rw-r--r--src/cryptsetup/cryptsetup-generator.c3
-rw-r--r--src/dissect/dissect.c2
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c2
-rw-r--r--src/hibernate-resume/hibernate-resume-generator.c3
-rw-r--r--src/home/homework-luks.c2
-rw-r--r--src/import/export-tar.c2
-rw-r--r--src/import/import-fs.c12
-rw-r--r--src/import/pull-tar.c6
-rw-r--r--src/integritysetup/integritysetup-generator.c2
-rwxr-xr-xsrc/kernel-install/60-ukify.install.in9
-rw-r--r--src/libsystemd/libsystemd.sym1
-rw-r--r--src/libsystemd/sd-login/sd-login.c19
-rw-r--r--src/login/systemd-user.in1
-rw-r--r--src/nspawn/nspawn.c30
-rw-r--r--src/partition/repart.c276
-rw-r--r--src/resolve/resolved-dns-packet.c14
-rw-r--r--src/shared/btrfs-util.c126
-rw-r--r--src/shared/btrfs-util.h32
-rw-r--r--src/shared/copy.c79
-rw-r--r--src/shared/copy.h12
-rw-r--r--src/shared/discover-image.c14
-rw-r--r--src/shared/generator.c8
-rw-r--r--src/shared/rm-rf.c27
-rw-r--r--src/shared/rm-rf.h6
-rw-r--r--src/systemd/sd-login.h3
-rw-r--r--src/test/meson.build4
-rw-r--r--src/test/test-btrfs.c12
-rw-r--r--src/test/test-chase.c48
-rw-r--r--src/test/test-execute.c2
-rw-r--r--src/test/test-path-util.c11
-rw-r--r--src/tmpfiles/tmpfiles.c2
-rwxr-xr-xsrc/ukify/test/test_ukify.py117
-rwxr-xr-xsrc/ukify/ukify.py55
43 files changed, 702 insertions, 390 deletions
diff --git a/src/basic/chase.c b/src/basic/chase.c
index 600e2b9d33..a4d9edb4c9 100644
--- a/src/basic/chase.c
+++ b/src/basic/chase.c
@@ -81,7 +81,6 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
const char *todo;
int r;
- assert(path);
assert(!FLAGS_SET(flags, CHASE_PREFIX_ROOT));
assert(!FLAGS_SET(flags, CHASE_STEP|CHASE_EXTRACT_FILENAME));
assert(!FLAGS_SET(flags, CHASE_TRAIL_SLASH|CHASE_EXTRACT_FILENAME));
@@ -618,11 +617,51 @@ int chaseat_prefix_root(const char *path, const char *root, char **ret) {
return 0;
}
+int chase_extract_filename(const char *path, const char *root, char **ret) {
+ int r;
+
+ /* This is similar to path_extract_filename(), but takes root directory.
+ * The result should be consistent with chase() with CHASE_EXTRACT_FILENAME. */
+
+ assert(path);
+ assert(ret);
+
+ if (isempty(path))
+ return -EINVAL;
+
+ if (!path_is_absolute(path))
+ return -EINVAL;
+
+ if (!empty_or_root(root)) {
+ _cleanup_free_ char *root_abs = NULL;
+
+ r = path_make_absolute_cwd(root, &root_abs);
+ if (r < 0)
+ return r;
+
+ path = path_startswith(path, root_abs);
+ if (!path)
+ return -EINVAL;
+ }
+
+ if (!isempty(path)) {
+ r = path_extract_filename(path, ret);
+ if (r != -EADDRNOTAVAIL)
+ return r;
+ }
+
+ char *fname = strdup(".");
+ if (!fname)
+ return -ENOMEM;
+
+ *ret = fname;
+ return 0;
+}
+
int chase_and_open(const char *path, const char *root, ChaseFlags chase_flags, int open_flags, char **ret_path) {
_cleanup_close_ int path_fd = -EBADF;
_cleanup_free_ char *p = NULL, *fname = NULL;
mode_t mode = open_flags & O_DIRECTORY ? 0755 : 0644;
- const char *q;
int r;
assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP)));
@@ -640,13 +679,10 @@ int chase_and_open(const char *path, const char *root, ChaseFlags chase_flags, i
return r;
assert(path_fd >= 0);
- assert_se(q = path_startswith(p, empty_to_root(root)));
- if (isempty(q))
- q = ".";
-
- if (!FLAGS_SET(chase_flags, CHASE_PARENT)) {
- r = path_extract_filename(q, &fname);
- if (r < 0 && r != -EADDRNOTAVAIL)
+ if (!FLAGS_SET(chase_flags, CHASE_PARENT) &&
+ !FLAGS_SET(chase_flags, CHASE_EXTRACT_FILENAME)) {
+ r = chase_extract_filename(p, root, &fname);
+ if (r < 0)
return r;
}
diff --git a/src/basic/chase.h b/src/basic/chase.h
index f37e836822..cfc714b9f7 100644
--- a/src/basic/chase.h
+++ b/src/basic/chase.h
@@ -43,6 +43,7 @@ bool unsafe_transition(const struct stat *a, const struct stat *b);
int chase(const char *path_with_prefix, const char *root, ChaseFlags chase_flags, char **ret_path, int *ret_fd);
int chaseat_prefix_root(const char *path, const char *root, char **ret);
+int chase_extract_filename(const char *path, const char *root, char **ret);
int chase_and_open(const char *path, const char *root, ChaseFlags chase_flags, int open_flags, char **ret_path);
int chase_and_opendir(const char *path, const char *root, ChaseFlags chase_flags, char **ret_path, DIR **ret_dir);
diff --git a/src/basic/chattr-util.c b/src/basic/chattr-util.c
index 3c66a3e0c8..c59fb8a84e 100644
--- a/src/basic/chattr-util.c
+++ b/src/basic/chattr-util.c
@@ -9,29 +9,29 @@
#include "chattr-util.h"
#include "errno-util.h"
#include "fd-util.h"
+#include "fs-util.h"
#include "macro.h"
#include "string-util.h"
-int chattr_full(const char *path,
- int fd,
- unsigned value,
- unsigned mask,
- unsigned *ret_previous,
- unsigned *ret_final,
- ChattrApplyFlags flags) {
+int chattr_full(
+ int dir_fd,
+ const char *path,
+ unsigned value,
+ unsigned mask,
+ unsigned *ret_previous,
+ unsigned *ret_final,
+ ChattrApplyFlags flags) {
- _cleanup_close_ int fd_will_close = -EBADF;
+ _cleanup_close_ int fd = -EBADF;
unsigned old_attr, new_attr;
int set_flags_errno = 0;
struct stat st;
- assert(path || fd >= 0);
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
- if (fd < 0) {
- fd = fd_will_close = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- if (fd < 0)
- return -errno;
- }
+ fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, /* xopen_flags = */ 0, /* mode = */ 0);
+ if (fd < 0)
+ return -errno;
if (fstat(fd, &st) < 0)
return -errno;
diff --git a/src/basic/chattr-util.h b/src/basic/chattr-util.h
index 82f91c66d9..c1ee63b4fa 100644
--- a/src/basic/chattr-util.h
+++ b/src/basic/chattr-util.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <fcntl.h>
#include <linux/fs.h>
#include <stdbool.h>
#include <stddef.h>
@@ -39,13 +40,15 @@ typedef enum ChattrApplyFlags {
CHATTR_WARN_UNSUPPORTED_FLAGS = 1 << 1,
} ChattrApplyFlags;
-int chattr_full(const char *path, int fd, unsigned value, unsigned mask, unsigned *ret_previous, unsigned *ret_final, ChattrApplyFlags flags);
-
+int chattr_full(int dir_fd, const char *path, unsigned value, unsigned mask, unsigned *ret_previous, unsigned *ret_final, ChattrApplyFlags flags);
+static inline int chattr_at(int dir_fd, const char *path, unsigned value, unsigned mask, unsigned *previous) {
+ return chattr_full(dir_fd, path, value, mask, previous, NULL, 0);
+}
static inline int chattr_fd(int fd, unsigned value, unsigned mask, unsigned *previous) {
- return chattr_full(NULL, fd, value, mask, previous, NULL, 0);
+ return chattr_full(fd, NULL, value, mask, previous, NULL, 0);
}
static inline int chattr_path(const char *path, unsigned value, unsigned mask, unsigned *previous) {
- return chattr_full(path, -1, value, mask, previous, NULL, 0);
+ return chattr_full(AT_FDCWD, path, value, mask, previous, NULL, 0);
}
int read_attr_fd(int fd, unsigned *ret);
@@ -57,5 +60,5 @@ int read_attr_path(const char *p, unsigned *ret);
#define CHATTR_SECRET_FLAGS (FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL)
static inline int chattr_secret(int fd, ChattrApplyFlags flags) {
- return chattr_full(NULL, fd, CHATTR_SECRET_FLAGS, CHATTR_SECRET_FLAGS, NULL, NULL, flags|CHATTR_FALLBACK_BITWISE);
+ return chattr_full(fd, NULL, CHATTR_SECRET_FLAGS, CHATTR_SECRET_FLAGS, NULL, NULL, flags|CHATTR_FALLBACK_BITWISE);
}
diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c
index 907bfeb600..ecbe58a9f8 100644
--- a/src/basic/fd-util.c
+++ b/src/basic/fd-util.c
@@ -891,12 +891,21 @@ int fd_get_diskseq(int fd, uint64_t *ret) {
return 0;
}
-int dir_fd_is_root(int dir_fd) {
+int path_is_root_at(int dir_fd, const char *path) {
STRUCT_NEW_STATX_DEFINE(st);
STRUCT_NEW_STATX_DEFINE(pst);
+ _cleanup_close_ int fd = -EBADF;
int r;
- assert(dir_fd >= 0);
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+
+ if (!isempty(path)) {
+ fd = openat(dir_fd, path, O_PATH|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ dir_fd = fd;
+ }
r = statx_fallback(dir_fd, ".", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st.sx);
if (r == -ENOTDIR)
diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h
index 2f59e334c5..c870a1b899 100644
--- a/src/basic/fd-util.h
+++ b/src/basic/fd-util.h
@@ -101,9 +101,12 @@ int fd_is_opath(int fd);
int read_nr_open(void);
int fd_get_diskseq(int fd, uint64_t *ret);
-int dir_fd_is_root(int dir_fd);
+int path_is_root_at(int dir_fd, const char *path);
+static inline int dir_fd_is_root(int dir_fd) {
+ return path_is_root_at(dir_fd, NULL);
+}
static inline int dir_fd_is_root_or_cwd(int dir_fd) {
- return dir_fd == AT_FDCWD ? true : dir_fd_is_root(dir_fd);
+ return dir_fd == AT_FDCWD ? true : path_is_root_at(dir_fd, NULL);
}
/* The maximum length a buffer for a /proc/self/fd/<fd> path needs */
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 78e92609c6..1cf0f5b945 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -1097,7 +1097,6 @@ int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
- assert(path);
if (isempty(path)) {
assert(!FLAGS_SET(open_flags, O_CREAT|O_EXCL));
diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c
index 633d9479dd..3c999098e1 100644
--- a/src/basic/stat-util.c
+++ b/src/basic/stat-util.c
@@ -207,20 +207,13 @@ bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
return F_TYPE_EQUAL(s->f_type, magic_value);
}
-int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
- struct statfs s;
-
- if (fstatfs(fd, &s) < 0)
- return -errno;
-
- return is_fs_type(&s, magic_value);
-}
-
-int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
+int is_fs_type_at(int dir_fd, const char *path, statfs_f_type_t magic_value) {
struct statfs s;
+ int r;
- if (statfs(path, &s) < 0)
- return -errno;
+ r = xstatfsat(dir_fd, path, &s);
+ if (r < 0)
+ return r;
return is_fs_type(&s, magic_value);
}
@@ -461,7 +454,6 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
_cleanup_close_ int fd = -EBADF;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
- assert(path);
assert(ret);
fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0);
diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h
index 3ae8b3eeb1..ae0aaf8f51 100644
--- a/src/basic/stat-util.h
+++ b/src/basic/stat-util.h
@@ -48,8 +48,13 @@ static inline int inode_same(const char *filea, const char *fileb, int flags) {
typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t;
bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) _pure_;
-int fd_is_fs_type(int fd, statfs_f_type_t magic_value);
-int path_is_fs_type(const char *path, statfs_f_type_t magic_value);
+int is_fs_type_at(int dir_fd, const char *path, statfs_f_type_t magic_value);
+static inline int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
+ return is_fs_type_at(fd, NULL, magic_value);
+}
+static inline int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
+ return is_fs_type_at(AT_FDCWD, path, magic_value);
+}
bool is_temporary_fs(const struct statfs *s) _pure_;
bool is_network_fs(const struct statfs *s) _pure_;
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index b573e6996d..68c19fbcc9 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
efi_config_h_dir = meson.current_build_dir()
+efi_addon = ''
if efi_arch != ''
libefitest = static_library(
@@ -376,6 +377,11 @@ foreach efi_elf_binary : efi_elf_binaries
if name.startswith('linux')
boot_stubs += exe
endif
+
+ # This is supposed to match exactly one time
+ if name == 'addon@0@.efi.stub'.format(efi_arch)
+ efi_addon = exe.full_path()
+ endif
endforeach
alias_target('systemd-boot', boot_targets)
diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c
index a22d932342..702b2615e2 100644
--- a/src/cryptsetup/cryptsetup-generator.c
+++ b/src/cryptsetup/cryptsetup-generator.c
@@ -562,7 +562,8 @@ static int create_disk(
if (!noauto && !nofail) {
r = write_drop_in(arg_dest, dmname, 40, "device-timeout",
"# Automatically generated by systemd-cryptsetup-generator\n\n"
- "[Unit]\nJobTimeoutSec=0");
+ "[Unit]\n"
+ "JobTimeoutSec=infinity\n");
if (r < 0)
log_warning_errno(r, "Failed to write device timeout drop-in: %m");
}
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c
index 5c742497d5..25bdd68cd2 100644
--- a/src/dissect/dissect.c
+++ b/src/dissect/dissect.c
@@ -1286,7 +1286,7 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) {
}
/* Try to copy as directory? */
- r = copy_directory_fd(source_fd, arg_target, COPY_REFLINK|COPY_MERGE_EMPTY|COPY_SIGINT|COPY_HARDLINKS);
+ r = copy_directory_at(source_fd, NULL, AT_FDCWD, arg_target, COPY_REFLINK|COPY_MERGE_EMPTY|COPY_SIGINT|COPY_HARDLINKS);
if (r >= 0)
return 0;
if (r != -ENOTDIR)
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index 8036724034..77f51c4245 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -142,7 +142,7 @@ static int add_cryptsetup(
r = write_drop_in_format(arg_dest, dmname, 50, "job-timeout",
"# Automatically generated by systemd-gpt-auto-generator\n\n"
"[Unit]\n"
- "JobTimeoutSec=0"); /* the binary handles timeouts anyway */
+ "JobTimeoutSec=infinity"); /* the binary handles timeouts anyway */
if (r < 0)
log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m");
diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c
index be52c21d00..1bcf9d69df 100644
--- a/src/hibernate-resume/hibernate-resume-generator.c
+++ b/src/hibernate-resume/hibernate-resume-generator.c
@@ -95,7 +95,8 @@ static int process_resume(void) {
r = write_drop_in(arg_dest, device_unit, 40, "device-timeout",
"# Automatically generated by systemd-hibernate-resume-generator\n\n"
- "[Unit]\nJobTimeoutSec=0");
+ "[Unit]\n"
+ "JobTimeoutSec=infinity\n");
if (r < 0)
log_warning_errno(r, "Failed to write device timeout drop-in: %m");
diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c
index e1f0ff1dcb..2359699801 100644
--- a/src/home/homework-luks.c
+++ b/src/home/homework-luks.c
@@ -2281,7 +2281,7 @@ int home_create_luks(
setup->temporary_image_path = TAKE_PTR(t);
- r = chattr_full(t, setup->image_fd, FS_NOCOW_FL|FS_NOCOMP_FL, FS_NOCOW_FL|FS_NOCOMP_FL, NULL, NULL, CHATTR_FALLBACK_BITWISE);
+ r = chattr_full(setup->image_fd, NULL, FS_NOCOW_FL|FS_NOCOMP_FL, FS_NOCOW_FL|FS_NOCOMP_FL, NULL, NULL, CHATTR_FALLBACK_BITWISE);
if (r < 0 && r != -ENOANO) /* ENOANO → some bits didn't work; which we skip logging about because chattr_full() already debug logs about those flags */
log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set file attributes on %s, ignoring: %m", setup->temporary_image_path);
diff --git a/src/import/export-tar.c b/src/import/export-tar.c
index 4aa8204983..2f54cd8114 100644
--- a/src/import/export-tar.c
+++ b/src/import/export-tar.c
@@ -293,7 +293,7 @@ int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType
return r;
/* Let's try to make a snapshot, if we can, so that the export is atomic */
- r = btrfs_subvol_snapshot_fd(sfd, e->temp_path, BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_RECURSIVE);
+ r = btrfs_subvol_snapshot_at(sfd, NULL, AT_FDCWD, e->temp_path, BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_RECURSIVE);
if (r < 0) {
log_debug_errno(r, "Couldn't create snapshot %s of %s, not exporting atomically: %m", e->temp_path, path);
e->temp_path = mfree(e->temp_path);
diff --git a/src/import/import-fs.c b/src/import/import-fs.c
index d81dd13ffd..fd79c8f01a 100644
--- a/src/import/import-fs.c
+++ b/src/import/import-fs.c
@@ -194,9 +194,9 @@ static int import_fs(int argc, char *argv[], void *userdata) {
BLOCK_SIGNALS(SIGINT, SIGTERM);
if (arg_btrfs_subvol)
- r = btrfs_subvol_snapshot_fd_full(
- fd,
- dest,
+ r = btrfs_subvol_snapshot_at_full(
+ fd, NULL,
+ AT_FDCWD, dest,
BTRFS_SNAPSHOT_FALLBACK_COPY|
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
BTRFS_SNAPSHOT_RECURSIVE|
@@ -206,9 +206,9 @@ static int import_fs(int argc, char *argv[], void *userdata) {
progress_bytes,
&progress);
else
- r = copy_directory_fd_full(
- fd,
- dest,
+ r = copy_directory_at_full(
+ fd, NULL,
+ AT_FDCWD, dest,
COPY_REFLINK|
COPY_SAME_MOUNT|
COPY_HARDLINKS|
diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c
index f22eb0e3a3..e474dffa72 100644
--- a/src/import/pull-tar.c
+++ b/src/import/pull-tar.c
@@ -237,9 +237,9 @@ static int tar_pull_make_local_copy(TarPull *i) {
return log_error_errno(r, "Failed to generate temporary filename for %s: %m", p);
if (i->flags & PULL_BTRFS_SUBVOL)
- r = btrfs_subvol_snapshot(
- i->final_path,
- t,
+ r = btrfs_subvol_snapshot_at(
+ AT_FDCWD, i->final_path,
+ AT_FDCWD, t,
(i->flags & PULL_BTRFS_QUOTA ? BTRFS_SNAPSHOT_QUOTA : 0)|
BTRFS_SNAPSHOT_FALLBACK_COPY|
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
diff --git a/src/integritysetup/integritysetup-generator.c b/src/integritysetup/integritysetup-generator.c
index 15f508902d..5df6d81a84 100644
--- a/src/integritysetup/integritysetup-generator.c
+++ b/src/integritysetup/integritysetup-generator.c
@@ -100,7 +100,7 @@ static int create_disk(
"[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
- "TimeoutSec=0\n"
+ "TimeoutSec=infinity\n"
"ExecStart=" ROOTLIBEXECDIR "/systemd-integritysetup attach '%s' '%s' '%s' '%s'\n"
"ExecStop=" ROOTLIBEXECDIR "/systemd-integritysetup detach '%s'\n",
name_escaped, device, empty_to_dash(key_file_escaped), empty_to_dash(options),
diff --git a/src/kernel-install/60-ukify.install.in b/src/kernel-install/60-ukify.install.in
index 7c29f7e8af..0927bd7a2e 100755
--- a/src/kernel-install/60-ukify.install.in
+++ b/src/kernel-install/60-ukify.install.in
@@ -183,11 +183,10 @@ def call_ukify(opts):
# The solution with runpy gives a dictionary, which isn't great, but will do.
ukify = runpy.run_path(UKIFY, run_name='ukify')
- # Create "empty" namespace. We want to override just a few settings,
- # so it doesn't make sense to duplicate all the fields. We use a hack
- # to pre-populate the namespace like argparse would, all defaults.
- # We need to specify the two mandatory arguments to not get an error.
- opts2 = ukify['create_parser']().parse_args(('A','B'))
+ # Create "empty" namespace. We want to override just a few settings, so it
+ # doesn't make sense to configure everything. We pretend to parse an empty
+ # argument set to prepopulate the namespace with the defaults.
+ opts2 = ukify['create_parser']().parse_args(())
opts2.config = config_file_location()
opts2.uname = opts.kernel_version
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index 936a3577f5..ea13e02719 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -826,4 +826,5 @@ global:
sd_pid_notify_barrier;
sd_event_source_leave_ratelimit;
sd_journal_step_one;
+ sd_session_get_leader;
} LIBSYSTEMD_253;
diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c
index c5d54bbf74..4d09b15653 100644
--- a/src/libsystemd/sd-login/sd-login.c
+++ b/src/libsystemd/sd-login/sd-login.c
@@ -870,6 +870,25 @@ _public_ int sd_session_get_remote_host(const char *session, char **remote_host)
return session_get_string(session, "REMOTE_HOST", remote_host);
}
+_public_ int sd_session_get_leader(const char *session, pid_t *leader) {
+ _cleanup_free_ char *leader_string = NULL;
+ pid_t pid;
+ int r;
+
+ assert_return(leader, -EINVAL);
+
+ r = session_get_string(session, "LEADER", &leader_string);
+ if (r < 0)
+ return r;
+
+ r = parse_pid(leader_string, &pid);
+ if (r < 0)
+ return r;
+
+ *leader = pid;
+ return 0;
+}
+
_public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
_cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
int r;
diff --git a/src/login/systemd-user.in b/src/login/systemd-user.in
index 9a665bd959..8a3c9e0165 100644
--- a/src/login/systemd-user.in
+++ b/src/login/systemd-user.in
@@ -19,4 +19,5 @@ session required pam_namespace.so
{% if ENABLE_HOMED %}
-session optional pam_systemd_home.so
{% endif %}
+session optional pam_umask.so silent
session optional pam_systemd.so
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index f54e955cae..ab61184d9f 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -5564,13 +5564,13 @@ static int run(int argc, char *argv[]) {
{
BLOCK_SIGNALS(SIGINT);
- r = btrfs_subvol_snapshot(arg_directory, np,
- (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
- BTRFS_SNAPSHOT_FALLBACK_COPY |
- BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
- BTRFS_SNAPSHOT_RECURSIVE |
- BTRFS_SNAPSHOT_QUOTA |
- BTRFS_SNAPSHOT_SIGINT);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, arg_directory, AT_FDCWD, np,
+ (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
+ BTRFS_SNAPSHOT_FALLBACK_COPY |
+ BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
+ BTRFS_SNAPSHOT_RECURSIVE |
+ BTRFS_SNAPSHOT_QUOTA |
+ BTRFS_SNAPSHOT_SIGINT);
}
if (r == -EINTR) {
log_error_errno(r, "Interrupted while copying file system tree to %s, removed again.", np);
@@ -5605,14 +5605,14 @@ static int run(int argc, char *argv[]) {
{
BLOCK_SIGNALS(SIGINT);
- r = btrfs_subvol_snapshot(arg_template, arg_directory,
- (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
- BTRFS_SNAPSHOT_FALLBACK_COPY |
- BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
- BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE |
- BTRFS_SNAPSHOT_RECURSIVE |
- BTRFS_SNAPSHOT_QUOTA |
- BTRFS_SNAPSHOT_SIGINT);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, arg_template, AT_FDCWD, arg_directory,
+ (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
+ BTRFS_SNAPSHOT_FALLBACK_COPY |
+ BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
+ BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE |
+ BTRFS_SNAPSHOT_RECURSIVE |
+ BTRFS_SNAPSHOT_QUOTA |
+ BTRFS_SNAPSHOT_SIGINT);
}
if (r == -EEXIST)
log_full(arg_quiet ? LOG_DEBUG : LOG_INFO,
diff --git a/src/partition/repart.c b/src/partition/repart.c
index 78bd06a537..4cf4e4c1f7 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -3155,29 +3155,85 @@ static int context_wipe_and_discard(Context *context) {
return 0;
}
+typedef struct DecryptedPartitionTarget {
+ int fd;
+ char *volume;
+ struct crypt_device *device;
+} DecryptedPartitionTarget;
+
+static DecryptedPartitionTarget* decrypted_partition_target_free(DecryptedPartitionTarget *t) {
+#ifdef HAVE_LIBCRYPTSETUP
+ _cleanup_free_ char *name = NULL;
+ int r;
+
+ if (!t)
+ return NULL;
+
+ r = path_extract_filename(t->volume, &name);
+ if (r < 0) {
+ assert(r == -ENOMEM);
+ log_oom();
+ }
+
+ safe_close(t->fd);
+
+ if (name) {
+ /* udev or so might access out block device in the background while we are done. Let's hence
+ * force detach the volume. We sync'ed before, hence this should be safe. */
+ r = sym_crypt_deactivate_by_name(t->device, name, CRYPT_DEACTIVATE_FORCE);
+ if (r < 0)
+ log_error_errno(r, "Failed to deactivate LUKS device: %m");
+ }
+
+ sym_crypt_free(t->device);
+ free(t->volume);
+ free(t);
+#endif
+ return NULL;
+}
+
typedef struct {
LoopDevice *loop;
int fd;
char *path;
int whole_fd;
+ DecryptedPartitionTarget *decrypted;
} PartitionTarget;
static int partition_target_fd(PartitionTarget *t) {
assert(t);
assert(t->loop || t->fd >= 0 || t->whole_fd >= 0);
- return t->loop ? t->loop->fd : t->fd >= 0 ? t->fd : t->whole_fd;
+
+ if (t->decrypted)
+ return t->decrypted->fd;
+
+ if (t->loop)
+ return t->loop->fd;
+
+ if (t->fd >= 0)
+ return t->fd;
+
+ return t->whole_fd;
}
static const char* partition_target_path(PartitionTarget *t) {
assert(t);
assert(t->loop || t->path);
- return t->loop ? t->loop->node : t->path;
+
+ if (t->decrypted)
+ return t->decrypted->volume;
+
+ if (t->loop)
+ return t->loop->node;
+
+ return t->path;
}
static PartitionTarget *partition_target_free(PartitionTarget *t) {
if (!t)
return NULL;
+ decrypted_partition_target_free(t->decrypted);
loop_device_unref(t->loop);
safe_close(t->fd);
unlink_and_free(t->path);
@@ -3285,6 +3341,7 @@ static int partition_target_grow(PartitionTarget *t, uint64_t size) {
int r;
assert(t);
+ assert(!t->decrypted);
if (t->loop) {
r = loop_device_refresh_size(t->loop, UINT64_MAX, size);
@@ -3308,6 +3365,9 @@ static int partition_target_sync(Context *context, Partition *p, PartitionTarget
assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
+ if (t->decrypted && fsync(t->decrypted->fd) < 0)
+ return log_error_errno(errno, "Failed to sync changes to '%s': %m", t->decrypted->volume);
+
if (t->loop) {
r = loop_device_sync(t->loop);
if (r < 0)
@@ -3340,12 +3400,13 @@ static int partition_target_sync(Context *context, Partition *p, PartitionTarget
return 0;
}
-static int partition_encrypt(Context *context, Partition *p, const char *node) {
+static int partition_encrypt(Context *context, Partition *p, PartitionTarget *target, bool offline) {
#if HAVE_LIBCRYPTSETUP && HAVE_CRYPT_SET_DATA_OFFSET && HAVE_CRYPT_REENCRYPT_INIT_BY_PASSPHRASE && HAVE_CRYPT_REENCRYPT
+ const char *node = partition_target_path(target);
struct crypt_params_luks2 luks_params = {
.label = strempty(ASSERT_PTR(p)->new_label),
.sector_size = ASSERT_PTR(context)->sector_size,
- .data_device = node,
+ .data_device = offline ? node : NULL,
};
struct crypt_params_reencrypt reencrypt_params = {
.mode = CRYPT_REENCRYPT_ENCRYPT,
@@ -3358,7 +3419,7 @@ static int partition_encrypt(Context *context, Partition *p, const char *node) {
_cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
_cleanup_fclose_ FILE *h = NULL;
- _cleanup_free_ char *hp = NULL;
+ _cleanup_free_ char *hp = NULL, *vol = NULL, *dm_name = NULL;
const char *passphrase = NULL;
size_t passphrase_size = 0;
const char *vt;
@@ -3374,39 +3435,51 @@ static int partition_encrypt(Context *context, Partition *p, const char *node) {
log_info("Encrypting future partition %" PRIu64 "...", p->partno);
- r = var_tmp_dir(&vt);
- if (r < 0)
- return log_error_errno(r, "Failed to determine temporary files directory: %m");
+ if (offline) {
+ r = var_tmp_dir(&vt);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine temporary files directory: %m");
- r = fopen_temporary_child(vt, &h, &hp);
- if (r < 0)
- return log_error_errno(r, "Failed to create temporary LUKS header file: %m");
+ r = fopen_temporary_child(vt, &h, &hp);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create temporary LUKS header file: %m");
- /* Weird cryptsetup requirement which requires the header file to be the size of at least one sector. */
- r = ftruncate(fileno(h), context->sector_size);
- if (r < 0)
- return log_error_errno(r, "Failed to grow temporary LUKS header file: %m");
+ /* Weird cryptsetup requirement which requires the header file to be the size of at least one
+ * sector. */
+ r = ftruncate(fileno(h), context->sector_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to grow temporary LUKS header file: %m");
+ } else {
+ if (asprintf(&dm_name, "luks-repart-%08" PRIx64, random_u64()) < 0)
+ return log_oom();
+
+ vol = path_join("/dev/mapper/", dm_name);
+ if (!vol)
+ return log_oom();
+ }
- r = sym_crypt_init(&cd, hp);
+ r = sym_crypt_init(&cd, offline ? hp : node);
if (r < 0)
return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", hp);
cryptsetup_enable_logging(cd);
- /* Disable kernel keyring usage by libcryptsetup as a workaround for
- * https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/273. This makes sure that we can do
- * offline encryption even when repart is running in a container. */
- r = sym_crypt_volume_key_keyring(cd, false);
- if (r < 0)
- return log_error_errno(r, "Failed to disable kernel keyring: %m");
+ if (offline) {
+ /* Disable kernel keyring usage by libcryptsetup as a workaround for
+ * https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/273. This makes sure that we can
+ * do offline encryption even when repart is running in a container. */
+ r = sym_crypt_volume_key_keyring(cd, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to disable kernel keyring: %m");
- r = sym_crypt_metadata_locking(cd, false);
- if (r < 0)
- return log_error_errno(r, "Failed to disable metadata locking: %m");
+ r = sym_crypt_metadata_locking(cd, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to disable metadata locking: %m");
- r = sym_crypt_set_data_offset(cd, LUKS2_METADATA_SIZE / 512);
- if (r < 0)
- return log_error_errno(r, "Failed to set data offset: %m");
+ r = sym_crypt_set_data_offset(cd, LUKS2_METADATA_SIZE / 512);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set data offset: %m");
+ }
r = sym_crypt_format(cd,
CRYPT_LUKS2,
@@ -3517,51 +3590,84 @@ static int partition_encrypt(Context *context, Partition *p, const char *node) {
#endif
}
- r = sym_crypt_reencrypt_init_by_passphrase(
- cd,
- NULL,
- passphrase,
- passphrase_size,
- CRYPT_ANY_SLOT,
- 0,
- sym_crypt_get_cipher(cd),
- sym_crypt_get_cipher_mode(cd),
- &reencrypt_params);
- if (r < 0)
- return log_error_errno(r, "Failed to prepare for reencryption: %m");
+ if (offline) {
+ r = sym_crypt_reencrypt_init_by_passphrase(
+ cd,
+ NULL,
+ passphrase,
+ passphrase_size,
+ CRYPT_ANY_SLOT,
+ 0,
+ sym_crypt_get_cipher(cd),
+ sym_crypt_get_cipher_mode(cd),
+ &reencrypt_params);
+ if (r < 0)
+ return log_error_errno(r, "Failed to prepare for reencryption: %m");
- /* crypt_reencrypt_init_by_passphrase() doesn't actually put the LUKS header at the front, we have
- * to do that ourselves. */
+ /* crypt_reencrypt_init_by_passphrase() doesn't actually put the LUKS header at the front, we
+ * have to do that ourselves. */
- sym_crypt_free(cd);
- cd = NULL;
+ sym_crypt_free(cd);
+ cd = NULL;
- r = sym_crypt_init(&cd, node);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", node);
+ r = sym_crypt_init(&cd, node);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", node);
- r = sym_crypt_header_restore(cd, CRYPT_LUKS2, hp);
- if (r < 0)
- return log_error_errno(r, "Failed to place new LUKS header at head of %s: %m", node);
+ r = sym_crypt_header_restore(cd, CRYPT_LUKS2, hp);
+ if (r < 0)
+ return log_error_errno(r, "Failed to place new LUKS header at head of %s: %m", node);
- reencrypt_params.flags &= ~CRYPT_REENCRYPT_INITIALIZE_ONLY;
+ reencrypt_params.flags &= ~CRYPT_REENCRYPT_INITIALIZE_ONLY;
- r = sym_crypt_reencrypt_init_by_passphrase(
- cd,
- NULL,
- passphrase,
- passphrase_size,
- CRYPT_ANY_SLOT,
- 0,
- NULL,
- NULL,
- &reencrypt_params);
- if (r < 0)
- return log_error_errno(r, "Failed to load reencryption context: %m");
+ r = sym_crypt_reencrypt_init_by_passphrase(
+ cd,
+ NULL,
+ passphrase,
+ passphrase_size,
+ CRYPT_ANY_SLOT,
+ 0,
+ NULL,
+ NULL,
+ &reencrypt_params);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load reencryption context: %m");
- r = sym_crypt_reencrypt(cd, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to encrypt %s: %m", node);
+ r = sym_crypt_reencrypt(cd, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to encrypt %s: %m", node);
+ } else {
+ _cleanup_free_ DecryptedPartitionTarget *t = NULL;
+ _cleanup_close_ int dev_fd = -1;
+
+ r = sym_crypt_activate_by_volume_key(
+ cd,
+ dm_name,
+ NULL,
+ VOLUME_KEY_SIZE,
+ arg_discard ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to activate LUKS superblock: %m");
+
+ dev_fd = open(vol, O_RDWR|O_CLOEXEC|O_NOCTTY);
+ if (dev_fd < 0)
+ return log_error_errno(errno, "Failed to open LUKS volume '%s': %m", vol);
+
+ if (flock(dev_fd, LOCK_EX) < 0)
+ return log_error_errno(errno, "Failed to lock '%s': %m", vol);
+
+ t = new(DecryptedPartitionTarget, 1);
+ if (!t)
+ return log_oom();
+
+ *t = (DecryptedPartitionTarget) {
+ .fd = TAKE_FD(dev_fd),
+ .volume = TAKE_PTR(vol),
+ .device = TAKE_PTR(cd),
+ };
+
+ target->decrypted = TAKE_PTR(t);
+ }
log_info("Successfully encrypted future partition %" PRIu64 ".", p->partno);
@@ -3833,6 +3939,12 @@ static int context_copy_blocks(Context *context) {
if (r < 0)
return r;
+ if (p->encrypt != ENCRYPT_OFF && t->loop) {
+ r = partition_encrypt(context, p, t, /* offline = */ false);
+ if (r < 0)
+ return r;
+ }
+
log_info("Copying in '%s' (%s) on block level into future partition %" PRIu64 ".",
p->copy_blocks_path, FORMAT_BYTES(p->copy_blocks_size), p->partno);
@@ -3840,8 +3952,10 @@ static int context_copy_blocks(Context *context) {
if (r < 0)
return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path);
- if (p->encrypt != ENCRYPT_OFF) {
- r = partition_encrypt(context, p, partition_target_path(t));
+ log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
+
+ if (p->encrypt != ENCRYPT_OFF && !t->loop) {
+ r = partition_encrypt(context, p, t, /* offline = */ true);
if (r < 0)
return r;
}
@@ -3850,8 +3964,6 @@ static int context_copy_blocks(Context *context) {
if (r < 0)
return r;
- log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
-
if (p->siblings[VERITY_HASH] && !partition_defer(p->siblings[VERITY_HASH])) {
r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
/* node = */ NULL, partition_target_path(t));
@@ -4073,14 +4185,14 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
sfd, ".",
pfd, fn,
UID_INVALID, GID_INVALID,
- COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN,
+ COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE,
denylist);
} else
r = copy_tree_at(
sfd, ".",
tfd, ".",
UID_INVALID, GID_INVALID,
- COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN,
+ COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE,
denylist);
if (r < 0)
return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m",
@@ -4113,7 +4225,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", *target);
- r = copy_bytes(sfd, tfd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_SIGINT);
+ r = copy_bytes(sfd, tfd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_SIGINT|COPY_TRUNCATE);
if (r < 0)
return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
@@ -4270,6 +4382,16 @@ static int context_mkfs(Context *context) {
if (r < 0)
return r;
+ if (p->encrypt != ENCRYPT_OFF && t->loop) {
+ r = partition_target_grow(t, p->new_size);
+ if (r < 0)
+ return r;
+
+ r = partition_encrypt(context, p, t, /* offline = */ false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to encrypt device: %m");
+ }
+
log_info("Formatting future partition %" PRIu64 ".", p->partno);
/* If we're not writing to a loop device or if we're populating a read-only filesystem, we
@@ -4305,17 +4427,17 @@ static int context_mkfs(Context *context) {
if (partition_needs_populate(p) && !root) {
assert(t->loop);
- r = partition_populate_filesystem(context, p, t->loop->node);
+ r = partition_populate_filesystem(context, p, partition_target_path(t));
if (r < 0)
return r;
}
- if (p->encrypt != ENCRYPT_OFF) {
+ if (p->encrypt != ENCRYPT_OFF && !t->loop) {
r = partition_target_grow(t, p->new_size);
if (r < 0)
return r;
- r = partition_encrypt(context, p, partition_target_path(t));
+ r = partition_encrypt(context, p, t, /* offline = */ true);
if (r < 0)
return log_error_errno(r, "Failed to encrypt device: %m");
}
@@ -4926,7 +5048,7 @@ static int context_split(Context *context) {
if (lseek(fd, p->offset, SEEK_SET) < 0)
return log_error_errno(errno, "Failed to seek to partition offset: %m");
- r = copy_bytes(fd, fdt, p->new_size, COPY_REFLINK|COPY_HOLES);
+ r = copy_bytes(fd, fdt, p->new_size, COPY_REFLINK|COPY_HOLES|COPY_TRUNCATE);
if (r < 0)
return log_error_errno(r, "Failed to copy to split partition %s: %m", p->split_path);
}
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index 574a1a4be9..d63760b7d1 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -911,9 +911,9 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns
if (r < 0)
goto fail;
- /* RFC 2782 states "Unless and until permitted by future standards
- * action, name compression is not to be used for this field." */
- r = dns_packet_append_name(p, rr->srv.name, false, true, NULL);
+ /* RFC 2782 states "Unless and until permitted by future standards action, name compression
+ * is not to be used for this field." Hence we turn off compression here. */
+ r = dns_packet_append_name(p, rr->srv.name, /* allow_compression= */ false, /* canonical_candidate= */ true, NULL);
break;
case DNS_TYPE_PTR:
@@ -1728,7 +1728,13 @@ int dns_packet_read_rr(
r = dns_packet_read_uint16(p, &rr->srv.port, NULL);
if (r < 0)
return r;
- r = dns_packet_read_name(p, &rr->srv.name, true, NULL);
+
+ /* RFC 2782 states "Unless and until permitted by future standards action, name compression
+ * is not to be used for this field." Nonetheless, we support it here, in the interest of
+ * increasing compatibility with implementations that do not implement this correctly. After
+ * all we didn't do this right once upon a time ourselves (see
+ * https://github.com/systemd/systemd/issues/9793). */
+ r = dns_packet_read_name(p, &rr->srv.name, /* allow_compression= */ true, NULL);
break;
case DNS_TYPE_PTR:
diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c
index 16295a5823..429e5afe11 100644
--- a/src/shared/btrfs-util.c
+++ b/src/shared/btrfs-util.c
@@ -17,6 +17,7 @@
#include "alloc-util.h"
#include "blockdev-util.h"
#include "btrfs-util.h"
+#include "chase.h"
#include "chattr-util.h"
#include "copy.h"
#include "fd-util.h"
@@ -70,32 +71,20 @@ static int extract_subvolume_name(const char *path, char **ret) {
return 0;
}
-int btrfs_is_subvol_fd(int fd) {
+int btrfs_is_subvol_at(int dir_fd, const char *path) {
struct stat st;
- assert(fd >= 0);
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
/* On btrfs subvolumes always have the inode 256 */
- if (fstat(fd, &st) < 0)
+ if (fstatat(dir_fd, strempty(path), &st, (isempty(path) ? AT_EMPTY_PATH : 0)) < 0)
return -errno;
if (!btrfs_might_be_subvol(&st))
return 0;
- return fd_is_fs_type(fd, BTRFS_SUPER_MAGIC);
-}
-
-int btrfs_is_subvol(const char *path) {
- _cleanup_close_ int fd = -EBADF;
-
- assert(path);
-
- fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (fd < 0)
- return -errno;
-
- return btrfs_is_subvol_fd(fd);
+ return is_fs_type_at(dir_fd, path, BTRFS_SUPER_MAGIC);
}
int btrfs_subvol_make_fd(int fd, const char *subvolume) {
@@ -163,11 +152,16 @@ int btrfs_subvol_make_fallback(const char *path, mode_t mode) {
return 0; /* plain directory */
}
-int btrfs_subvol_set_read_only_fd(int fd, bool b) {
+int btrfs_subvol_set_read_only_at(int dir_fd, const char *path, bool b) {
+ _cleanup_close_ int fd = -EBADF;
uint64_t flags, nflags;
struct stat st;
- assert(fd >= 0);
+ 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);
+ if (fd < 0)
+ return fd;
if (fstat(fd, &st) < 0)
return -errno;
@@ -185,16 +179,6 @@ int btrfs_subvol_set_read_only_fd(int fd, bool b) {
return RET_NERRNO(ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags));
}
-int btrfs_subvol_set_read_only(const char *path, bool b) {
- _cleanup_close_ int fd = -EBADF;
-
- fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (fd < 0)
- return -errno;
-
- return btrfs_subvol_set_read_only_fd(fd, b);
-}
-
int btrfs_subvol_get_read_only_fd(int fd) {
uint64_t flags;
struct stat st;
@@ -1135,25 +1119,21 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol
return 0;
}
-int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) {
+int btrfs_subvol_remove_at(int dir_fd, const char *path, BtrfsRemoveFlags flags) {
_cleanup_free_ char *subvolume = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
assert(path);
- r = extract_subvolume_name(path, &subvolume);
- if (r < 0)
- return r;
-
- fd = open_parent(path, O_CLOEXEC, 0);
+ fd = chase_and_openat(dir_fd, path, CHASE_PARENT|CHASE_EXTRACT_FILENAME, O_CLOEXEC, &subvolume);
if (fd < 0)
return fd;
- return subvol_remove_children(fd, subvolume, 0, flags);
-}
+ r = validate_subvolume_name(subvolume);
+ if (r < 0)
+ return r;
-int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags) {
return subvol_remove_children(fd, subvolume, 0, flags);
}
@@ -1526,22 +1506,37 @@ static int subvol_snapshot_children(
return 0;
}
-int btrfs_subvol_snapshot_fd_full(
- int old_fd,
- const char *new_path,
+int btrfs_subvol_snapshot_at_full(
+ int dir_fdf,
+ const char *from,
+ int dir_fdt,
+ const char *to,
BtrfsSnapshotFlags flags,
copy_progress_path_t progress_path,
copy_progress_bytes_t progress_bytes,
void *userdata) {
_cleanup_free_ char *subvolume = NULL;
- _cleanup_close_ int new_fd = -EBADF;
+ _cleanup_close_ int old_fd = -EBADF, new_fd = -EBADF;
int r;
- assert(old_fd >= 0);
- assert(new_path);
+ assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD);
+ 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);
+ if (old_fd < 0)
+ return old_fd;
- r = btrfs_is_subvol_fd(old_fd);
+ new_fd = chase_and_openat(dir_fdt, to, CHASE_PARENT|CHASE_EXTRACT_FILENAME, O_CLOEXEC, &subvolume);
+ if (new_fd < 0)
+ return new_fd;
+
+ r = validate_subvolume_name(subvolume);
+ if (r < 0)
+ return r;
+
+ r = btrfs_is_subvol_at(dir_fdf, from);
if (r < 0)
return r;
if (r == 0) {
@@ -1551,18 +1546,19 @@ int btrfs_subvol_snapshot_fd_full(
if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
return -EISDIR;
- r = btrfs_subvol_make(new_path);
+ r = btrfs_subvol_make_fd(new_fd, subvolume);
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 (mkdir(new_path, 0755) < 0)
+ if (mkdirat(new_fd, subvolume, 0755) < 0)
return -errno;
plain_directory = true;
} else if (r < 0)
return r;
- r = copy_directory_fd_full(
- old_fd, new_path,
+ r = copy_directory_at_full(
+ dir_fdf, from,
+ new_fd, subvolume,
COPY_MERGE_EMPTY|
COPY_REFLINK|
COPY_SAME_MOUNT|
@@ -1583,9 +1579,9 @@ int btrfs_subvol_snapshot_fd_full(
* it: the IMMUTABLE bit. Let's use this here, if this is requested. */
if (flags & BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE)
- (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
+ (void) chattr_at(new_fd, subvolume, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
} else {
- r = btrfs_subvol_set_read_only(new_path, true);
+ r = btrfs_subvol_set_read_only_at(new_fd, subvolume, true);
if (r < 0)
goto fallback_fail;
}
@@ -1594,41 +1590,13 @@ int btrfs_subvol_snapshot_fd_full(
return 0;
fallback_fail:
- (void) rm_rf(new_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+ (void) rm_rf_at(new_fd, subvolume, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
return r;
}
- r = extract_subvolume_name(new_path, &subvolume);
- if (r < 0)
- return r;
-
- new_fd = open_parent(new_path, O_CLOEXEC, 0);
- if (new_fd < 0)
- return new_fd;
-
return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
}
-int btrfs_subvol_snapshot_full(
- const char *old_path,
- const char *new_path,
- BtrfsSnapshotFlags flags,
- copy_progress_path_t progress_path,
- copy_progress_bytes_t progress_bytes,
- void *userdata) {
-
- _cleanup_close_ int old_fd = -EBADF;
-
- assert(old_path);
- assert(new_path);
-
- old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (old_fd < 0)
- return -errno;
-
- return btrfs_subvol_snapshot_fd_full(old_fd, new_path, flags, progress_path, progress_bytes, userdata);
-}
-
int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret) {
struct btrfs_ioctl_search_args args = {
diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h
index 0ce6d4b96a..de38f1e45c 100644
--- a/src/shared/btrfs-util.h
+++ b/src/shared/btrfs-util.h
@@ -43,8 +43,13 @@ typedef enum BtrfsRemoveFlags {
BTRFS_REMOVE_QUOTA = 1 << 1,
} BtrfsRemoveFlags;
-int btrfs_is_subvol_fd(int fd);
-int btrfs_is_subvol(const char *path);
+int btrfs_is_subvol_at(int dir_fd, const char *path);
+static inline int btrfs_is_subvol_fd(int fd) {
+ return btrfs_is_subvol_at(fd, NULL);
+}
+static inline int btrfs_is_subvol(const char *path) {
+ return btrfs_is_subvol_at(AT_FDCWD, path);
+}
int btrfs_get_block_device_at(int dir_fd, const char *path, dev_t *ret);
static inline int btrfs_get_block_device(const char *path, dev_t *ret) {
@@ -69,21 +74,24 @@ int btrfs_subvol_make_fd(int fd, const char *subvolume);
int btrfs_subvol_make_fallback(const char *path, mode_t);
-int btrfs_subvol_snapshot_fd_full(int old_fd, const char *new_path, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
-static inline int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
- return btrfs_subvol_snapshot_fd_full(old_fd, new_path, flags, NULL, NULL, NULL);
+int btrfs_subvol_snapshot_at_full(int dir_fdf, const char *from, int dir_fdt, const char *to, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
+static inline int btrfs_subvol_snapshot_at(int dir_fdf, const char *from, int dir_fdt, const char *to, BtrfsSnapshotFlags flags) {
+ return btrfs_subvol_snapshot_at_full(dir_fdf, from, dir_fdt, to, flags, NULL, NULL, NULL);
}
-int btrfs_subvol_snapshot_full(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
-static inline int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
- return btrfs_subvol_snapshot_full(old_path, new_path, flags, NULL, NULL, NULL);
+int btrfs_subvol_remove_at(int dir_fd, const char *path, BtrfsRemoveFlags flags);
+static inline int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) {
+ return btrfs_subvol_remove_at(AT_FDCWD, path, flags);
}
-int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags);
-int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags);
+int btrfs_subvol_set_read_only_at(int dir_fd, const char *path, bool b);
+static inline int btrfs_subvol_set_read_only_fd(int fd, bool b) {
+ return btrfs_subvol_set_read_only_at(fd, NULL, b);
+}
+static inline int btrfs_subvol_set_read_only(const char *path, bool b) {
+ return btrfs_subvol_set_read_only_at(AT_FDCWD, path, b);
+}
-int btrfs_subvol_set_read_only_fd(int fd, bool b);
-int btrfs_subvol_set_read_only(const char *path, bool b);
int btrfs_subvol_get_read_only_fd(int fd);
int btrfs_subvol_get_id(int fd, const char *subvolume, uint64_t *ret);
diff --git a/src/shared/copy.c b/src/shared/copy.c
index 14b9b61d8a..6b9d4b9942 100644
--- a/src/shared/copy.c
+++ b/src/shared/copy.c
@@ -245,7 +245,7 @@ int copy_bytes_full(
ssize_t n;
if (max_bytes <= 0)
- return 1; /* return > 0 if we hit the max_bytes limit */
+ break;
r = look_for_signals(copy_flags);
if (r < 0)
@@ -468,7 +468,16 @@ int copy_bytes_full(
copied_something = true;
}
- return 0; /* return 0 if we hit EOF earlier than the size limit */
+ if (copy_flags & COPY_TRUNCATE) {
+ off_t off = lseek(fdt, 0, SEEK_CUR);
+ if (off < 0)
+ return -errno;
+
+ if (ftruncate(fdt, off) < 0)
+ return -errno;
+ }
+
+ return max_bytes <= 0; /* return 0 if we hit EOF earlier than the size limit */
}
static int fd_copy_symlink(
@@ -1208,61 +1217,22 @@ int copy_tree_at_full(
return 0;
}
-static int sync_dir_by_flags(const char *path, CopyFlags copy_flags) {
+static int sync_dir_by_flags(int dir_fd, const char *path, CopyFlags copy_flags) {
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+ assert(path);
if (copy_flags & COPY_SYNCFS)
- return syncfs_path(AT_FDCWD, path);
+ return syncfs_path(dir_fd, path);
if (copy_flags & COPY_FSYNC_FULL)
- return fsync_parent_at(AT_FDCWD, path);
-
- return 0;
-}
-
-int copy_directory_fd_full(
- int dirfd,
- const char *to,
- CopyFlags copy_flags,
- copy_progress_path_t progress_path,
- copy_progress_bytes_t progress_bytes,
- void *userdata) {
-
- struct stat st;
- int r;
-
- assert(dirfd >= 0);
- assert(to);
-
- if (fstat(dirfd, &st) < 0)
- return -errno;
-
- r = stat_verify_directory(&st);
- if (r < 0)
- return r;
-
- r = fd_copy_directory(
- dirfd, NULL,
- &st,
- AT_FDCWD, to,
- st.st_dev,
- COPY_DEPTH_MAX,
- UID_INVALID, GID_INVALID,
- copy_flags,
- NULL, 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 fsync_parent_at(dir_fd, path);
return 0;
}
-int copy_directory_full(
+int copy_directory_at_full(
+ int dir_fdf,
const char *from,
+ int dir_fdt,
const char *to,
CopyFlags copy_flags,
copy_progress_path_t progress_path,
@@ -1272,10 +1242,11 @@ int copy_directory_full(
struct stat st;
int r;
- assert(from);
+ assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD);
+ assert(dir_fdt >= 0 || dir_fdt == AT_FDCWD);
assert(to);
- if (lstat(from, &st) < 0)
+ if (fstatat(dir_fdf, strempty(from), &st, AT_SYMLINK_NOFOLLOW|(isempty(from) ? AT_EMPTY_PATH : 0)) < 0)
return -errno;
r = stat_verify_directory(&st);
@@ -1283,9 +1254,9 @@ int copy_directory_full(
return r;
r = fd_copy_directory(
- AT_FDCWD, from,
+ dir_fdf, from,
&st,
- AT_FDCWD, to,
+ dir_fdt, to,
st.st_dev,
COPY_DEPTH_MAX,
UID_INVALID, GID_INVALID,
@@ -1297,7 +1268,7 @@ int copy_directory_full(
if (r < 0)
return r;
- r = sync_dir_by_flags(to, copy_flags);
+ r = sync_dir_by_flags(dir_fdt, to, copy_flags);
if (r < 0)
return r;
diff --git a/src/shared/copy.h b/src/shared/copy.h
index c4482eba7e..da084b66f2 100644
--- a/src/shared/copy.h
+++ b/src/shared/copy.h
@@ -28,6 +28,7 @@ typedef enum CopyFlags {
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 */
} CopyFlags;
typedef enum DenyType {
@@ -82,14 +83,9 @@ static inline int copy_tree(const char *from, const char *to, uid_t override_uid
return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, denylist, NULL, NULL, NULL);
}
-int copy_directory_fd_full(int dirfd, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
-static inline int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
- return copy_directory_fd_full(dirfd, to, copy_flags, NULL, NULL, NULL);
-}
-
-int copy_directory_full(const char *from, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
-static inline int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
- return copy_directory_full(from, to, copy_flags, NULL, NULL, NULL);
+int copy_directory_at_full(int dir_fdf, const char *from, int dir_fdt, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
+static inline int copy_directory_at(int dir_fdf, const char *from, int dir_fdt, const char *to, CopyFlags copy_flags) {
+ return copy_directory_at_full(dir_fdf, from, dir_fdt, to, copy_flags, NULL, NULL, NULL);
}
int copy_bytes_full(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags, void **ret_remains, size_t *ret_remains_size, copy_progress_bytes_t progress, void *userdata);
diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c
index 198c975c44..d93a1cc74c 100644
--- a/src/shared/discover-image.c
+++ b/src/shared/discover-image.c
@@ -923,13 +923,13 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
new_path = strjoina("/var/lib/machines/", new_name);
- r = btrfs_subvol_snapshot(i->path, new_path,
- (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
- BTRFS_SNAPSHOT_FALLBACK_COPY |
- BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
- BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE |
- BTRFS_SNAPSHOT_RECURSIVE |
- BTRFS_SNAPSHOT_QUOTA);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, i->path, AT_FDCWD, new_path,
+ (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
+ BTRFS_SNAPSHOT_FALLBACK_COPY |
+ BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
+ BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE |
+ BTRFS_SNAPSHOT_RECURSIVE |
+ BTRFS_SNAPSHOT_QUOTA);
if (r >= 0)
/* Enable "subtree" quotas for the copy, if we didn't copy any quota from the source. */
(void) btrfs_subvol_auto_qgroup(new_path, 0, true);
diff --git a/src/shared/generator.c b/src/shared/generator.c
index 49c5fe5c05..44ed319922 100644
--- a/src/shared/generator.c
+++ b/src/shared/generator.c
@@ -235,7 +235,7 @@ static int write_fsck_sysroot_service(
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"ExecStart=" SYSTEMD_FSCK_PATH " %7$s\n"
- "TimeoutSec=0\n",
+ "TimeoutSec=infinity\n",
program_invocation_short_name,
escaped,
unit,
@@ -530,7 +530,7 @@ int generator_hook_up_mkswap(
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"ExecStart="SYSTEMD_MAKEFS_PATH " swap %s\n"
- "TimeoutSec=0\n",
+ "TimeoutSec=infinity\n",
program_invocation_short_name,
where_unit,
escaped);
@@ -619,7 +619,7 @@ int generator_hook_up_mkfs(
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"ExecStart="SYSTEMD_MAKEFS_PATH " %s %s\n"
- "TimeoutSec=0\n",
+ "TimeoutSec=infinity\n",
program_invocation_short_name,
fsck_unit,
where_unit,
@@ -801,7 +801,7 @@ int generator_write_cryptsetup_service_section(
"[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
- "TimeoutSec=0\n" /* The binary handles timeouts on its own */
+ "TimeoutSec=infinity\n" /* The binary handles timeouts on its own */
"KeyringMode=shared\n" /* Make sure we can share cached keys among instances */
"OOMScoreAdjust=500\n" /* Unlocking can allocate a lot of memory if Argon2 is used */
"ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
diff --git a/src/shared/rm-rf.c b/src/shared/rm-rf.c
index 8e459f9d69..3a34281bb9 100644
--- a/src/shared/rm-rf.c
+++ b/src/shared/rm-rf.c
@@ -228,7 +228,7 @@ static int rm_rf_inner_child(
if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) {
/* This could be a subvolume, try to remove it */
- r = btrfs_subvol_remove_fd(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
+ r = btrfs_subvol_remove_at(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
if (r < 0) {
if (!IN_SET(r, -ENOTTY, -EINVAL))
return r;
@@ -425,11 +425,14 @@ static int rm_rf_children_impl(
return ret;
}
-int rm_rf(const char *path, RemoveFlags flags) {
+int rm_rf_at(int dir_fd, const char *path, RemoveFlags flags) {
mode_t old_mode;
int fd, r, q = 0;
- assert(path);
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+
+ if (FLAGS_SET(flags, REMOVE_ROOT))
+ assert(path && !dot_or_dot_dot(path)); /* unlinkat() does not support AT_EMPTY_PATH or "." so a path must be provided here. */
/* For now, don't support dropping subvols when also only dropping directories, since we can't do
* this race-freely. */
@@ -438,14 +441,13 @@ int rm_rf(const char *path, RemoveFlags flags) {
/* We refuse to clean the root file system with this call. This is extra paranoia to never cause a
* really seriously broken system. */
- if (path_equal_or_inode_same(path, "/", AT_SYMLINK_NOFOLLOW))
+ if (path_is_root_at(dir_fd, path) > 0)
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
- "Attempted to remove entire root file system (\"%s\"), and we can't allow that.",
- path);
+ "Attempted to remove entire root file system, and we can't allow that.");
if (FLAGS_SET(flags, REMOVE_SUBVOLUME | REMOVE_ROOT | REMOVE_PHYSICAL)) {
/* Try to remove as subvolume first */
- r = btrfs_subvol_remove(path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
+ r = btrfs_subvol_remove_at(dir_fd, path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
if (r >= 0)
return r;
@@ -458,13 +460,13 @@ int rm_rf(const char *path, RemoveFlags flags) {
/* Not btrfs or not a subvolume */
}
- fd = openat_harder(AT_FDCWD, path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME, flags, &old_mode);
+ fd = openat_harder(dir_fd, path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME, flags, &old_mode);
if (fd >= 0) {
/* We have a dir */
r = rm_rf_children_impl(fd, flags, NULL, old_mode);
if (FLAGS_SET(flags, REMOVE_ROOT))
- q = RET_NERRNO(rmdir(path));
+ q = RET_NERRNO(unlinkat(dir_fd, path, AT_REMOVEDIR));
} else {
r = fd;
if (FLAGS_SET(flags, REMOVE_MISSING_OK) && r == -ENOENT)
@@ -479,8 +481,9 @@ int rm_rf(const char *path, RemoveFlags flags) {
if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) {
struct statfs s;
- if (statfs(path, &s) < 0)
- return -errno;
+ r = xstatfsat(dir_fd, path, &s);
+ if (r < 0)
+ return r;
if (is_physical_fs(&s))
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
"Attempted to remove files from a disk file system under \"%s\", refusing.",
@@ -488,7 +491,7 @@ int rm_rf(const char *path, RemoveFlags flags) {
}
r = 0;
- q = RET_NERRNO(unlink(path));
+ q = RET_NERRNO(unlinkat(dir_fd, path, 0));
}
if (r < 0)
diff --git a/src/shared/rm-rf.h b/src/shared/rm-rf.h
index 24fd9a2aa2..9d93692e0a 100644
--- a/src/shared/rm-rf.h
+++ b/src/shared/rm-rf.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <fcntl.h>
#include <sys/stat.h>
#include "alloc-util.h"
@@ -26,7 +27,10 @@ int fstatat_harder(int dfd,
int rm_rf_children(int fd, RemoveFlags flags, const struct stat *root_dev);
int rm_rf_child(int fd, const char *name, RemoveFlags flags);
-int rm_rf(const char *path, RemoveFlags flags);
+int rm_rf_at(int dir_fd, const char *path, RemoveFlags flags);
+static inline int rm_rf(const char *path, RemoveFlags flags) {
+ return rm_rf_at(AT_FDCWD, path, flags);
+}
/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
static inline char *rm_rf_physical_and_free(char *p) {
diff --git a/src/systemd/sd-login.h b/src/systemd/sd-login.h
index 7246d9f472..4700a76441 100644
--- a/src/systemd/sd-login.h
+++ b/src/systemd/sd-login.h
@@ -184,6 +184,9 @@ int sd_session_get_desktop(const char *session, char **desktop);
/* Determine the X11 display of this session. */
int sd_session_get_display(const char *session, char **display);
+/* Determine the leader process of this session. */
+int sd_session_get_leader(const char *session, pid_t *leader);
+
/* Determine the remote host of this session. */
int sd_session_get_remote_host(const char *session, char **remote_host);
diff --git a/src/test/meson.build b/src/test/meson.build
index 84d642bf6e..fe1a0fec21 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -15,6 +15,10 @@ test_env.set('SYSTEMD_LANGUAGE_FALLBACK_MAP', language_fallback_map)
test_env.set('PATH', project_build_root + ':' + path)
test_env.set('PROJECT_BUILD_ROOT', project_build_root)
+if efi_addon != ''
+ test_env.set('EFI_ADDON', efi_addon)
+endif
+
############################################################
generate_sym_test_py = find_program('generate-sym-test.py')
diff --git a/src/test/test-btrfs.c b/src/test/test-btrfs.c
index 67acba23a7..ccb1661a91 100644
--- a/src/test/test-btrfs.c
+++ b/src/test/test-btrfs.c
@@ -54,11 +54,11 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "Failed to write file: %m");
- r = btrfs_subvol_snapshot("/xxxtest", "/xxxtest2", 0);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, "/xxxtest", AT_FDCWD, "/xxxtest2", 0);
if (r < 0)
log_error_errno(r, "Failed to make snapshot: %m");
- r = btrfs_subvol_snapshot("/xxxtest", "/xxxtest3", BTRFS_SNAPSHOT_READ_ONLY);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, "/xxxtest", AT_FDCWD, "/xxxtest3", BTRFS_SNAPSHOT_READ_ONLY);
if (r < 0)
log_error_errno(r, "Failed to make snapshot: %m");
@@ -74,7 +74,8 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
- r = btrfs_subvol_snapshot("/etc", "/etc2", BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_FALLBACK_COPY);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, "/etc", AT_FDCWD, "/etc2",
+ BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_FALLBACK_COPY);
if (r < 0)
log_error_errno(r, "Failed to make snapshot: %m");
@@ -115,7 +116,7 @@ int main(int argc, char *argv[]) {
if (mkdir("/xxxrectest/mnt", 0755) < 0)
log_error_errno(errno, "Failed to make directory: %m");
- r = btrfs_subvol_snapshot("/xxxrectest", "/xxxrectest2", BTRFS_SNAPSHOT_RECURSIVE);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, "/xxxrectest", AT_FDCWD, "/xxxrectest2", BTRFS_SNAPSHOT_RECURSIVE);
if (r < 0)
log_error_errno(r, "Failed to snapshot subvolume: %m");
@@ -151,7 +152,8 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "Failed to set up quota limit: %m");
- r = btrfs_subvol_snapshot("/xxxquotatest", "/xxxquotatest2", BTRFS_SNAPSHOT_RECURSIVE|BTRFS_SNAPSHOT_QUOTA);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, "/xxxquotatest", AT_FDCWD, "/xxxquotatest2",
+ BTRFS_SNAPSHOT_RECURSIVE|BTRFS_SNAPSHOT_QUOTA);
if (r < 0)
log_error_errno(r, "Failed to set up snapshot: %m");
diff --git a/src/test/test-chase.c b/src/test/test-chase.c
index 558f4109e3..75c508970e 100644
--- a/src/test/test-chase.c
+++ b/src/test/test-chase.c
@@ -17,6 +17,19 @@
static const char *arg_test_dir = NULL;
+static void test_chase_extract_filename_one(const char *path, const char *root, const char *expected) {
+ _cleanup_free_ char *ret1 = NULL, *ret2 = NULL, *fname = NULL;
+
+ log_debug("/* %s(path=%s, root=%s) */", __func__, path, strnull(root));
+
+ assert_se(chase(path, root, CHASE_EXTRACT_FILENAME, &ret1, NULL) > 0);
+ assert_se(streq(ret1, expected));
+
+ assert_se(chase(path, root, 0, &ret2, NULL) > 0);
+ assert_se(chase_extract_filename(ret2, root, &fname) >= 0);
+ assert_se(streq(fname, expected));
+}
+
TEST(chase) {
_cleanup_free_ char *result = NULL, *pwd = NULL;
_cleanup_close_ int pfd = -EBADF;
@@ -111,6 +124,19 @@ TEST(chase) {
assert_se(path_equal(result, temp));
result = mfree(result);
+ /* Tests for CHASE_EXTRACT_FILENAME and chase_extract_filename() */
+
+ p = strjoina(temp, "/start");
+ pslash = strjoina(p, "/");
+ test_chase_extract_filename_one(p, NULL, "usr");
+ test_chase_extract_filename_one(pslash, NULL, "usr");
+ test_chase_extract_filename_one(p, temp, "usr");
+ test_chase_extract_filename_one(pslash, temp, "usr");
+
+ p = strjoina(temp, "/slash");
+ test_chase_extract_filename_one(p, NULL, ".");
+ test_chase_extract_filename_one(p, temp, ".");
+
/* Paths that would "escape" outside of the "root" */
p = strjoina(temp, "/6dots");
@@ -548,7 +574,7 @@ TEST(chaseat) {
assert_se(chaseat(tfd, "i/../p", CHASE_MKDIR_0755|CHASE_NONEXISTENT, NULL, NULL) == -ENOENT);
- /* Test CHASE_FILENAME */
+ /* Test CHASE_EXTRACT_FILENAME */
assert_se(chaseat(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW|CHASE_EXTRACT_FILENAME, &result, &fd) >= 0);
assert_se(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW) >= 0);
@@ -570,6 +596,10 @@ TEST(chaseat) {
assert_se(streq(result, "."));
result = mfree(result);
+ assert_se(chaseat(tfd, NULL, CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL) >= 0);
+ assert_se(streq(result, "."));
+ result = mfree(result);
+
/* Test chase_and_openat() */
fd = chase_and_openat(tfd, "o/p/e/n/f/i/l/e", CHASE_MKDIR_0755, O_CREAT|O_EXCL|O_CLOEXEC, NULL);
@@ -582,6 +612,12 @@ TEST(chaseat) {
assert_se(fd_verify_directory(fd) >= 0);
fd = safe_close(fd);
+ fd = chase_and_openat(tfd, NULL, CHASE_PARENT|CHASE_EXTRACT_FILENAME, O_PATH|O_DIRECTORY|O_CLOEXEC, &result);
+ assert_se(fd >= 0);
+ assert_se(streq(result, "."));
+ fd = safe_close(fd);
+ result = mfree(result);
+
/* Test chase_and_openatdir() */
assert_se(chase_and_opendirat(tfd, "o/p/e/n/d/i", 0, &result, &dir) >= 0);
@@ -643,11 +679,6 @@ TEST(chaseat) {
result = mfree(result);
}
-static int intro(void) {
- arg_test_dir = saved_argv[1];
- return EXIT_SUCCESS;
-}
-
TEST(chaseat_prefix_root) {
_cleanup_free_ char *cwd = NULL, *ret = NULL, *expected = NULL;
@@ -685,4 +716,9 @@ TEST(chaseat_prefix_root) {
assert_se(streq(ret, expected));
}
+static int intro(void) {
+ arg_test_dir = saved_argv[1];
+ return EXIT_SUCCESS;
+}
+
DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index a07c837e3f..a63afa873b 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -1299,7 +1299,7 @@ static int prepare_ns(const char *process_name) {
/* Copy unit files to make them accessible even when unprivileged. */
assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0);
- assert_se(copy_directory(unit_dir, PRIVATE_UNIT_DIR, COPY_MERGE_EMPTY) >= 0);
+ assert_se(copy_directory_at(AT_FDCWD, unit_dir, AT_FDCWD, PRIVATE_UNIT_DIR, COPY_MERGE_EMPTY) >= 0);
/* Prepare credstore like tmpfiles.d/credstore.conf for LoadCredential= tests. */
FOREACH_STRING(p, "/run/credstore", "/run/credstore.encrypted") {
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
index 6b685a816f..31e2a3d296 100644
--- a/src/test/test-path-util.c
+++ b/src/test/test-path-util.c
@@ -600,6 +600,17 @@ TEST(path_startswith) {
test_path_startswith_one("/foo/bar/barfoo/", "////foo/bar/barfoo/", "/foo/bar/barfoo/", "");
test_path_startswith_one("/foo/bar/barfoo/", "/foo/bar/barfoo", "/foo/bar/barfoo/", "");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/foo", "/foo/./", "bar///barfoo/./.");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/foo/", "/foo/./", "bar///barfoo/./.");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/", "/", "foo/./bar///barfoo/./.");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "////", "/", "foo/./bar///barfoo/./.");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/foo//bar/////barfoo///", "/foo/./bar///barfoo/./.", "");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/foo/bar/barfoo////", "/foo/./bar///barfoo/./.", "");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/foo/bar///barfoo/", "/foo/./bar///barfoo/./.", "");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/foo////bar/barfoo/", "/foo/./bar///barfoo/./.", "");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "////foo/bar/barfoo/", "/foo/./bar///barfoo/./.", "");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/foo/bar/barfoo", "/foo/./bar///barfoo/./.", "");
+
test_path_startswith_one("/foo/bar/barfoo/", "/foo/bar/barfooa/", NULL, NULL);
test_path_startswith_one("/foo/bar/barfoo/", "/foo/bar/barfooa", NULL, NULL);
test_path_startswith_one("/foo/bar/barfoo/", "", NULL, NULL);
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index af1b904d32..e0087d997b 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -1540,7 +1540,7 @@ static int fd_set_attribute(
return log_error_errno(procfs_fd, "Failed to re-open '%s': %m", path);
unsigned previous, current;
- r = chattr_full(NULL, procfs_fd, f, item->attribute_mask, &previous, &current, CHATTR_FALLBACK_BITWISE);
+ r = chattr_full(procfs_fd, NULL, f, item->attribute_mask, &previous, &current, CHATTR_FALLBACK_BITWISE);
if (r == -ENOANO)
log_warning("Cannot set file attributes for '%s', maybe due to incompatibility in specified attributes, "
"previous=0x%08x, current=0x%08x, expected=0x%08x, ignoring.",
diff --git a/src/ukify/test/test_ukify.py b/src/ukify/test/test_ukify.py
index 692b7a384b..eae82c7f88 100755
--- a/src/ukify/test/test_ukify.py
+++ b/src/ukify/test/test_ukify.py
@@ -50,9 +50,9 @@ def test_round_up():
assert ukify.round_up(4097) == 8192
def test_namespace_creation():
- ns = ukify.create_parser().parse_args(('A','B'))
- assert ns.linux == pathlib.Path('A')
- assert ns.initrd == [pathlib.Path('B')]
+ ns = ukify.create_parser().parse_args(())
+ assert ns.linux is None
+ assert ns.initrd is None
def test_config_example():
ex = ukify.config_example()
@@ -87,7 +87,7 @@ def test_apply_config(tmp_path):
Phases = {':'.join(ukify.KNOWN_PHASES)}
'''))
- ns = ukify.create_parser().parse_args(('A','B'))
+ ns = ukify.create_parser().parse_args(())
ns.linux = None
ns.initrd = []
ukify.apply_config(ns, config)
@@ -143,7 +143,7 @@ def test_parse_args_minimal():
assert opts.os_release in (pathlib.Path('/etc/os-release'),
pathlib.Path('/usr/lib/os-release'))
-def test_parse_args_many():
+def test_parse_args_many_deprecated():
opts = ukify.parse_args(
['/ARG1', '///ARG2', '/ARG3 WITH SPACE',
'--cmdline=a b c',
@@ -186,9 +186,57 @@ def test_parse_args_many():
assert opts.output == pathlib.Path('OUTPUT')
assert opts.measure is False
+def test_parse_args_many():
+ opts = ukify.parse_args(
+ ['build',
+ '--linux=/ARG1',
+ '--initrd=///ARG2',
+ '--initrd=/ARG3 WITH SPACE',
+ '--cmdline=a b c',
+ '--os-release=K1=V1\nK2=V2',
+ '--devicetree=DDDDTTTT',
+ '--splash=splash',
+ '--pcrpkey=PATH',
+ '--uname=1.2.3',
+ '--stub=STUBPATH',
+ '--pcr-private-key=PKEY1',
+ '--pcr-public-key=PKEY2',
+ '--pcr-banks=SHA1,SHA256',
+ '--signing-engine=ENGINE',
+ '--secureboot-private-key=SBKEY',
+ '--secureboot-certificate=SBCERT',
+ '--sign-kernel',
+ '--no-sign-kernel',
+ '--tools=TOOLZ///',
+ '--output=OUTPUT',
+ '--measure',
+ '--no-measure',
+ ])
+ assert opts.linux == pathlib.Path('/ARG1')
+ assert opts.initrd == [pathlib.Path('/ARG2'), pathlib.Path('/ARG3 WITH SPACE')]
+ assert opts.cmdline == 'a b c'
+ assert opts.os_release == 'K1=V1\nK2=V2'
+ assert opts.devicetree == pathlib.Path('DDDDTTTT')
+ assert opts.splash == pathlib.Path('splash')
+ assert opts.pcrpkey == pathlib.Path('PATH')
+ assert opts.uname == '1.2.3'
+ assert opts.stub == pathlib.Path('STUBPATH')
+ assert opts.pcr_private_keys == [pathlib.Path('PKEY1')]
+ assert opts.pcr_public_keys == [pathlib.Path('PKEY2')]
+ assert opts.pcr_banks == ['SHA1', 'SHA256']
+ assert opts.signing_engine == 'ENGINE'
+ assert opts.sb_key == 'SBKEY'
+ assert opts.sb_cert == 'SBCERT'
+ assert opts.sign_kernel is False
+ assert opts.tools == [pathlib.Path('TOOLZ/')]
+ assert opts.output == pathlib.Path('OUTPUT')
+ assert opts.measure is False
+
def test_parse_sections():
opts = ukify.parse_args(
- ['/ARG1', '/ARG2',
+ ['build',
+ '--linux=/ARG1',
+ '--initrd=/ARG2',
'--section=test:TESTTESTTEST',
'--section=test2:@FILE',
])
@@ -239,7 +287,10 @@ def test_config_priority(tmp_path):
'''))
opts = ukify.parse_args(
- ['/ARG1', '///ARG2', '/ARG3 WITH SPACE',
+ ['build',
+ '--linux=/ARG1',
+ '--initrd=///ARG2',
+ '--initrd=/ARG3 WITH SPACE',
'--cmdline= a b c ',
'--os-release=K1=V1\nK2=V2',
'--devicetree=DDDDTTTT',
@@ -302,7 +353,7 @@ def test_help(capsys):
assert '--section' in out.out
assert not out.err
-def test_help_error(capsys):
+def test_help_error_deprecated(capsys):
with pytest.raises(SystemExit):
ukify.parse_args(['a', 'b', '--no-such-option'])
out = capsys.readouterr()
@@ -310,6 +361,14 @@ def test_help_error(capsys):
assert '--no-such-option' in out.err
assert len(out.err.splitlines()) == 1
+def test_help_error(capsys):
+ with pytest.raises(SystemExit):
+ ukify.parse_args(['build', '--no-such-option'])
+ out = capsys.readouterr()
+ assert not out.out
+ assert '--no-such-option' in out.err
+ assert len(out.err.splitlines()) == 1
+
@pytest.fixture(scope='session')
def kernel_initrd():
try:
@@ -326,7 +385,7 @@ def kernel_initrd():
initrd = f"{item['root']}{item['initrd'][0].split(' ')[0]}"
except (KeyError, IndexError):
continue
- return [linux, initrd]
+ return ['--linux', linux, '--initrd', initrd]
else:
return None
@@ -345,7 +404,11 @@ def test_basic_operation(kernel_initrd, tmpdir):
pytest.skip('linux+initrd not found')
output = f'{tmpdir}/basic.efi'
- opts = ukify.parse_args(kernel_initrd + [f'--output={output}'])
+ opts = ukify.parse_args([
+ 'build',
+ *kernel_initrd,
+ f'--output={output}',
+ ])
try:
ukify.check_inputs(opts)
except OSError as e:
@@ -362,6 +425,7 @@ def test_sections(kernel_initrd, tmpdir):
output = f'{tmpdir}/basic.efi'
opts = ukify.parse_args([
+ 'build',
*kernel_initrd,
f'--output={output}',
'--uname=1.2.3',
@@ -385,15 +449,22 @@ def test_sections(kernel_initrd, tmpdir):
def test_addon(kernel_initrd, tmpdir):
output = f'{tmpdir}/addon.efi'
- opts = ukify.parse_args([
+ args = [
+ 'build',
f'--output={output}',
'--cmdline=ARG1 ARG2 ARG3',
'--section=.test:CONTENTZ',
- ])
+ ]
+ if stub := os.getenv('EFI_ADDON'):
+ args += [f'--stub={stub}']
+ expected_exceptions = ()
+ else:
+ expected_exceptions = FileNotFoundError,
+ opts = ukify.parse_args(args)
try:
ukify.check_inputs(opts)
- except OSError as e:
+ except expected_exceptions as e:
pytest.skip(str(e))
ukify.make_uki(opts)
@@ -416,7 +487,8 @@ def test_uname_scraping(kernel_initrd):
if kernel_initrd is None:
pytest.skip('linux+initrd not found')
- uname = ukify.Uname.scrape(kernel_initrd[0])
+ assert kernel_initrd[0] == '--linux'
+ uname = ukify.Uname.scrape(kernel_initrd[1])
assert re.match(r'\d+\.\d+\.\d+', uname)
def test_efi_signing_sbsign(kernel_initrd, tmpdir):
@@ -431,6 +503,7 @@ def test_efi_signing_sbsign(kernel_initrd, tmpdir):
output = f'{tmpdir}/signed.efi'
opts = ukify.parse_args([
+ 'build',
*kernel_initrd,
f'--output={output}',
'--uname=1.2.3',
@@ -474,6 +547,7 @@ def test_efi_signing_pesign(kernel_initrd, tmpdir):
output = f'{tmpdir}/signed.efi'
opts = ukify.parse_args([
+ 'build',
*kernel_initrd,
f'--output={output}',
'--uname=1.2.3',
@@ -501,10 +575,6 @@ def test_efi_signing_pesign(kernel_initrd, tmpdir):
def test_pcr_signing(kernel_initrd, tmpdir):
if kernel_initrd is None:
pytest.skip('linux+initrd not found')
- if os.getuid() != 0:
- pytest.skip('must be root to access tpm2')
- if subprocess.call(['systemd-creds', 'has-tpm2', '-q']) != 0:
- pytest.skip('tpm2 is not available')
ourdir = pathlib.Path(__file__).parent
pub = unbase64(ourdir / 'example.tpm2-pcr-public.pem.base64')
@@ -512,6 +582,7 @@ def test_pcr_signing(kernel_initrd, tmpdir):
output = f'{tmpdir}/signed.efi'
opts = ukify.parse_args([
+ 'build',
*kernel_initrd,
f'--output={output}',
'--uname=1.2.3',
@@ -562,10 +633,6 @@ def test_pcr_signing(kernel_initrd, tmpdir):
def test_pcr_signing2(kernel_initrd, tmpdir):
if kernel_initrd is None:
pytest.skip('linux+initrd not found')
- if os.getuid() != 0:
- pytest.skip('must be root to access tpm2')
- if subprocess.call(['systemd-creds', 'has-tpm2', '-q']) != 0:
- pytest.skip('tpm2 is not available')
ourdir = pathlib.Path(__file__).parent
pub = unbase64(ourdir / 'example.tpm2-pcr-public.pem.base64')
@@ -578,8 +645,12 @@ def test_pcr_signing2(kernel_initrd, tmpdir):
microcode.write(b'1234567890')
output = f'{tmpdir}/signed.efi'
+ assert kernel_initrd[0] == '--linux'
opts = ukify.parse_args([
- kernel_initrd[0], microcode.name, kernel_initrd[1],
+ 'build',
+ *kernel_initrd[:2],
+ f'--initrd={microcode.name}',
+ *kernel_initrd[2:],
f'--output={output}',
'--uname=1.2.3',
'--cmdline=ARG1 ARG2 ARG3',
diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py
index 19896afac3..a9c21601df 100755
--- a/src/ukify/ukify.py
+++ b/src/ukify/ukify.py
@@ -19,7 +19,9 @@
# pylint: disable=missing-docstring,invalid-name,import-outside-toplevel
# pylint: disable=consider-using-with,unspecified-encoding,line-too-long
# pylint: disable=too-many-locals,too-many-statements,too-many-return-statements
-# pylint: disable=too-many-branches,fixme
+# pylint: disable=too-many-branches,too-many-lines,too-many-instance-attributes
+# pylint: disable=too-many-arguments,unnecessary-lambda-assignment,fixme
+# pylint: disable=unused-argument
import argparse
import configparser
@@ -436,9 +438,9 @@ def call_systemd_measure(uki, linux, opts):
def join_initrds(initrds):
- if len(initrds) == 0:
+ if not initrds:
return None
- elif len(initrds) == 1:
+ if len(initrds) == 1:
return initrds[0]
seq = []
@@ -478,6 +480,9 @@ def pe_add_sections(uki: UKI, output: str):
pe.FILE_HEADER.IMAGE_FILE_LOCAL_SYMS_STRIPPED = True
# Old stubs might have been stripped, leading to unaligned raw data values, so let's fix them up here.
+ # pylint thinks that Structure doesn't have various members that it has…
+ # pylint: disable=no-member
+
for i, section in enumerate(pe.sections):
oldp = section.PointerToRawData
oldsz = section.SizeOfRawData
@@ -745,6 +750,7 @@ class ConfigItem:
) -> None:
"Set namespace.<dest>[idx] to value, with idx derived from group"
+ # pylint: disable=protected-access
if group not in namespace._groups:
namespace._groups += [group]
idx = namespace._groups.index(group)
@@ -814,7 +820,10 @@ class ConfigItem:
else:
conv = lambda s:s
- if self.nargs == '*':
+ # This is a bit ugly, but --initrd is the only option which is specified
+ # with multiple args on the command line and a space-separated list in the
+ # config file.
+ if self.name == '--initrd':
value = [conv(v) for v in value.split()]
else:
value = conv(value)
@@ -834,8 +843,17 @@ class ConfigItem:
return (section_name, key, value)
+VERBS = ('build',)
+
CONFIG_ITEMS = [
ConfigItem(
+ 'positional',
+ metavar = 'VERB',
+ nargs = '*',
+ help = f"operation to perform ({','.join(VERBS)})",
+ ),
+
+ ConfigItem(
'--version',
action = 'version',
version = f'ukify {__version__}',
@@ -848,20 +866,18 @@ CONFIG_ITEMS = [
),
ConfigItem(
- 'linux',
- metavar = 'LINUX',
+ '--linux',
type = pathlib.Path,
- nargs = '?',
help = 'vmlinuz file [.linux section]',
config_key = 'UKI/Linux',
),
ConfigItem(
- 'initrd',
- metavar = 'INITRD…',
+ '--initrd',
+ metavar = 'INITRD',
type = pathlib.Path,
- nargs = '*',
- help = 'initrd files [.initrd section]',
+ action = 'append',
+ help = 'initrd file [part of .initrd section]',
config_key = 'UKI/Initrd',
config_push = ConfigItem.config_list_prepend,
),
@@ -1068,7 +1084,7 @@ def apply_config(namespace, filename=None):
# Fill in ._groups based on --pcr-public-key=, --pcr-private-key=, and --phases=.
assert '_groups' not in namespace
n_pcr_priv = len(namespace.pcr_private_keys or ())
- namespace._groups = list(range(n_pcr_priv))
+ namespace._groups = list(range(n_pcr_priv)) # pylint: disable=protected-access
cp = configparser.ConfigParser(
comment_prefixes='#',
@@ -1193,6 +1209,20 @@ def parse_args(args=None):
p = create_parser()
opts = p.parse_args(args)
+ # Figure out which syntax is being used, one of:
+ # ukify verb --arg --arg --arg
+ # ukify linux initrd…
+ if len(opts.positional) == 1 and opts.positional[0] in VERBS:
+ opts.verb = opts.positional[0]
+ elif opts.linux or opts.initrd:
+ raise ValueError('--linux/--initrd options cannot be used with positional arguments')
+ else:
+ print("Assuming obsolete commandline syntax with no verb. Please use 'build'.")
+ if opts.positional:
+ opts.linux = pathlib.Path(opts.positional[0])
+ opts.initrd = [pathlib.Path(arg) for arg in opts.positional[1:]]
+ opts.verb = 'build'
+
# Check that --pcr-public-key=, --pcr-private-key=, and --phases=
# have either the same number of arguments are are not specified at all.
n_pcr_pub = None if opts.pcr_public_keys is None else len(opts.pcr_public_keys)
@@ -1213,6 +1243,7 @@ def parse_args(args=None):
def main():
opts = parse_args()
check_inputs(opts)
+ assert opts.verb == 'build'
make_uki(opts)