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

Compare commits

...

18 Commits

Author SHA1 Message Date
Daan De Meyer
ba037640bf
Make .standalone programs same as normal and avoid double compilation (#40148) 2025-12-19 14:58:34 +01:00
Luca Boccassi
059b3b466d
core: reuse existing dm-verity device for single filesystem images pinned by policy (#40007)
Loading images is, generally speaking, the slowest part of sd-executor
when spawning a service. This is due to multiple factors. dm-verity is
obviously a big part of the cost, but dissecting in general via libblkid
also can take a lot of time, due to probing the images and their
filesystems.

A performance test doing service restarts in a row shows these
results, ran on a production system (low power and slow ARM64 SOC) with
a real production service, show the following service interruption
intervals:

```
Count | P50 (ms)  | P90 (ms) | P95 (ms) | P99 (ms) | P99.9 (ms)
507   | 1,532     | 1,726    | 2,548    | 4,112    | 4,592
```

One iteration is 507 restarts in this case, but this has ran hundreds
of times and the results are always in line within margin of error.
This also holds true for metrics from live systems, same numbers.
Between 1.0s and 1.2s can be attributed by profiling to the time needed
for the service code itself to start up and sd_notify, the rest is spent
inside systemd's code.

This means there is currently a tradeoff for services - either use
secure
images, or make restarting fast. Downtime of services is a very
important
metric, as for many cases this directly translates to outages, total or
partial (blackouot or greyout).

In order to facilitate using secure images without downsides, skip the
slow dissect steps (probing, loop devices, etc) when the configured
image is a single filesystem dm-verity image with a policy that pins it
to a single filesystem, and an already existing and open dm-verity
device
can be found and reused.

This allows orchestrators to pre-open images on download, before
restarting
services, to minimize downtimes.
2025-12-19 13:19:16 +00:00
Luca Boccassi
6ceb76bfcf
mkosi: update fedora commit (#40146) 2025-12-19 12:49:27 +00:00
Zbigniew Jędrzejewski-Szmek
736fc4b479 meson: avoid double compilation for standalone progs
So far we compiled the normal and standalone versions completely
independently. Let's use the 'extract' template pattern to avoid any
additional compilation and only require an single link to produce the
.standalone variants.

Unfortunately, as designed, the 'extract' framework only allows one set of
object files to be extracted. Since we need all the files for the
.standalone version, we cannot use 'extract' for other purposes. Thus, in
the two cases where 'extract' was used for the test binaries, this is now
changed to compile the files a second time. But the number of files in that
list is small, so this seems like a better option.

(If we weren't using the template system, we could easily extract just the
objects we need. But with the current system, at the point of the
definition, the binaries are not defined yet. We'd need to handle all of
this through sets of dictionaries, and that just seems like too much
trouble to avoid double compilation of a few small files.)
2025-12-19 13:15:49 +01:00
Zbigniew Jędrzejewski-Szmek
b6c86e145a meson: drop now-unused -DSTANDALONE
If we ever want to add it back, it should be with -DSTANDALONE=0|1, so
that #if instead of #ifdef can be used. We generally converted our internal
defines to that form.
2025-12-19 13:15:49 +01:00
Zbigniew Jędrzejewski-Szmek
795e066d22 sysusers,tmpfiles: make standalone versions full-featured
This effectively reverts 3537577c37d2c23a518540d36884a127aab944f8. Originally,
the #ifdefs were added because we didn't want to pull in the whole tree of
libmount and other dependencies in standalone versions. But dependencies are
now loaded through dlopen(), so this is not needed anymore. (And doesn't even
make much of a difference.)
2025-12-19 13:15:49 +01:00
Zbigniew Jędrzejewski-Szmek
54492552a1 meson: allow .standalone version to be always built
Allow .standalone version to be built on-demand, even if -Dstandalone=false
is configured. In other words, this changes the meson option from a hard
disablement to a soft "build is on/off by default".

The meson config was originally written in this way but we lost this feature
after the transition to templates. It is nice to build additional targets on
demand during development, so add this back.
2025-12-19 13:14:19 +01:00
Zbigniew Jędrzejewski-Szmek
9e104bd3aa meson: put src/import source lists directly in templates
The indirection through variables doesn't seem that useful here:
OTOH, the lists are short, and OTOH, there is a bunch of different
programs with similar names. Overall, it's all easier to follow if
the lists are inline.
2025-12-19 13:12:35 +01:00
Luca Boccassi
56a53a5974 mkosi: update fedora commit reference to cac8dde28a1298bbc2bee40e9ab3b9308392f691
* cac8dde28a test: Allow passing in extra tests to skip via TEST_SKIP
* 56377438ba Disable sysinit-path for upstream builds
* 0c8ea706f9 Fix links to patches
* 4f5b5a9615 Version 259
* bf8019c840 Version 259~rc3
* ef777d6572 Check if --max-lines is supported by meson
* b562e38e22 Fix use of removed $LOCAL_CONF variable
* 0289127dae Patch machined to continue after selinux denial
* 7e409130ee Version 259~rc2
* 33b38cdbc7 Suppress errors from tar
* ddb6474e94 Drop provides for removed sysvinit tools
* 9ac8c36307 Set meson auto features to auto when building for upstream
2025-12-19 11:35:21 +00:00
Luca Boccassi
2791af18e4 tools: use -f in mkosi summary in fetch-distro.py
$ ./tools/fetch-distro.py -u fedora
+ mkosi --json -d fedora summary
‣ Ignoring --distribution from the CLI. Run with -f to rebuild the image with this setting

Follow-up for 35f6e5c3278bda935b67249a9ee61e9f6252bb6f
2025-12-19 11:35:21 +00:00
Luca Boccassi
0bd766553c core: reuse existing dm-verity device for single filesystem images pinned by policy
Loading images is, generally speaking, the slowest part of sd-executor
when spawning a service. This is due to multiple factors. dm-verity is
obviously a big part of the cost, but dissecting in general via libblkid
also can take a lot of time, due to probing the images and their filesystems.

A performance test doing service restarts in a row shows these
results, ran on a production system (low power and slow ARM64 SOC) with
a real production service, show the following service interruption intervals:

Count | P50 (ms)  | P90 (ms) | P95 (ms) | P99 (ms) | P99.9 (ms)
507   | 1,532     | 1,726    | 2,548    | 4,112    | 4,592

One iteration is 507 restarts in this case, but this has ran hundreds
of times and the results are always in line within margin of error.
This also holds true for metrics from live systems, same numbers.
Between 1.0s and 1.2s can be attributed by profiling to the time needed
for the service code itself to start up and sd_notify, the rest is spent
inside systemd's code.

This means there is currently a tradeoff for services - either use secure
images, or make restarting fast. Downtime of services is a very important
metric, as for many cases this directly translates to outages, total or
partial (blackouot or greyout).

In order to facilitate using secure images without downsides, skip the
slow dissect steps (probing, loop devices, etc) when the configured
image is a single filesystem dm-verity image with a policy that pins it
to a single filesystem, and an already existing and open dm-verity device
can be found and reused.

This allows orchestrators to pre-open images on download, before restarting
services, to minimize downtimes.
2025-12-19 11:20:48 +00:00
seidlerv
bd6d8eec5e man/org.freedesktop.systemd1.xml: Fix wording 2025-12-19 11:28:46 +01:00
Daan De Meyer
9d10457a3e test-user-util: Migrate to new assertion macros 2025-12-19 11:11:39 +01:00
Luca Boccassi
4be5c2870f image-policy: add helper to determine fstype 2025-12-19 09:17:41 +00:00
Luca Boccassi
d0cb79b8a9 image-policy: add support for restricting to specific filesystems 2025-12-19 09:17:41 +00:00
Daan De Meyer
bc3f700a4a
logs-show: match init.scope rather than _PID=1 for UNIT= (#40142) 2025-12-19 08:50:33 +01:00
Mike Yuan
7f9e3c5919
logs-show: match init.scope rather than _PID=1 for UNIT=
We should consider the whole init.scope trusted, and any
process trying to log on behalf of a unit there should be
attributed as so.

Follow-up for 4f25248b6e69855b4da6d01690821b9359928edc
Only with this commit is the change in effect.
2025-12-19 02:18:44 +01:00
Mike Yuan
79e6bfb1e6
core/manager: use UNIT_IS_ACTIVE_OR_RELOADING rather than hardcoding service states
except for SERVICE_EXITED which has to be checked manually.
2025-12-19 02:18:44 +01:00
26 changed files with 994 additions and 580 deletions

View File

@ -39,7 +39,7 @@ jobs:
trigger: pull_request
fmf_url: https://src.fedoraproject.org/rpms/systemd
# This is automatically updated by tools/fetch-distro.py --update fedora
fmf_ref: 12f95f807fef5075a8842dd107f83b4c41d5ac26
fmf_ref: cac8dde28a1298bbc2bee40e9ab3b9308392f691
targets:
- fedora-rawhide-x86_64
# testing-farm in the Fedora repository is explicitly configured to use testing-farm bare metal runners as

View File

@ -4901,7 +4901,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
has no main PID. The control PID is the PID of the current start/stop/reload process running and is 0
if no such process is currently running. That means that <varname>ExecMainPID</varname> and
<varname>MainPID</varname> differ in the way that the latter immediately reflects whether a main
process is currently running while the latter possible contains information collected from the last run
process is currently running while the former possibly contains information collected from the last run
even if the process is no longer around.</para>
<para><varname>StatusText</varname>, <varname>StatusErrno</varname>, <varname>StatusBusError</varname>,

View File

@ -26,11 +26,11 @@
<para>In systemd, whenever a disk image (DDI) implementing the <ulink
url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">UAPI.2
Discoverable Partitions Specification</ulink> is activated, a policy may be specified controlling which
partitions to mount and what kind of cryptographic protection to require. Such a disk image dissection
policy is a string that contains per-partition-type rules, separated by colons
(<literal>:</literal>). The individual rules consist of a partition identifier, an equal sign
(<literal>=</literal>), and one or more flags which may be set per partition. If multiple flags are
specified per partition they are separated by a plus sign (<literal>+</literal>).</para>
partitions to mount and what kind of cryptographic protection to require, and which type of filesystem
is allowed. Such a disk image dissection policy is a string that contains per-partition-type rules,
separated by colons (<literal>:</literal>). The individual rules consist of a partition identifier, an
equal sign (<literal>=</literal>), and one or more flags which may be set per partition. If multiple
flags are specified per partition they are separated by a plus sign (<literal>+</literal>).</para>
<para>The partition identifiers currently defined are: <option>root</option>, <option>usr</option>,
<option>home</option>, <option>srv</option>, <option>esp</option>, <option>xbootldr</option>,
@ -71,6 +71,46 @@
image.</para></listitem>
</itemizedlist>
<para>The following filesystem policy flags are defined that dictate which filesystem types are allowed
for which partition:</para>
<table>
<title>
Filesystems types supported by the policy
</title>
<tgroup cols='1'>
<thead>
<row>
<entry>Filesystem flag</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>btrfs</literal></entry>
</row>
<row>
<entry><literal>erofs</literal></entry>
</row>
<row>
<entry><literal>ext4</literal></entry>
</row>
<row>
<entry><literal>f2fs</literal></entry>
</row>
<row>
<entry><literal>squashfs</literal></entry>
</row>
<row>
<entry><literal>vfat</literal></entry>
</row>
<row>
<entry><literal>xfs</literal></entry>
</row>
</tbody>
</tgroup>
</table>
<para>By setting a combination of the flags above, alternatives can be declared. For example the
combination <literal>unused+absent</literal> means: the partition may exist (in which case it shall not
be used) or may be absent. The combination of
@ -175,6 +215,11 @@
does not have to be, and ignores swap partitions, and uses all other partitions if they are available, possibly with encryption.</para>
<programlisting>root=unprotected+encrypted:swap=absent+unused:=unprotected+encrypted+absent</programlisting>
<para>The following image policy string dictates a single root partition that can only be erofs or squashfs,
and ignores swap partitions, and uses all other partitions if they are available, possibly with encryption.</para>
<programlisting>root=erofs+squashfs:swap=absent+unused:=unprotected+encrypted+absent</programlisting>
</refsect1>
<refsect1>

View File

@ -2517,6 +2517,7 @@ foreach dict : executables
is_test = name.startswith('test-')
is_fuzz = name.startswith('fuzz-')
is_standalone = name.endswith('.standalone')
build = true
foreach cond : dict.get('conditions', [])
@ -2529,17 +2530,13 @@ foreach dict : executables
continue
endif
if name.endswith('.standalone') and not have_standalone_binaries
continue
endif
exe_sources = dict.get('sources', []) + dict.get('extract', [])
kwargs = {}
foreach key, val : dict
if key in ['name', 'dbus', 'public', 'conditions', 'type', 'suite',
'timeout', 'parallel', 'objects', 'sources', 'extract',
'include_directories']
'include_directories', 'build_by_default']
continue
endif
@ -2551,7 +2548,7 @@ foreach dict : executables
endforeach
include_directories = dict['include_directories']
if not is_test
if not is_test and exe_sources.length() > 0
include_directories += fs.parent(exe_sources[0])
endif
@ -2579,17 +2576,21 @@ foreach dict : executables
endforeach
endif
build_by_default = dict.get('build_by_default',
have_standalone_binaries or not is_standalone)
exe = executable(
name,
sources : exe_sources,
kwargs : kwargs,
implicit_include_directories : false,
include_directories : include_directories,
build_by_default: build_by_default,
)
executables_by_name += { name : exe }
if not name.endswith('.standalone')
if not is_standalone
sources += exe_sources
endif
@ -2602,7 +2603,7 @@ foreach dict : executables
}
endif
if dict.get('build_by_default', true)
if build_by_default
if dict.get('dbus', false)
dbus_programs += exe
endif

View File

@ -9,5 +9,5 @@ Profiles=!hyperscale
Environment=
GIT_URL=https://src.fedoraproject.org/rpms/systemd.git
GIT_BRANCH=rawhide
GIT_COMMIT=12f95f807fef5075a8842dd107f83b4c41d5ac26
GIT_COMMIT=cac8dde28a1298bbc2bee40e9ab3b9308392f691
PKG_SUBDIR=fedora

View File

@ -9,7 +9,7 @@
#include "string-util.h"
static int table_add_designator_line(Table *table, PartitionDesignator d, PartitionPolicyFlags f) {
_cleanup_free_ char *q = NULL;
_cleanup_free_ char *q = NULL, *t = NULL;
const char *color;
int r;
@ -19,6 +19,9 @@ static int table_add_designator_line(Table *table, PartitionDesignator d, Partit
if (partition_policy_flags_to_string(f & _PARTITION_POLICY_USE_MASK, /* simplify= */ true, &q) < 0)
return log_oom();
if (partition_policy_flags_to_string(f & _PARTITION_POLICY_FSTYPE_MASK, /* simplify= */ true, &t) < 0)
return log_oom();
color = (f & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_IGNORE ? ansi_grey() :
((f & (PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT)) ==
(PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT)) ? ansi_highlight_yellow() :
@ -72,6 +75,11 @@ static int table_add_designator_line(Table *table, PartitionDesignator d, Partit
if (r < 0)
return table_log_add_error(r);
r = table_add_many(table,
TABLE_STRING, isempty(t) ? "-" : t);
if (r < 0)
return table_log_add_error(r);
return 0;
}
@ -128,7 +136,7 @@ int verb_image_policy(int argc, char *argv[], void *userdata) {
ansi_highlight(), as_string_simplified, ansi_normal(),
ansi_grey(), as_string, ansi_normal());
table = table_new("partition", "mode", "read-only", "growfs");
table = table_new("partition", "mode", "read-only", "growfs", "fstype");
if (!table)
return log_oom();

View File

@ -4350,7 +4350,7 @@ static bool manager_journal_is_running(Manager *m) {
u = manager_get_unit(m, SPECIAL_JOURNALD_SERVICE);
if (!u)
return false;
if (!IN_SET(SERVICE(u)->state, SERVICE_RELOAD, SERVICE_RUNNING))
if (!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)) || SERVICE(u)->state == SERVICE_EXITED)
return false;
return true;

View File

@ -2550,6 +2550,22 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
SET_FLAG(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE, p->verity && p->verity->data_path);
/* First check if we have a verity device already open and with a fstype pinned by policy. If it
* cannot be found, then fallback to the slow path (full dissect). */
r = dissected_image_new_from_existing_verity(
p->root_image,
p->verity,
p->root_image_options,
p->root_image_policy,
/* image_filter= */ NULL,
p->runtime_scope,
dissect_image_flags,
&dissected_image);
if (r < 0 && !ERRNO_IS_NEG_DEVICE_ABSENT(r) && r != -ENOPKG)
return r;
if (r >= 0)
log_debug("Reusing pre-existing verity-protected root image %s", p->root_image);
else {
if (p->runtime_scope == RUNTIME_SCOPE_SYSTEM) {
/* In system mode we mount directly */
@ -2589,7 +2605,7 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
r = dissected_image_decrypt(
dissected_image,
NULL,
/* passphrase= */ NULL,
p->verity,
p->root_image_policy,
dissect_image_flags);
@ -2611,6 +2627,7 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
return r;
}
}
}
if (p->root_directory)
root = p->root_directory;

View File

@ -4,40 +4,6 @@ if conf.get('ENABLE_IMPORTD') != 1
subdir_done()
endif
systemd_importd_sources = files(
'importd.c',
)
systemd_importd_extract_sources = files(
'import-common.c',
'import-compress.c',
'qcow2-util.c',
)
systemd_pull_sources = files(
'pull.c',
'pull-raw.c',
'pull-tar.c',
'pull-job.c',
'pull-common.c',
'curl-util.c',
)
systemd_import_sources = files(
'import.c',
'import-raw.c',
'import-tar.c',
)
systemd_import_fs_sources = files(
'import-fs.c',
)
systemd_export_sources = files(
'export.c',
'export-tar.c',
'export-raw.c',
)
common_deps = [
libbzip2,
libcurl,
@ -50,14 +16,27 @@ executables += [
libexec_template + {
'name' : 'systemd-importd',
'dbus' : true,
'sources' : systemd_importd_sources,
'extract' : systemd_importd_extract_sources,
'sources' : files(
'importd.c',
),
'extract' : files(
'import-common.c',
'import-compress.c',
'qcow2-util.c',
),
'dependencies' : [common_deps, threads],
},
libexec_template + {
'name' : 'systemd-pull',
'public' : true,
'sources' : systemd_pull_sources,
'sources' : files(
'pull.c',
'pull-raw.c',
'pull-tar.c',
'pull-job.c',
'pull-common.c',
'curl-util.c',
),
'objects' : ['systemd-importd'],
'dependencies' : common_deps + [
libopenssl,
@ -66,21 +45,31 @@ executables += [
libexec_template + {
'name' : 'systemd-import',
'public' : true,
'sources' : systemd_import_sources,
'sources' : files(
'import.c',
'import-raw.c',
'import-tar.c',
),
'objects' : ['systemd-importd'],
'dependencies' : common_deps,
},
libexec_template + {
'name' : 'systemd-import-fs',
'public' : true,
'sources' : systemd_import_fs_sources,
'sources' : files(
'import-fs.c',
),
'objects' : ['systemd-importd'],
'dependencies' : common_deps,
},
libexec_template + {
'name' : 'systemd-export',
'public' : true,
'sources' : systemd_export_sources,
'sources' : files(
'export.c',
'export-tar.c',
'export-raw.c',
),
'objects' : ['systemd-importd'],
'dependencies' : common_deps,
},

View File

@ -8,7 +8,7 @@ executables += [
executable_template + {
'name' : 'systemd-repart',
'public' : true,
'sources' : files('repart.c'),
'extract' : files('repart.c'),
'link_with' : [
libshared,
libshared_fdisk,
@ -24,8 +24,7 @@ executables += [
executable_template + {
'name' : 'systemd-repart.standalone',
'public' : true,
'sources' : files('repart.c'),
'c_args' : '-DSTANDALONE',
'objects' : ['systemd-repart'],
'link_with' : [
libc_wrapper_static,
libbasic_static,

View File

@ -843,12 +843,15 @@ static int open_partition(
int r;
assert(node);
assert(loop);
assert(loop || !is_partition);
fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return -errno;
if (!loop)
return TAKE_FD(fd);
/* Check if the block device is a child of (or equivalent to) the originally provided one. */
r = block_device_new_from_fd(fd, is_partition ? BLOCK_DEVICE_LOOKUP_WHOLE_DISK : 0, &dev);
if (r < 0)
@ -911,6 +914,85 @@ static bool image_filter_test(const ImageFilter *filter, PartitionDesignator d,
return fnmatch(filter->pattern[d], strempty(label), FNM_NOESCAPE) == 0;
}
static int dissect_image_from_unpartitioned(
const char *devname,
uint64_t diskseq,
const sd_id128_t *uuid,
bool encrypted,
const VeritySettings *verity,
const MountOptions *mount_options,
const ImagePolicy *policy,
const ImageFilter *filter,
int *mount_node_fd, /* taken over on success */
char **fstype, /* taken over on success */
DissectedImage *m,
DissectImageFlags flags,
PartitionPolicyFlags found_flags) {
_cleanup_free_ char *n = NULL, *o = NULL;
const char *options = NULL;
int r;
assert(devname);
assert(m);
assert(fstype);
if (!image_filter_test(filter, PARTITION_ROOT, /* label= */ NULL)) /* do a filter check with an empty partition label */
return -ECOMM;
r = image_policy_may_use(policy, PARTITION_ROOT);
if (r < 0)
return r;
if (r == 0) /* policy says ignore this, so we ignore it */
return -ENOPKG;
r = image_policy_check_protection(policy, PARTITION_ROOT, found_flags);
if (r < 0)
return r;
r = image_policy_check_partition_flags(policy, PARTITION_ROOT, 0); /* we have no gpt partition flags, hence check against all bits off */
if (r < 0)
return r;
r = make_partition_devname(devname, diskseq, /* nr= */ -1, flags, &n);
if (r < 0)
return r;
m->single_file_system = true;
m->encrypted = encrypted;
m->has_verity = verity && verity->data_path;
m->verity_ready = verity_settings_data_covers(verity, PARTITION_ROOT);
m->has_verity_sig = false; /* signature not embedded, must be specified */
m->verity_sig_ready = m->verity_ready && iovec_is_set(&verity->root_hash);
if (uuid)
m->image_uuid = *uuid;
options = mount_options_from_designator(mount_options, PARTITION_ROOT);
if (options) {
o = strdup(options);
if (!o)
return -ENOMEM;
}
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
.found = true,
.rw = !m->verity_ready && !fstype_is_ro(*fstype),
.partno = -1,
.architecture = _ARCHITECTURE_INVALID,
.fstype = TAKE_PTR(*fstype),
.node = TAKE_PTR(n),
.mount_options = TAKE_PTR(o),
.mount_node_fd = mount_node_fd ? TAKE_FD(*mount_node_fd) : -EBADF,
.size = UINT64_MAX,
.fsmount_fd = -EBADF,
};
return 0;
}
static int dissect_image(
DissectedImage *m,
int fd,
@ -1030,38 +1112,41 @@ static int dissect_image(
if ((!(flags & DISSECT_IMAGE_GPT_ONLY) &&
(flags & DISSECT_IMAGE_GENERIC_ROOT)) ||
(flags & DISSECT_IMAGE_NO_PARTITION_TABLE)) {
_cleanup_free_ char *root_fstype_string = NULL;
const char *usage = NULL;
bool encrypted;
r = partition_policy_determine_fstype(policy, PARTITION_ROOT, &encrypted, &root_fstype_string);
if (r < 0)
return r;
/* If flags permit this, also allow using non-partitioned single-filesystem images */
if (root_fstype_string)
usage = encrypted ? "crypto" : "filesystem";
else
(void) sym_blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
_cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
const char *fstype = NULL, *options = NULL;
_cleanup_free_ char *t = NULL;
const char *fstype = NULL;
_cleanup_close_ int mount_node_fd = -EBADF;
sd_id128_t uuid = SD_ID128_NULL;
PartitionPolicyFlags found_flags;
bool encrypted;
/* OK, we have found a file system, that's our root partition then. */
if (!image_filter_test(filter, PARTITION_ROOT, /* label= */ NULL)) /* do a filter check with an empty partition label */
return -ECOMM;
r = image_policy_may_use(policy, PARTITION_ROOT);
if (r < 0)
return r;
if (r == 0) /* policy says ignore this, so we ignore it */
return -ENOPKG;
if (!root_fstype_string) {
(void) sym_blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
/* blkid will return FAT's serial number as UUID, hence it is quite possible that
* parsing this will fail. We'll ignore the ID, since it's just too short to be
* useful as true identifier. */
(void) blkid_probe_lookup_value_id128(b, "UUID", &uuid);
} else
/* The policy fstype flags translate to the literal fstype name of each filesystem. */
fstype = root_fstype_string;
encrypted = streq_ptr(fstype, "crypto_LUKS");
encrypted = encrypted || streq_ptr(fstype, "crypto_LUKS");
if (verity_settings_data_covers(verity, PARTITION_ROOT))
found_flags = iovec_is_set(&verity->root_hash_sig) ? PARTITION_POLICY_SIGNED : PARTITION_POLICY_VERITY;
@ -1074,14 +1159,6 @@ static int dissect_image(
} else
found_flags = PARTITION_POLICY_UNPROTECTED;
r = image_policy_check_protection(policy, PARTITION_ROOT, found_flags);
if (r < 0)
return r;
r = image_policy_check_partition_flags(policy, PARTITION_ROOT, 0); /* we have no gpt partition flags, hence check against all bits off */
if (r < 0)
return r;
if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES)) {
mount_node_fd = open_partition(devname, /* is_partition= */ false, m->loop);
if (mount_node_fd < 0)
@ -1094,43 +1171,20 @@ static int dissect_image(
return -ENOMEM;
}
r = make_partition_devname(devname, diskseq, -1, flags, &n);
if (r < 0)
return r;
m->single_file_system = true;
m->encrypted = encrypted;
m->has_verity = verity && verity->data_path;
m->verity_ready = verity_settings_data_covers(verity, PARTITION_ROOT);
m->has_verity_sig = false; /* signature not embedded, must be specified */
m->verity_sig_ready = m->verity_ready && iovec_is_set(&verity->root_hash);
m->image_uuid = uuid;
options = mount_options_from_designator(mount_options, PARTITION_ROOT);
if (options) {
o = strdup(options);
if (!o)
return -ENOMEM;
}
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
.found = true,
.rw = !m->verity_ready && !fstype_is_ro(fstype),
.partno = -1,
.architecture = _ARCHITECTURE_INVALID,
.fstype = TAKE_PTR(t),
.node = TAKE_PTR(n),
.mount_options = TAKE_PTR(o),
.mount_node_fd = TAKE_FD(mount_node_fd),
.offset = 0,
.size = UINT64_MAX,
.fsmount_fd = -EBADF,
};
return 0;
return dissect_image_from_unpartitioned(
devname,
diskseq,
&uuid,
encrypted,
verity,
mount_options,
policy,
filter,
&mount_node_fd,
&t,
m,
flags,
found_flags);
}
}
@ -1555,10 +1609,15 @@ static int dissect_image(
if (r < 0)
return r;
/* Local override via env var or designator type wins */
if (fstype) {
t = strdup(fstype);
if (!t)
return -ENOMEM;
} else {
r = partition_policy_determine_fstype(policy, type.designator, /* ret_encrypted= */ NULL, &t);
if (r < 0)
return r;
}
if (label) {
@ -1896,6 +1955,97 @@ static int dissect_image(
}
#endif
int dissected_image_new_from_existing_verity(
const char *src,
const VeritySettings *verity,
const MountOptions *options,
const ImagePolicy *image_policy,
const ImageFilter *image_filter,
RuntimeScope runtime_scope,
DissectImageFlags dissect_image_flags,
DissectedImage **ret) {
/* Look for an already set up dm-verity device with a single filesystem, according to our naming
* scheme and image policy, and if it is pinned by filesystem type set up the image directly. */
#if HAVE_BLKID
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
_cleanup_free_ char *node = NULL, *root_hash_encoded = NULL, *root_fstype_string = NULL;
_cleanup_close_ int mount_node_fd = -EBADF;
PartitionPolicyFlags found_flags;
bool encrypted = false;
int r;
assert(!verity || verity->designator < 0 || IN_SET(verity->designator, PARTITION_ROOT, PARTITION_USR));
assert(!verity || iovec_is_valid(&verity->root_hash));
assert(!verity || iovec_is_valid(&verity->root_hash_sig));
assert(!verity || iovec_is_set(&verity->root_hash) || !iovec_is_set(&verity->root_hash_sig));
assert(ret);
/* Shortcut: this deals only with verity images and requires a policy, and only for system services */
if (runtime_scope != RUNTIME_SCOPE_SYSTEM ||
!FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_VERITY_SHARE) ||
!image_policy ||
!verity ||
!verity->data_path ||
(verity->designator >= 0 && verity->designator != PARTITION_ROOT) ||
!iovec_is_set(&verity->root_hash))
return -ENOPKG;
/* The policy fstype flags translate to the literal fstype name of each filesystem.
* Input must be a single filesystem image, if the policy specifies more than one, we need to dissect */
r = partition_policy_determine_fstype(image_policy, PARTITION_ROOT, &encrypted, &root_fstype_string);
if (r < 0)
return r;
if (!root_fstype_string)
return -ENOPKG;
if (verity_settings_data_covers(verity, PARTITION_ROOT))
found_flags = iovec_is_set(&verity->root_hash_sig) ? PARTITION_POLICY_SIGNED : PARTITION_POLICY_VERITY;
else
found_flags = encrypted ? PARTITION_POLICY_ENCRYPTED : PARTITION_POLICY_UNPROTECTED;
root_hash_encoded = hexmem(verity->root_hash.iov_base, verity->root_hash.iov_len);
if (!root_hash_encoded)
return -ENOMEM;
node = strjoin("/dev/mapper/", root_hash_encoded, "-verity");
if (!node)
return -ENOMEM;
r = dissected_image_new(src, &dissected_image);
if (r < 0)
return r;
mount_node_fd = open_partition(node, /* is_partition= */ false, /* loop= */ NULL);
if (mount_node_fd < 0)
return mount_node_fd;
r = dissect_image_from_unpartitioned(
node,
/* diskseq= */ 0,
/* uuid= */ NULL,
encrypted,
verity,
options,
image_policy,
image_filter,
&mount_node_fd,
&root_fstype_string,
dissected_image,
dissect_image_flags,
found_flags);
if (r < 0)
return r;
*ret = TAKE_PTR(dissected_image);
return 0;
#else
return -EOPNOTSUPP;
#endif
}
int dissect_image_file(
const char *path,
const VeritySettings *verity,
@ -4636,6 +4786,22 @@ int verity_dissect_and_mount(
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY |
DISSECT_IMAGE_VERITY_SHARE;
/* First check if we have a verity device already open and with a fstype pinned by policy. If it
* cannot be found, then fallback to the slow path (full dissect). */
r = dissected_image_new_from_existing_verity(
src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src,
verity,
options,
image_policy,
image_filter,
runtime_scope,
dissect_image_flags,
&dissected_image);
if (r < 0 && !ERRNO_IS_NEG_DEVICE_ABSENT(r) && r != -ENOPKG)
return r;
if (r >= 0)
log_debug("Reusing pre-existing verity-protected image %s", src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src);
else {
if (runtime_scope == RUNTIME_SCOPE_SYSTEM) {
/* Note that we don't use loop_device_make here, as the FD is most likely O_PATH which would not be
* accepted by LOOP_CONFIGURE, so just let loop_device_make_by_path reopen it as a regular FD. */
@ -4701,6 +4867,7 @@ int verity_dissect_and_mount(
if (r < 0)
return r;
}
}
if (dest) {
r = mkdir_p_label(dest, 0755);

View File

@ -165,6 +165,7 @@ int dissect_image_file(const char *path, const VeritySettings *verity, const Mou
int dissect_image_file_and_warn(const char *path, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, const ImageFilter *filter, DissectImageFlags flags, DissectedImage **ret);
int dissect_loop_device(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, const ImageFilter *image_filter, DissectImageFlags flags, DissectedImage **ret);
int dissect_loop_device_and_warn(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, const ImageFilter *image_filter, DissectImageFlags flags, DissectedImage **ret);
int dissected_image_new_from_existing_verity(const char *src, const VeritySettings *verity, const MountOptions *options, const ImagePolicy *image_policy, const ImageFilter *image_filter, RuntimeScope runtime_scope, DissectImageFlags dissect_image_flags, DissectedImage **ret);
void dissected_image_close(DissectedImage *m);
DissectedImage* dissected_image_unref(DissectedImage *m);

View File

@ -207,6 +207,20 @@ static PartitionPolicyFlags policy_flag_from_string_one(const char *s) {
return PARTITION_POLICY_GROWFS_ON;
if (streq(s, "growfs-off"))
return PARTITION_POLICY_GROWFS_OFF;
if (streq(s, "btrfs"))
return PARTITION_POLICY_BTRFS;
if (streq(s, "erofs"))
return PARTITION_POLICY_EROFS;
if (streq(s, "ext4"))
return PARTITION_POLICY_EXT4;
if (streq(s, "f2fs"))
return PARTITION_POLICY_F2FS;
if (streq(s, "squashfs"))
return PARTITION_POLICY_SQUASHFS;
if (streq(s, "vfat"))
return PARTITION_POLICY_VFAT;
if (streq(s, "xfs"))
return PARTITION_POLICY_XFS;
return _PARTITION_POLICY_FLAGS_INVALID;
}
@ -389,7 +403,7 @@ int image_policy_from_string(const char *s, bool graceful, ImagePolicy **ret) {
int partition_policy_flags_to_string(PartitionPolicyFlags flags, bool simplify, char **ret) {
_cleanup_free_ char *buf = NULL;
const char *l[CONST_LOG2U(_PARTITION_POLICY_MASK + 1) + 1]; /* one string per known flag at most */
const char *l[CONST_LOG2U(_PARTITION_POLICY_MASK + _PARTITION_POLICY_FSTYPE_MASK + 1) + 1]; /* one string per known flag at most */
size_t m = 0;
assert(ret);
@ -449,6 +463,23 @@ int partition_policy_flags_to_string(PartitionPolicyFlags flags, bool simplify,
l[m++] = "growfs-on";
}
/* These flags must translate to the literal fstype name of each filesystem, as accepted by
* `mount -t`. */
if (flags & PARTITION_POLICY_BTRFS)
l[m++] = "btrfs";
if (flags & PARTITION_POLICY_EROFS)
l[m++] = "erofs";
if (flags & PARTITION_POLICY_EXT4)
l[m++] = "ext4";
if (flags & PARTITION_POLICY_F2FS)
l[m++] = "f2fs";
if (flags & PARTITION_POLICY_SQUASHFS)
l[m++] = "squashfs";
if (flags & PARTITION_POLICY_VFAT)
l[m++] = "vfat";
if (flags & PARTITION_POLICY_XFS)
l[m++] = "xfs";
if (m == 0)
buf = strdup("-");
else {
@ -461,7 +492,44 @@ int partition_policy_flags_to_string(PartitionPolicyFlags flags, bool simplify,
return -ENOMEM;
*ret = TAKE_PTR(buf);
return (int) m;
}
int partition_policy_determine_fstype(
const ImagePolicy *policy,
PartitionDesignator designator,
bool *ret_encrypted,
char **ret_fstype) {
_cleanup_free_ char *fstype = NULL;
PartitionPolicyFlags policy_flags;
int r;
assert(designator >= 0 && designator < _PARTITION_DESIGNATOR_MAX);
assert(ret_fstype);
policy_flags = image_policy_get_exhaustively(policy, designator);
if (policy_flags < 0)
return policy_flags;
/* The policy fstype flags translate to the literal fstype name of each filesystem. */
r = partition_policy_flags_to_string(policy_flags & _PARTITION_POLICY_FSTYPE_MASK, /* simplify= */ true, &fstype);
if (r < 0)
return r;
/* Input must be a single filesystem type, if the policy specifies more than one, return NULL */
if (r != 1) {
if (ret_encrypted)
*ret_encrypted = false;
*ret_fstype = NULL;
return 0;
}
/* If the policy also allows unprotected or verity filesystems, don't set the 'encrypted' flag */
if (ret_encrypted)
*ret_encrypted = (policy_flags & (PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY)) &&
!(policy_flags & (PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_UNPROTECTED));
*ret_fstype = TAKE_PTR(fstype);
return 1;
}
static bool partition_policy_flags_extended_equal(PartitionPolicyFlags a, PartitionPolicyFlags b) {

View File

@ -36,6 +36,19 @@ typedef enum PartitionPolicyFlags {
_PARTITION_POLICY_MASK = _PARTITION_POLICY_USE_MASK|_PARTITION_POLICY_READ_ONLY_MASK|_PARTITION_POLICY_GROWFS_MASK,
/* Policies can impose filesystem type requirements on images. These flags translate to the literal
* fstype name of each filesystem. */
PARTITION_POLICY_BTRFS = 1 << 11,
PARTITION_POLICY_EROFS = 1 << 12,
PARTITION_POLICY_EXT4 = 1 << 13,
PARTITION_POLICY_F2FS = 1 << 14,
PARTITION_POLICY_SQUASHFS = 1 << 15,
PARTITION_POLICY_VFAT = 1 << 16,
PARTITION_POLICY_XFS = 1 << 17,
_PARTITION_POLICY_FSTYPE_MASK = PARTITION_POLICY_BTRFS|PARTITION_POLICY_EROFS|PARTITION_POLICY_EXT4|
PARTITION_POLICY_F2FS|PARTITION_POLICY_SQUASHFS|
PARTITION_POLICY_VFAT|PARTITION_POLICY_XFS,
_PARTITION_POLICY_FLAGS_INVALID = -EINVAL,
_PARTITION_POLICY_FLAGS_ERRNO_MAX = -ERRNO_MAX, /* Ensure the whole errno range fits into this enum */
} PartitionPolicyFlags;
@ -84,6 +97,8 @@ PartitionPolicyFlags partition_policy_flags_reduce(PartitionPolicyFlags flags);
PartitionPolicyFlags partition_policy_flags_from_string(const char *s, bool graceful);
int partition_policy_flags_to_string(PartitionPolicyFlags flags, bool simplify, char **ret);
int partition_policy_determine_fstype(const ImagePolicy *policy, PartitionDesignator designator, bool *ret_encrypted, char **ret_fstype);
int image_policy_from_string(const char *s, bool graceful, ImagePolicy **ret);
int image_policy_to_string(const ImagePolicy *policy, bool simplify, char **ret);

View File

@ -1715,9 +1715,11 @@ int add_matches_for_unit_full(sd_journal *j, MatchUnitFlag flags, const char *un
/* Look for messages from the service itself */
(r = journal_add_match_pair(j, "_SYSTEMD_UNIT", unit)) ||
/* Look for messages from PID 1 about this service */
/* Look for messages from PID 1 about this service. Note that the actual match is placed
* on init.scope rather than _PID=1, as we want to match messages from helper processes
* forked off by init too. */
(r = sd_journal_add_disjunction(j)) ||
(r = sd_journal_add_match(j, "_PID=1", SIZE_MAX)) ||
(r = sd_journal_add_match(j, "_SYSTEMD_CGROUP=/init.scope", SIZE_MAX)) ||
(r = journal_add_match_pair(j, "UNIT", unit)) ||
/* Look for messages from authorized daemons about this service */

View File

@ -455,6 +455,18 @@ _noreturn_ void log_test_failed_internal(const char *file, int line, const char
})
#endif
#ifdef __COVERITY__
# define ASSERT_NOT_STREQ(expr1, expr2) __coverity_check__(!streq_ptr((expr1), (expr2)))
#else
# define ASSERT_NOT_STREQ(expr1, expr2) \
({ \
const char *_expr1 = (expr1), *_expr2 = (expr2); \
if (streq_ptr(_expr1, _expr2)) \
log_test_failed("Expected \"%s != %s\", got \"%s == %s\"", \
#expr1, #expr2, strnull(_expr1), strnull(_expr2)); \
})
#endif
#ifdef __COVERITY__
# define ASSERT_STRNEQ(expr1, expr2, n) __coverity_check__(strneq_ptr((expr1), (expr2), (n)))
#else

View File

@ -4,13 +4,7 @@ if conf.get('HAVE_LIBMOUNT') != 1
subdir_done()
endif
systemd_shutdown_sources = files(
'detach-dm.c',
'detach-loopback.c',
'detach-md.c',
'shutdown.c',
)
systemd_shutdown_extract_sources = files(
shutdown_detach_sources = files(
'detach-swap.c',
'umount.c',
)
@ -18,14 +12,17 @@ systemd_shutdown_extract_sources = files(
executables += [
libexec_template + {
'name' : 'systemd-shutdown',
'sources' : systemd_shutdown_sources,
'extract' : systemd_shutdown_extract_sources,
'extract' : files(
'detach-dm.c',
'detach-loopback.c',
'detach-md.c',
'shutdown.c',
) + shutdown_detach_sources,
'dependencies' : libmount_cflags,
},
libexec_template + {
'name' : 'systemd-shutdown.standalone',
'sources' : systemd_shutdown_sources + systemd_shutdown_extract_sources,
'c_args' : '-DSTANDALONE',
'objects' : ['systemd-shutdown'],
'link_with' : [
libc_wrapper_static,
libbasic_static,
@ -35,8 +32,8 @@ executables += [
'dependencies' : libmount_cflags,
},
test_template + {
'sources' : files('test-umount.c'),
'objects' : ['systemd-shutdown'],
'sources' : files('test-umount.c') +
shutdown_detach_sources,
'dependencies' : libmount_cflags,
},
]

View File

@ -8,14 +8,13 @@ executables += [
executable_template + {
'name' : 'systemd-sysusers',
'public' : true,
'sources' : files('sysusers.c'),
'extract' : files('sysusers.c'),
'dependencies' : libaudit_cflags,
},
executable_template + {
'name' : 'systemd-sysusers.standalone',
'public' : true,
'sources' : files('sysusers.c'),
'c_args' : '-DSTANDALONE',
'objects' : ['systemd-sysusers'],
'link_with' : [
libc_wrapper_static,
libbasic_static,

View File

@ -2151,15 +2151,10 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_IMAGE:
#ifdef STANDALONE
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"This systemd-sysusers version is compiled without support for --image=.");
#else
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
if (r < 0)
return r;
break;
#endif
case ARG_IMAGE_POLICY:
r = parse_image_policy_argument(optarg, &arg_image_policy);
@ -2283,10 +2278,8 @@ static int read_credential_lines(Context *c) {
}
static int run(int argc, char *argv[]) {
#ifndef STANDALONE
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(umount_and_freep) char *mounted_dir = NULL;
#endif
_cleanup_close_ int lock = -EBADF;
_cleanup_(context_done) Context c = {
.audit_fd = -EBADF,
@ -2314,7 +2307,6 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
#ifndef STANDALONE
if (arg_image) {
assert(!arg_root);
@ -2338,9 +2330,6 @@ static int run(int argc, char *argv[]) {
if (!arg_root)
return log_oom();
}
#else
assert(!arg_image);
#endif
/* Prepare to emit audit events, but only if we're operating on the host system. */
if (!arg_root)

View File

@ -92,6 +92,10 @@ TEST_RET(test_image_policy_to_string) {
test_policy_string("swap=open:root=signed+read-only-on+growfs-off:=absent");
test_policy_string("=-");
test_policy_string("=");
test_policy_string("root=ext4+squashfs+verity");
test_policy_string("usr=encrypted+erofs+read-only-off");
test_policy_string("home=unprotected+btrfs");
test_policy_string("=vfat+erofs");
test_policy_equiv("", image_policy_equiv_ignore);
test_policy_equiv("-", image_policy_equiv_ignore);
@ -197,4 +201,68 @@ TEST(image_policy_ignore_designators) {
test_policy_ignore_designators_one("~", ((const PartitionDesignator[]) { PARTITION_VAR, PARTITION_ESP, PARTITION_VAR }), 2, "var=ignore:esp=ignore:=absent");
}
TEST(partition_policy_determine_fstype) {
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
_cleanup_free_ char *fstype = NULL;
bool encrypted;
int r;
ASSERT_OK(image_policy_from_string("root=ext4+encrypted", /* graceful= */ false, &p));
r = partition_policy_determine_fstype(p, PARTITION_ROOT, &encrypted, &fstype);
ASSERT_GT(r, 0);
ASSERT_STREQ(fstype, "ext4");
ASSERT_TRUE(encrypted);
fstype = mfree(fstype);
p = image_policy_free(p);
ASSERT_OK(image_policy_from_string("usr=verity+erofs", /* graceful= */ false, &p));
r = partition_policy_determine_fstype(p, PARTITION_USR, &encrypted, &fstype);
ASSERT_GT(r, 0);
ASSERT_STREQ(fstype, "erofs");
ASSERT_FALSE(encrypted);
fstype = mfree(fstype);
r = partition_policy_determine_fstype(p, PARTITION_ROOT, &encrypted, &fstype);
ASSERT_EQ(r, 0);
ASSERT_FALSE(fstype);
ASSERT_FALSE(encrypted);
fstype = mfree(fstype);
p = image_policy_free(p);
ASSERT_OK(image_policy_from_string("root=ext4+erofs", /* graceful= */ false, &p));
r = partition_policy_determine_fstype(p, PARTITION_ROOT, &encrypted, &fstype);
ASSERT_EQ(r, 0);
ASSERT_FALSE(fstype);
ASSERT_FALSE(encrypted);
fstype = mfree(fstype);
p = image_policy_free(p);
ASSERT_OK(image_policy_from_string("root=encrypted", /* graceful= */ false, &p));
r = partition_policy_determine_fstype(p, PARTITION_ROOT, &encrypted, &fstype);
ASSERT_EQ(r, 0);
ASSERT_FALSE(fstype);
ASSERT_FALSE(encrypted);
fstype = mfree(fstype);
p = image_policy_free(p);
ASSERT_OK(image_policy_from_string("", /* graceful= */ false, &p));
r = partition_policy_determine_fstype(p, PARTITION_ROOT, &encrypted, &fstype);
ASSERT_EQ(r, 0);
ASSERT_FALSE(fstype);
ASSERT_FALSE(encrypted);
fstype = mfree(fstype);
r = partition_policy_determine_fstype(/* policy= */ NULL, PARTITION_ROOT, &encrypted, &fstype);
ASSERT_EQ(r, 0);
ASSERT_FALSE(fstype);
ASSERT_FALSE(encrypted);
fstype = mfree(fstype);
p = image_policy_free(p);
ASSERT_OK(image_policy_from_string("root=encrypted+signed+btrfs", /* graceful= */ false, &p));
r = partition_policy_determine_fstype(p, PARTITION_ROOT, &encrypted, &fstype);
ASSERT_GT(r, 0);
ASSERT_STREQ(fstype, "btrfs");
ASSERT_FALSE(encrypted);
}
DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -91,6 +91,11 @@ TEST(ASSERT) {
ASSERT_SIGNAL(ASSERT_STREQ(null, "bar"), SIGABRT);
ASSERT_SIGNAL(ASSERT_STREQ("foo", "bar"), SIGABRT);
ASSERT_NOT_STREQ("foo", "bar");
ASSERT_NOT_STREQ("foo", NULL);
ASSERT_SIGNAL(ASSERT_NOT_STREQ("foo", "foo"), SIGABRT);
ASSERT_SIGNAL(ASSERT_NOT_STREQ(NULL, NULL), SIGABRT);
ASSERT_EQ(0, 0);
ASSERT_EQ(-1, -1);
ASSERT_SIGNAL(ASSERT_EQ(255, -1), SIGABRT);

View File

@ -17,7 +17,7 @@ static void test_uid_to_name_one(uid_t uid, const char *name) {
log_info("/* %s("UID_FMT", \"%s\") */", __func__, uid, name);
assert_se(t = uid_to_name(uid));
t = ASSERT_NOT_NULL(uid_to_name(uid));
if (!synthesize_nobody() && streq(name, NOBODY_USER_NAME)) {
log_info("(skipping detailed tests because nobody is not synthesized)");
return;
@ -37,7 +37,7 @@ static void test_gid_to_name_one(gid_t gid, const char *name) {
log_info("/* %s("GID_FMT", \"%s\") */", __func__, gid, name);
assert_se(t = gid_to_name(gid));
t = ASSERT_NOT_NULL(gid_to_name(gid));
if (!synthesize_nobody() && streq(name, NOBODY_GROUP_NAME)) {
log_info("(skipping detailed tests because nobody is not synthesized)");
return;
@ -53,290 +53,238 @@ TEST(gid_to_name) {
}
TEST(parse_uid) {
int r;
uid_t uid;
r = parse_uid("0", &uid);
assert_se(r == 0);
assert_se(uid == 0);
ASSERT_OK(parse_uid("0", &uid));
ASSERT_EQ(uid, 0u);
r = parse_uid("1", &uid);
assert_se(r == 0);
assert_se(uid == 1);
ASSERT_OK(parse_uid("1", &uid));
ASSERT_EQ(uid, 1u);
r = parse_uid("01", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 1);
ASSERT_ERROR(parse_uid("01", &uid), EINVAL);
ASSERT_EQ(uid, 1u);
ASSERT_ERROR(parse_uid("001", &uid), EINVAL);
r = parse_uid("001", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 1);
ASSERT_OK(parse_uid("100", &uid));
ASSERT_EQ(uid, 100u);
r = parse_uid("100", &uid);
assert_se(r == 0);
assert_se(uid == 100);
r = parse_uid("65535", &uid);
assert_se(r == -ENXIO);
assert_se(uid == 100);
r = parse_uid("0x1234", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 100);
r = parse_uid("0o1234", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 100);
r = parse_uid("0b1234", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 100);
r = parse_uid("+1234", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 100);
r = parse_uid("-1234", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 100);
r = parse_uid(" 1234", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 100);
r = parse_uid("01234", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 100);
r = parse_uid("001234", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 100);
r = parse_uid("0001234", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 100);
r = parse_uid("-0", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 100);
r = parse_uid("+0", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 100);
r = parse_uid("00", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 100);
r = parse_uid("000", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 100);
r = parse_uid("asdsdas", &uid);
assert_se(r == -EINVAL);
assert_se(uid == 100);
ASSERT_ERROR(parse_uid("65535", &uid), ENXIO);
ASSERT_ERROR(parse_uid("0x1234", &uid), EINVAL);
ASSERT_ERROR(parse_uid("0o1234", &uid), EINVAL);
ASSERT_ERROR(parse_uid("0b1234", &uid), EINVAL);
ASSERT_ERROR(parse_uid("+1234", &uid), EINVAL);
ASSERT_ERROR(parse_uid("-1234", &uid), EINVAL);
ASSERT_ERROR(parse_uid(" 1234", &uid), EINVAL);
ASSERT_ERROR(parse_uid("01234", &uid), EINVAL);
ASSERT_ERROR(parse_uid("001234", &uid), EINVAL);
ASSERT_ERROR(parse_uid("0001234", &uid), EINVAL);
ASSERT_ERROR(parse_uid("-0", &uid), EINVAL);
ASSERT_ERROR(parse_uid("+0", &uid), EINVAL);
ASSERT_ERROR(parse_uid("00", &uid), EINVAL);
ASSERT_ERROR(parse_uid("000", &uid), EINVAL);
ASSERT_ERROR(parse_uid("asdsdas", &uid), EINVAL);
}
TEST(uid_ptr) {
ASSERT_NOT_NULL(UID_TO_PTR(0));
ASSERT_NOT_NULL(UID_TO_PTR(1000));
assert_se(PTR_TO_UID(UID_TO_PTR(0)) == 0);
assert_se(PTR_TO_UID(UID_TO_PTR(1000)) == 1000);
ASSERT_EQ(PTR_TO_UID(UID_TO_PTR(0)), 0u);
ASSERT_EQ(PTR_TO_UID(UID_TO_PTR(1000)), 1000u);
}
TEST(valid_user_group_name_relaxed) {
assert_se(!valid_user_group_name(NULL, VALID_USER_RELAX));
assert_se(!valid_user_group_name("", VALID_USER_RELAX));
assert_se(!valid_user_group_name("1", VALID_USER_RELAX));
assert_se(!valid_user_group_name("65535", VALID_USER_RELAX));
assert_se(!valid_user_group_name("-1", VALID_USER_RELAX));
assert_se(!valid_user_group_name("foo\nbar", VALID_USER_RELAX));
assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", VALID_USER_RELAX));
assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_RELAX|VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name(".aaa:bbb", VALID_USER_RELAX|VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name(".", VALID_USER_RELAX));
assert_se(!valid_user_group_name("..", VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name(NULL, VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name("", VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name("1", VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name("65535", VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name("-1", VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name("foo\nbar", VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name("0123456789012345678901234567890123456789", VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name("aaa:bbb", VALID_USER_RELAX|VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name(".aaa:bbb", VALID_USER_RELAX|VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name(".", VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name("..", VALID_USER_RELAX));
assert_se(valid_user_group_name("root", VALID_USER_RELAX));
assert_se(valid_user_group_name("lennart", VALID_USER_RELAX));
assert_se(valid_user_group_name("LENNART", VALID_USER_RELAX));
assert_se(valid_user_group_name("_kkk", VALID_USER_RELAX));
assert_se(valid_user_group_name("kkk-", VALID_USER_RELAX));
assert_se(valid_user_group_name("kk-k", VALID_USER_RELAX));
assert_se(valid_user_group_name("eff.eff", VALID_USER_RELAX));
assert_se(valid_user_group_name("eff.", VALID_USER_RELAX));
assert_se(valid_user_group_name("-kkk", VALID_USER_RELAX));
assert_se(valid_user_group_name("rööt", VALID_USER_RELAX));
assert_se(valid_user_group_name(".eff", VALID_USER_RELAX));
assert_se(valid_user_group_name(".1", VALID_USER_RELAX));
assert_se(valid_user_group_name(".65535", VALID_USER_RELAX));
assert_se(valid_user_group_name(".-1", VALID_USER_RELAX));
assert_se(valid_user_group_name(".-kkk", VALID_USER_RELAX));
assert_se(valid_user_group_name(".rööt", VALID_USER_RELAX));
assert_se(valid_user_group_name("...", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("root", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("lennart", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("LENNART", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("_kkk", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("kkk-", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("kk-k", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("eff.eff", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("eff.", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("-kkk", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("rööt", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name(".eff", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name(".1", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name(".65535", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name(".-1", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name(".-kkk", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name(".rööt", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("...", VALID_USER_RELAX));
assert_se(valid_user_group_name("some5", VALID_USER_RELAX));
assert_se(valid_user_group_name("5some", VALID_USER_RELAX));
assert_se(valid_user_group_name("INNER5NUMBER", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("some5", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("5some", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("INNER5NUMBER", VALID_USER_RELAX));
assert_se(valid_user_group_name("piff.paff@ad.domain.example", VALID_USER_RELAX));
assert_se(valid_user_group_name("Dāvis", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("piff.paff@ad.domain.example", VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("Dāvis", VALID_USER_RELAX));
}
TEST(valid_user_group_name) {
assert_se(!valid_user_group_name(NULL, 0));
assert_se(!valid_user_group_name("", 0));
assert_se(!valid_user_group_name("1", 0));
assert_se(!valid_user_group_name("65535", 0));
assert_se(!valid_user_group_name("-1", 0));
assert_se(!valid_user_group_name("-kkk", 0));
assert_se(!valid_user_group_name("rööt", 0));
assert_se(!valid_user_group_name(".", 0));
assert_se(!valid_user_group_name(".eff", 0));
assert_se(!valid_user_group_name("foo\nbar", 0));
assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", 0));
assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name(".", 0));
assert_se(!valid_user_group_name("..", 0));
assert_se(!valid_user_group_name("...", 0));
assert_se(!valid_user_group_name(".1", 0));
assert_se(!valid_user_group_name(".65535", 0));
assert_se(!valid_user_group_name(".-1", 0));
assert_se(!valid_user_group_name(".-kkk", 0));
assert_se(!valid_user_group_name(".rööt", 0));
assert_se(!valid_user_group_name(".aaa:bbb", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name(NULL, 0));
ASSERT_FALSE(valid_user_group_name("", 0));
ASSERT_FALSE(valid_user_group_name("1", 0));
ASSERT_FALSE(valid_user_group_name("65535", 0));
ASSERT_FALSE(valid_user_group_name("-1", 0));
ASSERT_FALSE(valid_user_group_name("-kkk", 0));
ASSERT_FALSE(valid_user_group_name("rööt", 0));
ASSERT_FALSE(valid_user_group_name(".", 0));
ASSERT_FALSE(valid_user_group_name(".eff", 0));
ASSERT_FALSE(valid_user_group_name("foo\nbar", 0));
ASSERT_FALSE(valid_user_group_name("0123456789012345678901234567890123456789", 0));
ASSERT_FALSE(valid_user_group_name("aaa:bbb", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name(".", 0));
ASSERT_FALSE(valid_user_group_name("..", 0));
ASSERT_FALSE(valid_user_group_name("...", 0));
ASSERT_FALSE(valid_user_group_name(".1", 0));
ASSERT_FALSE(valid_user_group_name(".65535", 0));
ASSERT_FALSE(valid_user_group_name(".-1", 0));
ASSERT_FALSE(valid_user_group_name(".-kkk", 0));
ASSERT_FALSE(valid_user_group_name(".rööt", 0));
ASSERT_FALSE(valid_user_group_name(".aaa:bbb", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("root", 0));
assert_se(valid_user_group_name("lennart", 0));
assert_se(valid_user_group_name("LENNART", 0));
assert_se(valid_user_group_name("_kkk", 0));
assert_se(valid_user_group_name("kkk-", 0));
assert_se(valid_user_group_name("kk-k", 0));
assert_se(!valid_user_group_name("eff.eff", 0));
assert_se(!valid_user_group_name("eff.", 0));
ASSERT_TRUE(valid_user_group_name("root", 0));
ASSERT_TRUE(valid_user_group_name("lennart", 0));
ASSERT_TRUE(valid_user_group_name("LENNART", 0));
ASSERT_TRUE(valid_user_group_name("_kkk", 0));
ASSERT_TRUE(valid_user_group_name("kkk-", 0));
ASSERT_TRUE(valid_user_group_name("kk-k", 0));
ASSERT_FALSE(valid_user_group_name("eff.eff", 0));
ASSERT_FALSE(valid_user_group_name("eff.", 0));
assert_se(valid_user_group_name("some5", 0));
assert_se(!valid_user_group_name("5some", 0));
assert_se(valid_user_group_name("INNER5NUMBER", 0));
ASSERT_TRUE(valid_user_group_name("some5", 0));
ASSERT_FALSE(valid_user_group_name("5some", 0));
ASSERT_TRUE(valid_user_group_name("INNER5NUMBER", 0));
assert_se(!valid_user_group_name("piff.paff@ad.domain.example", 0));
assert_se(!valid_user_group_name("Dāvis", 0));
ASSERT_FALSE(valid_user_group_name("piff.paff@ad.domain.example", 0));
ASSERT_FALSE(valid_user_group_name("Dāvis", 0));
}
TEST(valid_user_group_name_or_numeric_relaxed) {
assert_se(!valid_user_group_name(NULL, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name("", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("0", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("1", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("65534", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name("65535", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("65536", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name("-1", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name("foo\nbar", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name(".", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name("..", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name(NULL, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name("", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("0", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("1", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("65534", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name("65535", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("65536", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name("-1", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name("foo\nbar", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name("0123456789012345678901234567890123456789", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name("aaa:bbb", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name(".", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_FALSE(valid_user_group_name("..", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("root", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("lennart", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("LENNART", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("_kkk", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("kkk-", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("kk-k", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("-kkk", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("rööt", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name(".eff", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("eff.eff", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("eff.", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("...", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("root", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("lennart", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("LENNART", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("_kkk", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("kkk-", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("kk-k", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("-kkk", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("rööt", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name(".eff", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("eff.eff", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("eff.", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("...", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("some5", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("5some", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("INNER5NUMBER", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("some5", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("5some", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("INNER5NUMBER", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("piff.paff@ad.domain.example", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("Dāvis", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("piff.paff@ad.domain.example", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
ASSERT_TRUE(valid_user_group_name("Dāvis", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
}
TEST(valid_user_group_name_or_numeric) {
assert_se(!valid_user_group_name(NULL, VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("0", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("1", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("65534", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("65535", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("65536", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("-1", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("-kkk", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("rööt", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name(".", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("..", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("...", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name(".eff", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("eff.eff", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("eff.", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("foo\nbar", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name(NULL, VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name("", VALID_USER_ALLOW_NUMERIC));
ASSERT_TRUE(valid_user_group_name("0", VALID_USER_ALLOW_NUMERIC));
ASSERT_TRUE(valid_user_group_name("1", VALID_USER_ALLOW_NUMERIC));
ASSERT_TRUE(valid_user_group_name("65534", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name("65535", VALID_USER_ALLOW_NUMERIC));
ASSERT_TRUE(valid_user_group_name("65536", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name("-1", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name("-kkk", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name("rööt", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name(".", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name("..", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name("...", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name(".eff", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name("eff.eff", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name("eff.", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name("foo\nbar", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name("0123456789012345678901234567890123456789", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name("aaa:bbb", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("root", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("lennart", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("LENNART", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("_kkk", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("kkk-", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("kk-k", VALID_USER_ALLOW_NUMERIC));
ASSERT_TRUE(valid_user_group_name("root", VALID_USER_ALLOW_NUMERIC));
ASSERT_TRUE(valid_user_group_name("lennart", VALID_USER_ALLOW_NUMERIC));
ASSERT_TRUE(valid_user_group_name("LENNART", VALID_USER_ALLOW_NUMERIC));
ASSERT_TRUE(valid_user_group_name("_kkk", VALID_USER_ALLOW_NUMERIC));
ASSERT_TRUE(valid_user_group_name("kkk-", VALID_USER_ALLOW_NUMERIC));
ASSERT_TRUE(valid_user_group_name("kk-k", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("some5", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("5some", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("INNER5NUMBER", VALID_USER_ALLOW_NUMERIC));
ASSERT_TRUE(valid_user_group_name("some5", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name("5some", VALID_USER_ALLOW_NUMERIC));
ASSERT_TRUE(valid_user_group_name("INNER5NUMBER", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("piff.paff@ad.domain.example", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("Dāvis", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name("piff.paff@ad.domain.example", VALID_USER_ALLOW_NUMERIC));
ASSERT_FALSE(valid_user_group_name("Dāvis", VALID_USER_ALLOW_NUMERIC));
}
TEST(valid_gecos) {
assert_se(!valid_gecos(NULL));
assert_se(valid_gecos(""));
assert_se(valid_gecos("test"));
assert_se(valid_gecos("Ümläüt"));
assert_se(!valid_gecos("In\nvalid"));
assert_se(!valid_gecos("In:valid"));
ASSERT_FALSE(valid_gecos(NULL));
ASSERT_TRUE(valid_gecos(""));
ASSERT_TRUE(valid_gecos("test"));
ASSERT_TRUE(valid_gecos("Ümläüt"));
ASSERT_FALSE(valid_gecos("In\nvalid"));
ASSERT_FALSE(valid_gecos("In:valid"));
}
TEST(valid_home) {
assert_se(!valid_home(NULL));
assert_se(!valid_home(""));
assert_se(!valid_home("."));
assert_se(!valid_home("/home/.."));
assert_se(!valid_home("/home/../"));
assert_se(!valid_home("/home\n/foo"));
assert_se(!valid_home("./piep"));
assert_se(!valid_home("piep"));
assert_se(!valid_home("/home/user:lennart"));
ASSERT_FALSE(valid_home(NULL));
ASSERT_FALSE(valid_home(""));
ASSERT_FALSE(valid_home("."));
ASSERT_FALSE(valid_home("/home/.."));
ASSERT_FALSE(valid_home("/home/../"));
ASSERT_FALSE(valid_home("/home\n/foo"));
ASSERT_FALSE(valid_home("./piep"));
ASSERT_FALSE(valid_home("piep"));
ASSERT_FALSE(valid_home("/home/user:lennart"));
assert_se(valid_home("/"));
assert_se(valid_home("/home"));
assert_se(valid_home("/home/foo"));
assert_se(valid_home("/home/foo/"));
ASSERT_TRUE(valid_home("/"));
ASSERT_TRUE(valid_home("/home"));
ASSERT_TRUE(valid_home("/home/foo"));
ASSERT_TRUE(valid_home("/home/foo/"));
}
TEST(valid_shell) {
assert_se(!valid_shell(NULL));
assert_se(!valid_shell(""));
assert_se(!valid_shell("."));
assert_se(!valid_shell("/shell/.."));
assert_se(!valid_shell("/shell/../"));
assert_se(!valid_shell("/shell\n/foo"));
assert_se(!valid_shell("./piep"));
assert_se(!valid_shell("piep"));
assert_se(!valid_shell("/shell/user:lennart"));
assert_se(!valid_shell("/"));
assert_se(!valid_shell("/bin/sh/"));
assert_se(valid_shell("/shell"));
assert_se(valid_shell("/shell/foo"));
assert_se(valid_shell("/bin/sh"));
ASSERT_FALSE(valid_shell(NULL));
ASSERT_FALSE(valid_shell(""));
ASSERT_FALSE(valid_shell("."));
ASSERT_FALSE(valid_shell("/shell/.."));
ASSERT_FALSE(valid_shell("/shell/../"));
ASSERT_FALSE(valid_shell("/shell\n/foo"));
ASSERT_FALSE(valid_shell("./piep"));
ASSERT_FALSE(valid_shell("piep"));
ASSERT_FALSE(valid_shell("/shell/user:lennart"));
ASSERT_FALSE(valid_shell("/"));
ASSERT_FALSE(valid_shell("/bin/sh/"));
ASSERT_TRUE(valid_shell("/shell"));
ASSERT_TRUE(valid_shell("/shell/foo"));
ASSERT_TRUE(valid_shell("/bin/sh"));
}
static void test_get_user_creds_one(const char *id, const char *name, uid_t uid, gid_t gid, const char *home, const char *shell) {
@ -356,11 +304,11 @@ static void test_get_user_creds_one(const char *id, const char *name, uid_t uid,
log_info("(skipping detailed tests because nobody is not synthesized)");
return;
}
assert_se(r == 0);
ASSERT_OK(r);
ASSERT_STREQ(id, name);
assert_se(ruid == uid);
assert_se(rgid == gid);
assert_se(path_equal(rhome, home));
ASSERT_EQ(ruid, uid);
ASSERT_EQ(rgid, gid);
ASSERT_TRUE(path_equal(rhome, home));
}
TEST(get_user_creds) {
@ -382,9 +330,9 @@ static void test_get_group_creds_one(const char *id, const char *name, gid_t gid
log_info("(skipping detailed tests because nobody is not synthesized)");
return;
}
assert_se(r == 0);
ASSERT_OK(r);
ASSERT_STREQ(id, name);
assert_se(rgid == gid);
ASSERT_EQ(rgid, gid);
}
TEST(get_group_creds) {
@ -397,20 +345,20 @@ TEST(get_group_creds) {
TEST(make_salt) {
_cleanup_free_ char *s, *t;
assert_se(make_salt(&s) == 0);
ASSERT_OK(make_salt(&s));
log_info("got %s", s);
assert_se(make_salt(&t) == 0);
ASSERT_OK(make_salt(&t));
log_info("got %s", t);
assert_se(!streq(s, t));
ASSERT_NOT_STREQ(s, t);
}
TEST(in_gid) {
assert_se(in_gid(getgid()) >= 0);
assert_se(in_gid(getegid()) >= 0);
assert_se(in_gid(GID_INVALID) < 0);
assert_se(in_gid(TTY_GID) == 0); /* The TTY gid is for owning ttys, it would be really really weird if we were in it. */
ASSERT_OK(in_gid(getgid()));
ASSERT_OK(in_gid(getegid()));
ASSERT_FAIL(in_gid(GID_INVALID));
ASSERT_OK_ZERO(in_gid(TTY_GID)); /* The TTY gid is for owning ttys, it would be really really weird if we were in it. */
}
TEST(gid_lists_ops) {
@ -429,63 +377,76 @@ TEST(gid_lists_ops) {
_cleanup_free_ gid_t *res4 = NULL;
int nresult;
nresult = merge_gid_lists(l2, ELEMENTSOF(l2), l3, ELEMENTSOF(l3), &res1);
assert_se(nresult >= 0);
assert_se(memcmp_nn(res1, nresult, result1, ELEMENTSOF(result1)) == 0);
nresult = ASSERT_OK(merge_gid_lists(l2, ELEMENTSOF(l2), l3, ELEMENTSOF(l3), &res1));
ASSERT_EQ(memcmp_nn(res1, nresult, result1, ELEMENTSOF(result1)), 0);
nresult = merge_gid_lists(NULL, 0, l2, ELEMENTSOF(l2), &res2);
assert_se(nresult >= 0);
assert_se(memcmp_nn(res2, nresult, l2, ELEMENTSOF(l2)) == 0);
nresult = ASSERT_OK(merge_gid_lists(NULL, 0, l2, ELEMENTSOF(l2), &res2));
ASSERT_EQ(memcmp_nn(res2, nresult, l2, ELEMENTSOF(l2)), 0);
nresult = merge_gid_lists(l1, ELEMENTSOF(l1), l1, ELEMENTSOF(l1), &res3);
assert_se(nresult >= 0);
assert_se(memcmp_nn(l1, ELEMENTSOF(l1), res3, nresult) == 0);
nresult = ASSERT_OK(merge_gid_lists(l1, ELEMENTSOF(l1), l1, ELEMENTSOF(l1), &res3));
ASSERT_EQ(memcmp_nn(l1, ELEMENTSOF(l1), res3, nresult), 0);
nresult = merge_gid_lists(l1, ELEMENTSOF(l1), l4, ELEMENTSOF(l4), &res4);
assert_se(nresult >= 0);
assert_se(memcmp_nn(result2, ELEMENTSOF(result2), res4, nresult) == 0);
nresult = ASSERT_OK(merge_gid_lists(l1, ELEMENTSOF(l1), l4, ELEMENTSOF(l4), &res4));
ASSERT_EQ(memcmp_nn(result2, ELEMENTSOF(result2), res4, nresult), 0);
ASSERT_OK(nresult = getgroups_alloc(&gids));
assert_se(gids || nresult == 0);
nresult = ASSERT_OK(getgroups_alloc(&gids));
if (nresult > 0)
ASSERT_TRUE(gids);
}
TEST(parse_uid_range) {
uid_t a = 4711, b = 4711;
assert_se(parse_uid_range("", &a, &b) == -EINVAL && a == 4711 && b == 4711);
assert_se(parse_uid_range(" ", &a, &b) == -EINVAL && a == 4711 && b == 4711);
assert_se(parse_uid_range("x", &a, &b) == -EINVAL && a == 4711 && b == 4711);
ASSERT_ERROR(parse_uid_range("", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range(" ", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range("x", &a, &b), EINVAL);
ASSERT_EQ(a, 4711u);
ASSERT_EQ(b, 4711u);
assert_se(parse_uid_range("0", &a, &b) >= 0 && a == 0 && b == 0);
assert_se(parse_uid_range("1", &a, &b) >= 0 && a == 1 && b == 1);
assert_se(parse_uid_range("2-2", &a, &b) >= 0 && a == 2 && b == 2);
assert_se(parse_uid_range("3-3", &a, &b) >= 0 && a == 3 && b == 3);
assert_se(parse_uid_range("4-5", &a, &b) >= 0 && a == 4 && b == 5);
ASSERT_OK(parse_uid_range("0", &a, &b));
ASSERT_EQ(a, 0u);
ASSERT_EQ(b, 0u);
assert_se(parse_uid_range("7-6", &a, &b) == -EINVAL && a == 4 && b == 5);
assert_se(parse_uid_range("-1", &a, &b) == -EINVAL && a == 4 && b == 5);
assert_se(parse_uid_range("01", &a, &b) == -EINVAL && a == 4 && b == 5);
assert_se(parse_uid_range("001", &a, &b) == -EINVAL && a == 4 && b == 5);
assert_se(parse_uid_range("+1", &a, &b) == -EINVAL && a == 4 && b == 5);
assert_se(parse_uid_range("1--1", &a, &b) == -EINVAL && a == 4 && b == 5);
assert_se(parse_uid_range(" 1", &a, &b) == -EINVAL && a == 4 && b == 5);
assert_se(parse_uid_range(" 1-2", &a, &b) == -EINVAL && a == 4 && b == 5);
assert_se(parse_uid_range("1 -2", &a, &b) == -EINVAL && a == 4 && b == 5);
assert_se(parse_uid_range("1- 2", &a, &b) == -EINVAL && a == 4 && b == 5);
assert_se(parse_uid_range("1-2 ", &a, &b) == -EINVAL && a == 4 && b == 5);
assert_se(parse_uid_range("01-2", &a, &b) == -EINVAL && a == 4 && b == 5);
assert_se(parse_uid_range("1-02", &a, &b) == -EINVAL && a == 4 && b == 5);
assert_se(parse_uid_range("001-2", &a, &b) == -EINVAL && a == 4 && b == 5);
assert_se(parse_uid_range("1-002", &a, &b) == -EINVAL && a == 4 && b == 5);
assert_se(parse_uid_range(" 01", &a, &b) == -EINVAL && a == 4 && b == 5);
ASSERT_OK(parse_uid_range("1", &a, &b));
ASSERT_EQ(a, 1u);
ASSERT_EQ(b, 1u);
ASSERT_OK(parse_uid_range("2-2", &a, &b));
ASSERT_EQ(a, 2u);
ASSERT_EQ(b, 2u);
ASSERT_OK(parse_uid_range("3-3", &a, &b));
ASSERT_EQ(a, 3u);
ASSERT_EQ(b, 3u);
ASSERT_OK(parse_uid_range("4-5", &a, &b));
ASSERT_EQ(a, 4u);
ASSERT_EQ(b, 5u);
ASSERT_ERROR(parse_uid_range("7-6", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range("-1", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range("01", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range("001", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range("+1", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range("1--1", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range(" 1", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range(" 1-2", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range("1 -2", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range("1- 2", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range("1-2 ", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range("01-2", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range("1-02", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range("001-2", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range("1-002", &a, &b), EINVAL);
ASSERT_ERROR(parse_uid_range(" 01", &a, &b), EINVAL);
}
static void test_mangle_gecos_one(const char *input, const char *expected) {
_cleanup_free_ char *p = NULL;
assert_se(p = mangle_gecos(input));
p = ASSERT_NOT_NULL(mangle_gecos(input));
ASSERT_STREQ(p, expected);
assert_se(valid_gecos(p));
ASSERT_TRUE(valid_gecos(p));
}
TEST(mangle_gecos) {

View File

@ -4,26 +4,20 @@ if conf.get('ENABLE_TMPFILES') != 1
subdir_done()
endif
systemd_tmpfiles_sources = files(
'tmpfiles.c',
)
systemd_tmpfiles_extract_sources = files(
'offline-passwd.c',
)
offline_passwd_c = files('offline-passwd.c')
executables += [
executable_template + {
'name' : 'systemd-tmpfiles',
'public' : true,
'sources' : systemd_tmpfiles_sources,
'extract' : systemd_tmpfiles_extract_sources,
'extract' : files('tmpfiles.c') +
offline_passwd_c,
'dependencies' : libacl_cflags,
},
executable_template + {
'name' : 'systemd-tmpfiles.standalone',
'public' : true,
'sources' : systemd_tmpfiles_sources + systemd_tmpfiles_extract_sources,
'c_args' : '-DSTANDALONE',
'objects' : ['systemd-tmpfiles'],
'link_with' : [
libc_wrapper_static,
libbasic_static,
@ -33,7 +27,7 @@ executables += [
'dependencies' : libacl_cflags,
},
test_template + {
'sources' : files('test-offline-passwd.c'),
'objects' : ['systemd-tmpfiles'],
'sources' : files('test-offline-passwd.c') +
offline_passwd_c,
},
]

View File

@ -4316,14 +4316,10 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_IMAGE:
#ifdef STANDALONE
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"This systemd-tmpfiles version is compiled without support for --image=.");
#else
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
if (r < 0)
return r;
#endif
/* Imply -E here since it makes little sense to create files persistently in the /run mountpoint of a disk image */
_fallthrough_;
@ -4560,10 +4556,8 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_array_hash_ops, char, string_
ItemArray, item_array_free);
static int run(int argc, char *argv[]) {
#ifndef STANDALONE
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(umount_and_freep) char *mounted_dir = NULL;
#endif
_cleanup_strv_free_ char **config_dirs = NULL;
_cleanup_(context_done) Context c = {};
bool invalid_config = false;
@ -4642,7 +4636,6 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
#ifndef STANDALONE
if (arg_image) {
assert(!arg_root);
@ -4666,9 +4659,6 @@ static int run(int argc, char *argv[]) {
if (!arg_root)
return log_oom();
}
#else
assert(!arg_image);
#endif
c.items = ordered_hashmap_new(&item_array_hash_ops);
c.globs = ordered_hashmap_new(&item_array_hash_ops);

View File

@ -507,10 +507,54 @@ NONEXISTENT_VDIR="/tmp/$VBASE-nonexistent.v"
mkdir "$VDIR" "$EMPTY_VDIR"
ln -s /tmp/app0.raw "$VDIR/${VBASE}_0.raw"
ln -s /tmp/app0.verity "$VDIR/${VBASE}_0.verity"
ln -s /tmp/app0.roothash "$VDIR/${VBASE}_0.roothash"
ln -s /tmp/app1.raw "$VDIR/${VBASE}_1.raw"
systemd-run -P -p ExtensionImages="$VDIR -$EMPTY_VDIR -$NONEXISTENT_VDIR" bash -o pipefail -c '/opt/script1.sh | grep ID'
# Check dissect shortcut for verity images
# No verity for the second img (previous test benefits from variations), remove it for the next one
rm -f "$VDIR/${VBASE}_1.raw"
cat >/run/systemd/system/testservice-50e-vpick.service <<EOF
[Service]
Type=notify
NotifyAccess=all
MountAPIVFS=yes
TemporaryFileSystem=/run /var/lib
StateDirectory=app-vpick
RootImage=$MINIMAL_IMAGE.raw
RootImagePolicy=root=squashfs
ExtensionImages=$VDIR:x-systemd.relax-extension-release-check -$EMPTY_VDIR -$NONEXISTENT_VDIR
ExtensionImagePolicy=root=squashfs
# Relevant only for sanitizer runs
UnsetEnvironment=LD_PRELOAD
ExecStartPre=bash -c '/opt/script0.sh | grep ID'
ExecStart=sh -c 'echo "READY=1" | socat -t 5 - UNIX-SENDTO:\$\$NOTIFY_SOCKET; sleep infinity'
EOF
systemctl start testservice-50e-vpick.service
systemctl is-active testservice-50e-vpick.service
# Ensure the device is preopened for reuse. Note that sd-dissect -M does not work on older
# kernels, <something> makes the devices disappear on Ubuntu 24.04/C9S, so set them
# up by hand. Don't fail if it's already set up.
veritysetup open "$MINIMAL_IMAGE.raw" "$(cat "$MINIMAL_IMAGE.roothash")-verity" "$MINIMAL_IMAGE.verity" --root-hash-file "$MINIMAL_IMAGE.roothash" ||:
veritysetup open "$VDIR/${VBASE}_0.raw" "$(cat "$VDIR/${VBASE}_0.roothash")-verity" "$VDIR/${VBASE}_0.verity" --root-hash-file "$VDIR/${VBASE}_0.roothash" ||:
mkdir -p /tmp/img /tmp/ext
mount -o ro "/dev/mapper/$(cat "$MINIMAL_IMAGE.roothash")-verity" /tmp/img
mount -o ro "/dev/mapper/$(cat "$VDIR/${VBASE}_0.roothash")-verity" /tmp/ext
journalctl --sync
since="$(date '+%H:%M:%S')"
systemctl restart testservice-50e-vpick.service
systemctl is-active testservice-50e-vpick.service
journalctl --sync
timeout -v 30 journalctl --since "$since" -n all --follow | grep -m 2 -F 'Reusing pre-existing verity-protected root image'
timeout -v 30 journalctl --since "$since" -n all --follow | grep -m 2 -F 'Reusing pre-existing verity-protected image'
systemctl stop testservice-50e-vpick.service
umount -R /tmp/img
umount -R /tmp/ext
veritysetup close "$(cat "$MINIMAL_IMAGE.roothash")-verity" ||:
veritysetup close "$(cat "$VDIR/${VBASE}_0.roothash")-verity" ||:
rm -rf "$VDIR" "$EMPTY_VDIR"
# ExtensionDirectories will set up an overlay
@ -787,6 +831,49 @@ rm -f /run/systemd/system/testservice-50k.service
systemctl daemon-reload
rm -rf "$VDIR" "$VDIR2" /tmp/vpickminimg /tmp/markers/
# Check dissect shortcut for verity images
cat >/run/systemd/system/testservice-50m.service <<EOF
[Service]
Type=notify
NotifyAccess=all
TemporaryFileSystem=/run /var/lib
StateDirectory=app0
RootImage=$MINIMAL_IMAGE.raw
RootImagePolicy=root=squashfs
ExtensionImages=/tmp/app0.raw /tmp/app1.raw
ExtensionImagePolicy=root=squashfs
MountImages=/tmp/app0.raw:/var/lib/app
MountImagePolicy=root=squashfs
# Relevant only for sanitizer runs
UnsetEnvironment=LD_PRELOAD
ExecStartPre=bash -o pipefail -c '/opt/script0.sh | grep ID'
ExecStartPre=bash -o pipefail -c '/opt/script1.sh | grep ID'
ExecStartPre=test -e "/dev/mapper/$(</tmp/app0.roothash)-verity"
ExecStart=sh -c 'echo "READY=1" | socat -t 5 - UNIX-SENDTO:\$\$NOTIFY_SOCKET; sleep infinity'
EOF
systemctl start testservice-50m.service
systemctl is-active testservice-50m.service
# Ensure the device is preopened for reuse. Note that sd-dissect -M does not work on older
# kernels, <something> makes the devices disappear on Ubuntu 24.04/C9S, so set them
# up by hand. Don't fail if it's already set up.
veritysetup open "$MINIMAL_IMAGE.raw" "$(cat "$MINIMAL_IMAGE.roothash")-verity" "$MINIMAL_IMAGE.verity" --root-hash-file "$MINIMAL_IMAGE.roothash" ||:
veritysetup open /tmp/app0.raw "$(cat /tmp/app0.roothash)-verity" /tmp/app0.verity --root-hash-file /tmp/app0.roothash ||:
mkdir -p /tmp/img /tmp/ext
mount -o ro "/dev/mapper/$(cat "$MINIMAL_IMAGE.roothash")-verity" /tmp/img
mount -o ro "/dev/mapper/$(cat /tmp/app0.roothash)-verity" /tmp/ext
journalctl --sync
since="$(date '+%H:%M:%S')"
systemctl restart testservice-50m.service
systemctl is-active testservice-50m.service
journalctl --sync
timeout -v 30 journalctl --since "$since" -n all --follow | grep -m 4 -F 'Reusing pre-existing verity-protected root image'
timeout -v 30 journalctl --since "$since" -n all --follow | grep -m 8 -F 'Reusing pre-existing verity-protected image'
systemctl stop testservice-50m.service
umount /tmp/img
umount /tmp/ext
veritysetup close "$(cat "$MINIMAL_IMAGE.roothash")-verity" ||:
veritysetup close "$(cat /tmp/app0.roothash)-verity" ||:
# Test that an extension consisting of an empty directory under /etc/extensions/ takes precedence
mkdir -p /var/lib/extensions/
ln -s /tmp/app-nodistro.raw /var/lib/extensions/app-nodistro.raw

View File

@ -35,7 +35,7 @@ def parse_args():
return p.parse_args()
def read_config(distro: str):
cmd = ['mkosi', '--json', '-d', distro, 'summary']
cmd = ['mkosi', '--json', '-d', distro, '-f', 'summary']
if args.profile:
cmd += ['--profile', args.profile]
print(f"+ {shlex.join(cmd)}")