1
0
mirror of https://github.com/systemd/systemd synced 2026-03-16 18:14:46 +01:00

Compare commits

...

39 Commits

Author SHA1 Message Date
safforddr
331fef07d8 tpm2: allow use of recoverable sealing keys
In some use cases it is desirable to use a recoverable (ie duplicatable)
sealing key. Currently objects have the attribute TPMA_OBJECT_FIXEDTPM
and TPMA_OBJECT_FIXEDPARENT hard coded, which will not work with a
recoverable sealing key. This patch sets the object's attributes from
the sealing key's attributes, so that both types of sealing keys will work.
2026-01-19 22:00:45 +01:00
Lennart Poettering
9316e02c9d
Introduce 'fixate-volume-key' option to repart/cryptsetup to pin the exact LUKS volume key hash (#40343)
Add an option to generate the expected volume key hash for LUKS volumes
by systemd-repart
and put it to crypttab, make systemd-cryptsetup check it upon attaching.
The format of the hash
matches what's currently being measured to TPM2 PCR with
tpm2-measure-pcr=.

Closes #40123
2026-01-19 21:59:47 +01:00
Lennart Poettering
3a0ddbe338
core: follow-ups for changes to MountOptions (#40395)
Follow-up for #39449
2026-01-19 21:59:19 +01:00
Lennart Poettering
799392286e sd-varlink: ensure that "any" actually means "any but null"
The new "any" type was implemented by accident that it actually meant
"any but null" – unless marked as "any?" in which case it actually meant
truly any, including null. The spec change in
https://github.com/varlink/varlink.github.io/pull/43 otoh suggested that
"any" really means anything, and "any?" apparently too.

I think the implementation in code makes more sense than the spec change
however, hence let's add some checks/tests to ensure the behaviour of
the code is made explicitly and cared for.

I will prep a spec change to make the spec follow the code on this too.

Follow-up for: #39918
2026-01-19 21:29:38 +01:00
Yu Watanabe
8a5fb3627a vmspawn: use indexed loop
Previously, the index is obtained from the pointer offset. The
pointer offset is expressed by ptrdiff_t and may be different from
ssize_t.

Let's avoid to use FOREACH_ARRAY() but use an indexed loop.
This also renames `mount` to `m` to avoid conflict with `mount()`.

Fixes #40380.
2026-01-20 02:26:35 +09:00
Vitaly Kuznetsov
398acccb2a TEST-58-REPART: Add a test for fixate_folume_key
The test checks that the expected hash is correctly recorded to the
generated crypttab and also checks that systemd-cryptsetup handles
the option correctly.
2026-01-19 17:50:25 +01:00
Vitaly Kuznetsov
4754289ddf repart: Add fixate_volume_key crypttab option generation
Add an option to capture the expected LUKS volume key hash and
record it to the generated crypttab.
2026-01-19 17:50:25 +01:00
Vitaly Kuznetsov
69fab29ec4 cryptsetup: Add fixate-volume-key option to pin the expected volume key hash
The expected hash (SHA265 HMAC signature) uses the exact same algorithm which
is used to calculate sha256 PCR bank digest when 'tpm2-measure-pcr=' is used.
2026-01-19 17:49:39 +01:00
Mike Yuan
e1388041c0
core/dbus-util: several cleanups for bus_read_mount_options()
* Make sure ret_options is initialized on success.
* Return empty mount options as-is rather than NULL-ing it -
  dbus property parser for RootImageOptions relies on it
  for resetting options for a specific partition designator.
* Format partition:options properly with strextendf, currently
  multiple ":" will be emitted.
* Allow separator to be unset if in_out_format_str is not
  needed.
2026-01-19 17:49:29 +01:00
Mike Yuan
079be1e71b
core/varlink-execute: skip empty mount options
This keeps things in line with bus_append_mount_options().
2026-01-19 17:35:38 +01:00
Mike Yuan
831eb5453a
core/dbus-execute: constify MountOptions 2026-01-19 17:35:37 +01:00
Mike Yuan
bdfa039404
dissect-image: use consistent log level 2026-01-19 17:35:37 +01:00
Yu Watanabe
3d2284c7db journal/audit: do not use strjoina() for arbitrary length of received string
Otherwise, when a long message is received, the assertion in strjoina()
may be triggered.

Fixes oss-fuzz#476768320 (https://issues.oss-fuzz.com/issues/476768320).
Fixes #40388.
2026-01-20 01:29:32 +09:00
Luca Boccassi
2ba15493af
portable: pin attached image via image-policy (#40152)
When attaching images generate a policy in the portable drop-in that
matches the partition types and content found while dissecting, so that
it can no longer be changed later without a reattach
2026-01-19 16:24:44 +00:00
Yu Watanabe
1580907953
audit-type: include alloc-util.h for newa() (#40389)
The macro audit_type_name_alloca() needs newa() which is defined in
alloc-util.h.
2026-01-19 16:31:39 +01:00
Yu Watanabe
b7dd507cae tree-wide: use consistent format of short comment for constant argument
Follow-ups for recent changes after 93378148d48fa700c7f53b5f8969787af2a47ccb.
2026-01-20 00:22:15 +09:00
Luca Boccassi
d059615492 portable: pin attached image via image-policy
When attaching images generate a policy in the portable drop-in
that matches the partition types and content found while dissecting,
so that it can no longer be changed later without a reattach.
2026-01-19 15:54:12 +01:00
Luca Boccassi
2258c93270 image-policy: add helper to create policy from dissected image
Pin policies to exactly what was found while dissecting
2026-01-19 15:47:15 +01:00
Luca Boccassi
f639fb4b77 image-policy: add image_policy_union helper
Similar to image_policy_intersect but is the union of all used policies
instead
2026-01-19 15:47:15 +01:00
Yu Watanabe
67040ea2bc
More legacy kernel support removal; follow-ups for XAT_FDROOT (#40384) 2026-01-19 23:10:17 +09:00
Vitaly Kuznetsov
f99f843f24 tpm2-util: make tpm2_pcr_extend_bytes() declaration match its implementation
tpm2_pcr_extend_bytes() has differences in parameter names between the
declaration and the implementation, in particular, 'event' in the header is
named 'event_type' in the implementation.
2026-01-19 13:52:33 +01:00
Mike Yuan
1a0df07843
core/cgroup: obsolete DefaultMemoryMin/Low=
Now that our kernel baseline is v5.7 and memory_recursiveprot
is universally enabled, remove userspace workarounds.
2026-01-19 13:33:28 +01:00
Mike Yuan
d57fdbb5e2
core/cgroup: do not consider cgroup_context_has_io_config() if only IOAccounting=yes
This makes things in line with unit_has_memory_config(),
and suppresses pointless application of IO configs.
2026-01-19 13:33:28 +01:00
Mike Yuan
8292b859f3
core/cgroup: drop trusted.survive_final_kill_signal fallback for kernel < 5.7 2026-01-19 13:33:28 +01:00
Mike Yuan
6fd7c5ecdf
core/namespace: remove availability checks for procfs hidepid=/subset= opts 2026-01-19 13:33:27 +01:00
Mike Yuan
5f90e4e4bc
stat-util: O_NOCTTY is nop with O_PATH 2026-01-19 13:33:27 +01:00
Mike Yuan
b0906b1216
stat-util: do not use RET_NERRNO for our own function
Follow-up for 95abe4beff08636681069623ffdf16cfa34c7cf8
2026-01-19 13:33:27 +01:00
Mike Yuan
02bf2b7cdf
fs-util: optimize xopenat(XAT_FDROOT, ...) a bit
Follow-up for ef582ab201c2942813960ad3c451df3642d85558

* Open / with O_PATH
* Shortcut to fd_reopen() directly if path is empty
2026-01-19 13:33:27 +01:00
Mike Yuan
9b906ae48e
fs-util: group access_fd() with access_nofollow() 2026-01-19 13:33:27 +01:00
Mike Yuan
52bf232e82
fs-util: drop fallback for kernels without AT_EMPTY_PATH support (< 5.8) 2026-01-19 13:33:26 +01:00
Mike Yuan
6753bd8a2f
mountpoint-util: assume MS_NOSYMFOLLOW is available
Since our kernel baseline is v5.10 now.
2026-01-19 13:33:26 +01:00
Mike Yuan
58fb46ecd5
firewall-util: drop fallback for kernel < 5.8 2026-01-19 12:51:56 +01:00
Mike Yuan
42b09813f4
netlink-util: remove support for kernel < 5.5 2026-01-19 12:51:56 +01:00
Mike Yuan
742308caba
nspawn: drop compat glue for kernel < 3.14 2026-01-19 12:51:55 +01:00
Mike Yuan
254445e927
README: kcmp() is unnecessary if we have F_DUPFD_QUERY (v6.10) 2026-01-19 12:51:55 +01:00
Mike Yuan
77b4a411ac
README: CONFIG_DEVPTS_MULTIPLE_INSTANCES has been dropped in v4.7 2026-01-19 12:51:55 +01:00
Vitaly Kuznetsov
15b1c334df repart: Generate fstab and crypttab late
The immediate need for the change is to allow to capture the expected
LUKS volume key hash and record it to the generated crypttab but it
also seems to make sense to not generate crypttab/fstab before we know
that partition creation succeeded as fstab/crypttab entries are bogus
otherwise.
2026-01-19 11:29:00 +01:00
Mike Yuan
a3507f6628
mkosi/opensuse: remove resolved TODO
Follow-up for ff9c4ace709a1274b8f7e2569161da559c43fbd5
2026-01-19 03:06:30 +01:00
Mike Yuan
537045ec2f
recurse-dir: remove stale comment
Follow-up for 6393b847f459dba14d2b615ee93babb143168b57
2026-01-19 03:06:30 +01:00
75 changed files with 1148 additions and 959 deletions

23
README
View File

@ -50,9 +50,10 @@ REQUIREMENTS:
≥ 5.6 for getrandom() GRND_INSECURE
≥ 5.7 for CLONE_INTO_CGROUP, cgroup2fs memory_recursiveprot option,
BPF links and the BPF LSM hook
≥ 5.8 for LOOP_CONFIGURE and STATX_ATTR_MOUNT_ROOT
≥ 5.8 for LOOP_CONFIGURE, STATX_ATTR_MOUNT_ROOT, and procfs
per-instance hidepid=/subset= options
≥ 5.9 for close_range()
≥ 5.10 for STATX_MNT_ID
≥ 5.10 for STATX_MNT_ID and MS_NOSYMFOLLOW mount option
⛔ Kernel versions below 5.10 ("minimum baseline") are not supported at all,
and are missing required functionality as listed above.
@ -124,7 +125,8 @@ REQUIREMENTS:
CONFIG_SECCOMP
CONFIG_SECCOMP_FILTER (required for seccomp support)
CONFIG_KCMP (for the kcmp() syscall, used to be under
CONFIG_CHECKPOINT_RESTORE before ~5.12)
CONFIG_CHECKPOINT_RESTORE before ~5.12;
not needed after 6.10)
CONFIG_NET_SCHED
CONFIG_NET_SCH_FQ_CODEL
@ -192,25 +194,12 @@ REQUIREMENTS:
This is shipped by default, see modprobe.d/systemd.conf.
Required for systemd-nspawn:
CONFIG_DEVPTS_MULTIPLE_INSTANCES or Linux kernel >= 4.7
CONFIG_DEVPTS_MULTIPLE_INSTANCES (removed and unneeded since 4.7)
Required for systemd-oomd:
CONFIG_PSI
CONFIG_MEMCG
Note that kernel auditing is broken when used with systemd's container
code. When using systemd in conjunction with containers, please make
sure to either turn off auditing at runtime using the kernel command
line option "audit=0", or turn it off at kernel compile time using:
CONFIG_AUDIT=n
If systemd is compiled with libseccomp support on architectures which do
not use socketcall() and where seccomp is supported (this effectively
means x86-64 and ARM, but excludes 32-bit x86!), then nspawn will now
install a work-around seccomp filter that makes containers boot even
with audit being enabled. This works correctly only on kernels 3.14 and
newer though. TL;DR: turn audit off, still.
glibc >= 2.31
libxcrypt >= 4.4.0 (optional)
libmount >= 2.30 (from util-linux)

6
TODO
View File

@ -107,8 +107,6 @@ Deprecations and removals:
* drop support for LOOP_CONFIGURE-less loopback block devices, once kernel
baseline is 5.8.
* Once baseline is 5.10, remove support or MS_NOSYMFOLLOW-less kernels
* Remove /dev/mem ACPI FPDT parsing when /sys/firmware/acpi/fpdt is ubiquitous.
That requires distros to enable CONFIG_ACPI_FPDT, and have kernels v5.12 for
x86 and v6.2 for arm.
@ -122,10 +120,6 @@ Deprecations and removals:
https://github.com/util-linux/util-linux/commit/508fb0e7ac103b68531a59db2a4473897853ab52
has hit the prominent distributions, revert --issue-file= hack in units/*getty*service.in
* Once kernel baseline is 5.7, obsolete DefaultMemoryMin/Low= and DefaultStartupMemoryLow=
since we can systematically enable memory_recursiveprot on cgroupfs and have those
enforced by the kernel
Features:
* systemd-sysupdate: add support a "best before" in manifests (ie. SHA256SUMS)

View File

@ -1012,6 +1012,26 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>fixate-volume-key=</option></term>
<listitem><para>Pin the expected hash of the volume key.</para>
<para>In certain cases, e.g. for LUKS volumes where the key is sealed to the TPM2, this may be required
to provide a guarantee that the volume being attached is the volume which was previously created.
<option>fixate-volume-key=</option> can be used to set the expected volume key hash and refuse to attach the volume
if it has a different one. The expected hash matches the digest which is measured to the sha256 PCR bank of the
TPM2 when <option>tpm2-measure-pcr=</option> is used.</para>
<para>For newly created LUKS volumes, the expected hash can be generated by
<citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
For additional details, see the <option>EncryptedVolume=</option> description at
<citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
<xi:include href="version-info.xml" xpointer="v260"/>
</listitem>
</varlistentry>
</variablelist>
<para>At early boot and when the system manager configuration is
@ -1121,6 +1141,8 @@ external /dev/sda3 keyfile:LABEL=keydev keyfile-timeout=10s,cipher=xchac
<member><citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry project='die-net'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry project='man-pages'><refentrytitle>mkswap</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>

View File

@ -2969,12 +2969,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly b MemoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultMemoryLow = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultStartupMemoryLow = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultMemoryMin = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryMin = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryLow = ...;
@ -3642,12 +3636,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<!--property MemoryAccounting is not documented!-->
<!--property DefaultMemoryLow is not documented!-->
<!--property DefaultStartupMemoryLow is not documented!-->
<!--property DefaultMemoryMin is not documented!-->
<!--property MemoryMin is not documented!-->
<!--property MemoryLow is not documented!-->
@ -4336,12 +4324,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryLow"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultStartupMemoryLow"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryMin"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryMin"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryLow"/>
@ -5238,12 +5220,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly b MemoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultMemoryLow = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultStartupMemoryLow = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultMemoryMin = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryMin = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryLow = ...;
@ -5929,12 +5905,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<!--property MemoryAccounting is not documented!-->
<!--property DefaultMemoryLow is not documented!-->
<!--property DefaultStartupMemoryLow is not documented!-->
<!--property DefaultMemoryMin is not documented!-->
<!--property MemoryMin is not documented!-->
<!--property MemoryLow is not documented!-->
@ -6599,12 +6569,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryLow"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultStartupMemoryLow"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryMin"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryMin"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryLow"/>
@ -7325,12 +7289,6 @@ node /org/freedesktop/systemd1/unit/home_2emount {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly b MemoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultMemoryLow = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultStartupMemoryLow = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultMemoryMin = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryMin = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryLow = ...;
@ -7940,12 +7898,6 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<!--property MemoryAccounting is not documented!-->
<!--property DefaultMemoryLow is not documented!-->
<!--property DefaultStartupMemoryLow is not documented!-->
<!--property DefaultMemoryMin is not documented!-->
<!--property MemoryMin is not documented!-->
<!--property MemoryLow is not documented!-->
@ -8518,12 +8470,6 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryLow"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultStartupMemoryLow"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryMin"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryMin"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryLow"/>
@ -9377,12 +9323,6 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly b MemoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultMemoryLow = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultStartupMemoryLow = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultMemoryMin = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryMin = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryLow = ...;
@ -9974,12 +9914,6 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<!--property MemoryAccounting is not documented!-->
<!--property DefaultMemoryLow is not documented!-->
<!--property DefaultStartupMemoryLow is not documented!-->
<!--property DefaultMemoryMin is not documented!-->
<!--property MemoryMin is not documented!-->
<!--property MemoryLow is not documented!-->
@ -10534,12 +10468,6 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryLow"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultStartupMemoryLow"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryMin"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryMin"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryLow"/>
@ -11246,12 +11174,6 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly b MemoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultMemoryLow = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultStartupMemoryLow = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultMemoryMin = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryMin = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryLow = ...;
@ -11427,12 +11349,6 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
<!--property MemoryAccounting is not documented!-->
<!--property DefaultMemoryLow is not documented!-->
<!--property DefaultStartupMemoryLow is not documented!-->
<!--property DefaultMemoryMin is not documented!-->
<!--property MemoryMin is not documented!-->
<!--property MemoryLow is not documented!-->
@ -11619,12 +11535,6 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryLow"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultStartupMemoryLow"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryMin"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryMin"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryLow"/>
@ -11836,12 +11746,6 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly b MemoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultMemoryLow = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultStartupMemoryLow = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultMemoryMin = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryMin = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryLow = ...;
@ -12031,12 +11935,6 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
<!--property MemoryAccounting is not documented!-->
<!--property DefaultMemoryLow is not documented!-->
<!--property DefaultStartupMemoryLow is not documented!-->
<!--property DefaultMemoryMin is not documented!-->
<!--property MemoryMin is not documented!-->
<!--property MemoryLow is not documented!-->
@ -12247,12 +12145,6 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryLow"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultStartupMemoryLow"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryMin"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryMin"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryLow"/>

View File

@ -141,6 +141,11 @@
immediately started (blocking operation unless <option>--no-block</option> is passed) and/or enabled after
attaching the image.</para>
<para>When images are attached, an image policy will be generated that pins the attached image by
the exact content that was found while attaching, so that it cannot be swapped downgrading security
(e.g.: removing dm-verity protection) without a full reinstallation. For more details on policies, see
<citerefentry><refentrytitle>systemd.image-policy</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
<xi:include href="vpick.xml" xpointer="image"/>
<xi:include href="vpick.xml" xpointer="directory"/>
<xi:include href="version-info.xml" xpointer="v239"/>

View File

@ -869,13 +869,18 @@
<term><varname>EncryptedVolume=</varname></term>
<listitem><para>Specifies how the encrypted partition should be set up. Takes at least one and at most
three fields separated with a colon (<literal>:</literal>). The first field specifies the encrypted
four fields separated with a colon (<literal>:</literal>). The first field specifies the encrypted
volume name under <filename>/dev/mapper/</filename>. If not specified, <literal>luks-UUID</literal>
will be used where <literal>UUID</literal> is the LUKS UUID. The second field specifies the keyfile
to use following the same format as specified in crypttab. The third field specifies a
comma-delimited list of crypttab options. These fields correspond to the first, third and fourth
comma-delimited list of crypttab options. These three fields correspond to the first, third and fourth
column of the
<citerefentry><refentrytitle>crypttab</refentrytitle><manvolnum>5</manvolnum></citerefentry> format.
The fourth field contains extra options, which are not directly reflected to crypttab options. Currently,
the only supported extra option is <varname>fixate-volume-key</varname>. When specified, the volume key
hash will be taken upon LUKS volume formatting and the result will be added to the list of crypttab options,
i.e. <varname>fixate-volume-key=expected-hash</varname> will be added to the fourth column of the generated
crypttab.
</para>
<para>Note that this setting is only taken into account when <option>--generate-crypttab=</option>

View File

@ -326,7 +326,7 @@ CPUWeight=20 DisableControllers=cpu / \
<varlistentry>
<term><varname>MemoryMin=<replaceable>bytes</replaceable></varname>, <varname>MemoryLow=<replaceable>bytes</replaceable></varname></term>
<term><varname>StartupMemoryLow=<replaceable>bytes</replaceable></varname>, <varname>DefaultStartupMemoryLow=<replaceable>bytes</replaceable></varname></term>
<term><varname>StartupMemoryLow=<replaceable>bytes</replaceable></varname></term>
<listitem>
<para>These settings control the <option>memory</option> controller in the unified hierarchy.</para>
@ -353,16 +353,6 @@ CPUWeight=20 DisableControllers=cpu / \
For details about this control group attribute, see <ulink
url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
<para>Units may have their children use a default <literal>memory.min</literal> or
<literal>memory.low</literal> value by specifying <varname>DefaultMemoryMin=</varname> or
<varname>DefaultMemoryLow=</varname>, which has the same semantics as
<varname>MemoryMin=</varname> and <varname>MemoryLow=</varname>, or <varname>DefaultStartupMemoryLow=</varname>
which has the same semantics as <varname>StartupMemoryLow=</varname>.
This setting does not affect <literal>memory.min</literal> or <literal>memory.low</literal>
in the unit itself.
Using it to set a default child allocation is only useful on kernels older than 5.7,
which do not support the <literal>memory_recursiveprot</literal> cgroup2 mount option.</para>
<para>While <varname>StartupMemoryLow=</varname> applies to the startup and shutdown phases of the system,
<varname>MemoryMin=</varname> applies to normal runtime of the system, and if the former is not set also to
the startup and shutdown phases. Using <varname>StartupMemoryLow=</varname> allows prioritizing specific services at

View File

@ -18,9 +18,6 @@ TS="${SOURCE_DATE_EPOCH:-$(date +%s)}"
# The openSUSE filelists hardcode the manpage compression extension. This causes rpmbuild errors since we
# disable manpage compression as the files cannot be found. Fix the issue by removing the compression
# extension.
#
# TODO: remove patches for removal of cgroups-agent, renaming pubring file, and removal of sysv files
# when the upstream spec is updated
while read -r filelist; do
sed -E \
-e 's/\.gz$//' \

View File

@ -321,22 +321,7 @@ int futimens_opath(int fd, const struct timespec ts[2]) {
assert(fd >= 0);
if (utimensat(fd, "", ts, AT_EMPTY_PATH) >= 0)
return 0;
if (errno != EINVAL)
return -errno;
/* Support for AT_EMPTY_PATH is added rather late (kernel 5.8), so fall back to going through /proc/
* if unavailable. */
if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, /* flags= */ 0) < 0) {
if (errno != ENOENT)
return -errno;
return proc_fd_enoent_errno();
}
return 0;
return RET_NERRNO(utimensat(fd, "", ts, AT_EMPTY_PATH));
}
int stat_warn_permissions(const char *path, const struct stat *st) {
@ -375,6 +360,19 @@ int access_nofollow(const char *path, int mode) {
return RET_NERRNO(faccessat(AT_FDCWD, path, mode, AT_SYMLINK_NOFOLLOW));
}
int access_fd(int fd, int mode) {
/* Like access() but operates on an already open fd */
if (fd == AT_FDCWD)
return RET_NERRNO(access(".", mode));
if (fd == XAT_FDROOT)
return RET_NERRNO(access("/", mode));
assert(fd >= 0);
return RET_NERRNO(faccessat(fd, "", mode, AT_EMPTY_PATH));
}
int touch_fd(int fd, usec_t stamp) {
assert(fd >= 0);
@ -712,34 +710,6 @@ char* unlink_and_free(char *p) {
return mfree(p);
}
int access_fd(int fd, int mode) {
/* Like access() but operates on an already open fd */
if (fd == AT_FDCWD)
return RET_NERRNO(access(".", mode));
if (fd == XAT_FDROOT)
return RET_NERRNO(access("/", mode));
assert(fd >= 0);
if (faccessat(fd, "", mode, AT_EMPTY_PATH) >= 0)
return 0;
if (errno != EINVAL)
return -errno;
/* Support for AT_EMPTY_PATH is added rather late (kernel 5.8), so fall back to going through /proc/
* if unavailable. */
if (access(FORMAT_PROC_FD_PATH(fd), mode) < 0) {
if (errno != ENOENT)
return -errno;
return proc_fd_enoent_errno();
}
return 0;
}
int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
_cleanup_close_ int truncate_fd = -EBADF;
struct stat st;
@ -1188,19 +1158,6 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
* The dir fd can be passed as XAT_FDROOT, in which case any relative paths will be taken relative to the root fs.
*/
_cleanup_close_ int _dir_fd = -EBADF;
if (dir_fd == XAT_FDROOT) {
if (path_is_absolute(path))
dir_fd = AT_FDCWD;
else {
_dir_fd = open("/", O_CLOEXEC|O_DIRECTORY|O_RDONLY);
if (_dir_fd < 0)
return -errno;
dir_fd = _dir_fd;
}
}
if (mode == MODE_INVALID)
mode = (open_flags & O_DIRECTORY) ? 0755 : 0644;
@ -1216,6 +1173,19 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
return fd_reopen(dir_fd, open_flags & ~O_NOFOLLOW);
}
_cleanup_close_ int _dir_fd = -EBADF;
if (dir_fd == XAT_FDROOT) {
if (path_is_absolute(path))
dir_fd = AT_FDCWD;
else {
_dir_fd = open("/", O_CLOEXEC|O_DIRECTORY|O_PATH);
if (_dir_fd < 0)
return -errno;
dir_fd = _dir_fd;
}
}
bool call_label_ops_post = false;
if (FLAGS_SET(open_flags, O_CREAT) && FLAGS_SET(xopen_flags, XO_LABEL)) {

View File

@ -42,6 +42,7 @@ int fd_warn_permissions(const char *path, int fd);
int stat_warn_permissions(const char *path, const struct stat *st);
int access_nofollow(const char *path, int mode);
int access_fd(int fd, int mode);
int touch_fd(int fd, usec_t stamp);
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode);
@ -86,8 +87,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free);
char* unlink_and_free(char *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
int access_fd(int fd, int mode);
typedef enum UnlinkDeallocateFlags {
UNLINK_REMOVEDIR = 1 << 0,
UNLINK_ERASE = 1 << 1,

View File

@ -529,66 +529,6 @@ bool mount_new_api_supported(void) {
return (cache = true);
}
unsigned long ms_nosymfollow_supported(void) {
_cleanup_close_ int fsfd = -EBADF, mntfd = -EBADF;
static int cache = -1;
/* Returns MS_NOSYMFOLLOW if it is supported, zero otherwise. */
if (cache >= 0)
return cache ? MS_NOSYMFOLLOW : 0;
if (!mount_new_api_supported())
goto not_supported;
/* Checks if MS_NOSYMFOLLOW is supported (which was added in 5.10). We use the new mount API's
* mount_setattr() call for that, which was added in 5.12, which is close enough. */
fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
if (fsfd < 0) {
if (ERRNO_IS_NOT_SUPPORTED(errno))
goto not_supported;
log_debug_errno(errno, "Failed to open superblock context for tmpfs: %m");
return 0;
}
if (fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) {
if (ERRNO_IS_NOT_SUPPORTED(errno))
goto not_supported;
log_debug_errno(errno, "Failed to create tmpfs superblock: %m");
return 0;
}
mntfd = fsmount(fsfd, FSMOUNT_CLOEXEC, 0);
if (mntfd < 0) {
if (ERRNO_IS_NOT_SUPPORTED(errno))
goto not_supported;
log_debug_errno(errno, "Failed to turn superblock fd into mount fd: %m");
return 0;
}
if (mount_setattr(mntfd, "", AT_EMPTY_PATH|AT_RECURSIVE,
&(struct mount_attr) {
.attr_set = MOUNT_ATTR_NOSYMFOLLOW,
}, sizeof(struct mount_attr)) < 0) {
if (ERRNO_IS_NOT_SUPPORTED(errno))
goto not_supported;
log_debug_errno(errno, "Failed to set MOUNT_ATTR_NOSYMFOLLOW mount attribute: %m");
return 0;
}
cache = true;
return MS_NOSYMFOLLOW;
not_supported:
cache = false;
return 0;
}
int mount_option_supported(const char *fstype, const char *key, const char *value) {
_cleanup_close_ int fd = -EBADF;
int r;

View File

@ -75,7 +75,6 @@ int mount_propagation_flag_from_string(const char *name, unsigned long *ret);
bool mount_propagation_flag_is_valid(unsigned long flag);
bool mount_new_api_supported(void);
unsigned long ms_nosymfollow_supported(void);
int mount_option_supported(const char *fstype, const char *key, const char *value);

View File

@ -1804,7 +1804,7 @@ int namespace_fork_full(
r = pidref_safe_fork_full(
outer_name,
/* stdio_fds = */ NULL, /* except_fds = */ NULL, /* n_except_fds = */ 0,
/* stdio_fds= */ NULL, /* except_fds= */ NULL, /* n_except_fds= */ 0,
(flags|FORK_DEATHSIG_SIGKILL) & ~(FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE|FORK_NEW_USERNS|FORK_NEW_NETNS|FORK_NEW_PIDNS|FORK_CLOSE_ALL_FDS|FORK_PACK_FDS|FORK_CLOEXEC_OFF|FORK_RLIMIT_NOFILE_SAFE),
&pidref_outer);
if (r == -EPROTO && FLAGS_SET(flags, FORK_WAIT)) {

View File

@ -36,8 +36,7 @@ int readdir_all(int dir_fd, RecurseDirFlags flags, DirectoryEntries **ret) {
assert(dir_fd >= 0);
/* Returns an array with pointers to "struct dirent" directory entries, optionally sorted. Free the
* array with readdir_all_freep().
/* Returns an array with pointers to "struct dirent" directory entries, optionally sorted.
*
* Start with space for up to 8 directory entries. We expect at least 2 ("." + ".."), hence hopefully
* 8 will cover most cases comprehensively. (Note that most likely a lot more entries will actually

View File

@ -257,6 +257,22 @@ static int xfstatfs(int fd, struct statfs *ret) {
return RET_NERRNO(fstatfs(fd, ret));
}
int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
_cleanup_close_ int fd = -EBADF;
assert(dir_fd >= 0 || IN_SET(dir_fd, AT_FDCWD, XAT_FDROOT));
assert(ret);
if (!isempty(path)) {
fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC);
if (fd < 0)
return fd;
dir_fd = fd;
}
return xfstatfs(dir_fd, ret);
}
int fd_is_read_only_fs(int fd) {
int r;
@ -522,22 +538,6 @@ bool statx_mount_same(const struct statx *a, const struct statx *b) {
return a->stx_mnt_id == b->stx_mnt_id;
}
int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
_cleanup_close_ int fd = -EBADF;
assert(dir_fd >= 0 || IN_SET(dir_fd, AT_FDCWD, XAT_FDROOT));
assert(ret);
if (!isempty(path)) {
fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return fd;
dir_fd = fd;
}
return RET_NERRNO(xfstatfs(dir_fd, ret));
}
usec_t statx_timestamp_load(const struct statx_timestamp *ts) {
return timespec_load(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
}

View File

@ -65,7 +65,7 @@ static int bind_network_interface_install_impl(Unit *u, CGroupRuntime *crt) {
if (isempty(cc->bind_network_interface))
return 0;
r = cg_get_path(crt->cgroup_path, /* suffix = */ NULL, &cgroup_path);
r = cg_get_path(crt->cgroup_path, /* suffix= */ NULL, &cgroup_path);
if (r < 0)
return log_unit_error_errno(u, r, "bind-interface: Failed to get cgroup path: %m");

View File

@ -330,13 +330,13 @@ static int unit_compare_memory_limit(Unit *u, const char *property_name, uint64_
bool startup = u->manager && IN_SET(manager_state(u->manager), MANAGER_STARTING, MANAGER_INITIALIZING, MANAGER_STOPPING);
if (streq(property_name, "MemoryLow")) {
unit_value = unit_get_ancestor_memory_low(u);
unit_value = c->memory_low;
file = "memory.low";
} else if (startup && streq(property_name, "StartupMemoryLow")) {
unit_value = unit_get_ancestor_startup_memory_low(u);
unit_value = c->startup_memory_low;
file = "memory.low";
} else if (streq(property_name, "MemoryMin")) {
unit_value = unit_get_ancestor_memory_min(u);
unit_value = c->memory_min;
file = "memory.min";
} else if (streq(property_name, "MemoryHigh")) {
unit_value = c->memory_high;
@ -504,8 +504,6 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
"%sStartupAllowedMemoryNodes: %s\n"
"%sIOWeight: %" PRIu64 "\n"
"%sStartupIOWeight: %" PRIu64 "\n"
"%sDefaultMemoryMin: %" PRIu64 "\n"
"%sDefaultMemoryLow: %" PRIu64 "\n"
"%sMemoryMin: %" PRIu64 "%s\n"
"%sMemoryLow: %" PRIu64 "%s\n"
"%sStartupMemoryLow: %" PRIu64 "%s\n"
@ -542,8 +540,6 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
prefix, strempty(startup_cpuset_mems),
prefix, c->io_weight,
prefix, c->startup_io_weight,
prefix, c->default_memory_min,
prefix, c->default_memory_low,
prefix, c->memory_min, format_cgroup_memory_limit_comparison(u, "MemoryMin", cda, sizeof(cda)),
prefix, c->memory_low, format_cgroup_memory_limit_comparison(u, "MemoryLow", cdb, sizeof(cdb)),
prefix, c->startup_memory_low, format_cgroup_memory_limit_comparison(u, "StartupMemoryLow", cdc, sizeof(cdc)),
@ -764,36 +760,6 @@ int cgroup_context_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_typ
return 0;
}
#define UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(entry) \
uint64_t unit_get_ancestor_##entry(Unit *u) { \
CGroupContext *c; \
\
/* 1. Is entry set in this unit? If so, use that. \
* 2. Is the default for this entry set in any \
* ancestor? If so, use that. \
* 3. Otherwise, return CGROUP_LIMIT_MIN. */ \
\
assert(u); \
\
c = unit_get_cgroup_context(u); \
if (c && c->entry##_set) \
return c->entry; \
\
while ((u = UNIT_GET_SLICE(u))) { \
c = unit_get_cgroup_context(u); \
if (c && c->default_##entry##_set) \
return c->default_##entry; \
} \
\
/* We've reached the root, but nobody had default for \
* this entry set, so set it to the kernel default. */ \
return CGROUP_LIMIT_MIN; \
}
UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(memory_low);
UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(startup_memory_low);
UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(memory_min);
static void unit_set_xattr_graceful(Unit *u, const char *name, const void *data, size_t size) {
int r;
@ -944,39 +910,12 @@ static void cgroup_delegate_xattr_apply(Unit *u) {
}
static void cgroup_survive_xattr_apply(Unit *u) {
int r;
assert(u);
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (!crt)
return;
if (u->survive_final_kill_signal) {
r = cg_set_xattr(
crt->cgroup_path,
"user.survive_final_kill_signal",
"1",
1,
/* flags= */ 0);
/* user xattr support was added in kernel v5.7 */
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
r = cg_set_xattr(
crt->cgroup_path,
"trusted.survive_final_kill_signal",
"1",
1,
/* flags= */ 0);
if (r < 0)
log_unit_debug_errno(u,
r,
"Failed to set 'survive_final_kill_signal' xattr on control "
"group %s, ignoring: %m",
empty_to_root(crt->cgroup_path));
} else {
if (u->survive_final_kill_signal)
unit_set_xattr_graceful(u, "user.survive_final_kill_signal", "1", 1);
else
unit_remove_xattr_graceful(u, "user.survive_final_kill_signal");
unit_remove_xattr_graceful(u, "trusted.survive_final_kill_signal");
}
}
static void cgroup_xattr_apply(Unit *u) {
@ -1177,8 +1116,9 @@ static void cgroup_apply_cpuset(Unit *u, const CPUSet *cpus, const char *name) {
}
static bool cgroup_context_has_io_config(CGroupContext *c) {
return c->io_accounting ||
c->io_weight != CGROUP_WEIGHT_INVALID ||
assert(c);
return c->io_weight != CGROUP_WEIGHT_INVALID ||
c->startup_io_weight != CGROUP_WEIGHT_INVALID ||
c->io_device_weights ||
c->io_device_latencies ||
@ -1289,15 +1229,11 @@ static void cgroup_apply_io_device_limit(Unit *u, const char *dev_path, uint64_t
(void) set_attribute_and_warn(u, "io.max", buf);
}
static bool unit_has_memory_config(Unit *u) {
CGroupContext *c;
static bool cgroup_context_has_memory_config(CGroupContext *c) {
assert(c);
assert(u);
assert_se(c = unit_get_cgroup_context(u));
return unit_get_ancestor_memory_min(u) > 0 ||
unit_get_ancestor_memory_low(u) > 0 || unit_get_ancestor_startup_memory_low(u) > 0 ||
return c->memory_min > 0 ||
c->memory_low > 0 || c->startup_memory_low_set ||
c->memory_high != CGROUP_LIMIT_MAX || c->startup_memory_high_set ||
c->memory_max != CGROUP_LIMIT_MAX || c->startup_memory_max_set ||
c->memory_swap_max != CGROUP_LIMIT_MAX || c->startup_memory_swap_max_set ||
@ -1543,19 +1479,20 @@ static void cgroup_context_apply(
/* 'memory' attributes do not exist on the root cgroup. */
if ((apply_mask & CGROUP_MASK_MEMORY) && !is_local_root) {
uint64_t max = CGROUP_LIMIT_MAX, swap_max = CGROUP_LIMIT_MAX, zswap_max = CGROUP_LIMIT_MAX, high = CGROUP_LIMIT_MAX;
uint64_t low = CGROUP_LIMIT_MIN, max = CGROUP_LIMIT_MAX, swap_max = CGROUP_LIMIT_MAX, zswap_max = CGROUP_LIMIT_MAX, high = CGROUP_LIMIT_MAX;
if (unit_has_memory_config(u)) {
if (cgroup_context_has_memory_config(c)) {
bool startup = IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING, MANAGER_STOPPING);
low = startup && c->startup_memory_low_set ? c->startup_memory_low : c->memory_low;
high = startup && c->startup_memory_high_set ? c->startup_memory_high : c->memory_high;
max = startup && c->startup_memory_max_set ? c->startup_memory_max : c->memory_max;
swap_max = startup && c->startup_memory_swap_max_set ? c->startup_memory_swap_max : c->memory_swap_max;
zswap_max = startup && c->startup_memory_zswap_max_set ? c->startup_memory_zswap_max : c->memory_zswap_max;
}
cgroup_apply_memory_limit(u, "memory.min", unit_get_ancestor_memory_min(u));
cgroup_apply_memory_limit(u, "memory.low", unit_get_ancestor_memory_low(u));
cgroup_apply_memory_limit(u, "memory.min", c->memory_min);
cgroup_apply_memory_limit(u, "memory.low", low);
cgroup_apply_memory_limit(u, "memory.high", high);
cgroup_apply_memory_limit(u, "memory.max", max);
cgroup_apply_memory_limit(u, "memory.swap.max", swap_max);
@ -1718,11 +1655,12 @@ static CGroupMask unit_get_cgroup_mask(Unit *u) {
if (cgroup_context_has_allowed_cpus(c) || cgroup_context_has_allowed_mems(c))
mask |= CGROUP_MASK_CPUSET;
if (cgroup_context_has_io_config(c))
if (c->io_accounting ||
cgroup_context_has_io_config(c))
mask |= CGROUP_MASK_IO;
if (c->memory_accounting ||
unit_has_memory_config(u))
cgroup_context_has_memory_config(c))
mask |= CGROUP_MASK_MEMORY;
if (cgroup_context_has_device_policy(c))

View File

@ -137,9 +137,6 @@ typedef struct CGroupContext {
LIST_HEAD(CGroupIODeviceLimit, io_device_limits);
LIST_HEAD(CGroupIODeviceLatency, io_device_latencies);
uint64_t default_memory_min;
uint64_t default_memory_low;
uint64_t default_startup_memory_low;
uint64_t memory_min;
uint64_t memory_low;
uint64_t startup_memory_low;
@ -152,11 +149,6 @@ typedef struct CGroupContext {
uint64_t memory_zswap_max;
uint64_t startup_memory_zswap_max;
bool default_memory_min_set:1;
bool default_memory_low_set:1;
bool default_startup_memory_low_set:1;
bool memory_min_set:1;
bool memory_low_set:1;
bool startup_memory_low_set:1;
bool startup_memory_high_set:1;
bool startup_memory_max_set:1;
@ -416,10 +408,6 @@ Unit* manager_get_unit_by_pidref_cgroup(Manager *m, const PidRef *pid);
Unit* manager_get_unit_by_pidref_watching(Manager *m, const PidRef *pid);
Unit* manager_get_unit_by_pidref(Manager *m, PidRef *pid);
uint64_t unit_get_ancestor_memory_min(Unit *u);
uint64_t unit_get_ancestor_memory_low(Unit *u);
uint64_t unit_get_ancestor_startup_memory_low(Unit *u);
int unit_search_main_pid(Unit *u, PidRef *ret);
int unit_get_memory_available(Unit *u, uint64_t *ret);

View File

@ -395,9 +395,6 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_PROPERTY("IOWriteIOPSMax", "a(st)", property_get_io_device_limits, 0, 0),
SD_BUS_PROPERTY("IODeviceLatencyTargetUSec", "a(st)", property_get_io_device_latency, 0, 0),
SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0),
SD_BUS_PROPERTY("DefaultMemoryLow", "t", NULL, offsetof(CGroupContext, default_memory_low), 0),
SD_BUS_PROPERTY("DefaultStartupMemoryLow", "t", NULL, offsetof(CGroupContext, default_startup_memory_low), 0),
SD_BUS_PROPERTY("DefaultMemoryMin", "t", NULL, offsetof(CGroupContext, default_memory_min), 0),
SD_BUS_PROPERTY("MemoryMin", "t", NULL, offsetof(CGroupContext, memory_min), 0),
SD_BUS_PROPERTY("MemoryLow", "t", NULL, offsetof(CGroupContext, memory_low), 0),
SD_BUS_PROPERTY("StartupMemoryLow", "t", NULL, offsetof(CGroupContext, startup_memory_low), 0),
@ -447,6 +444,10 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_ast, 0, SD_BUS_VTABLE_DEPRECATED|SD_BUS_VTABLE_HIDDEN),
/* since kernel v4.15 CPU accounting requires no controller, i.e. is available everywhere */
SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool_true, 0, SD_BUS_VTABLE_DEPRECATED|SD_BUS_VTABLE_HIDDEN),
/* since kernel v5.7 kernel would take care of these when cgroup is mounted with memory_recursiveprot */
SD_BUS_PROPERTY("DefaultMemoryMin", "t", bus_property_get_uint64_0, 0, SD_BUS_VTABLE_DEPRECATED|SD_BUS_VTABLE_HIDDEN),
SD_BUS_PROPERTY("DefaultMemoryLow", "t", bus_property_get_uint64_0, 0, SD_BUS_VTABLE_DEPRECATED|SD_BUS_VTABLE_HIDDEN),
SD_BUS_PROPERTY("DefaultStartupMemoryLow", "t", bus_property_get_uint64_0, 0, SD_BUS_VTABLE_DEPRECATED|SD_BUS_VTABLE_HIDDEN),
SD_BUS_VTABLE_END
};
@ -1053,19 +1054,11 @@ int bus_cgroup_set_property(
if (streq(name, "MemoryAccounting"))
return bus_cgroup_set_boolean(u, name, &c->memory_accounting, CGROUP_MASK_MEMORY, message, flags, reterr_error);
if (streq(name, "MemoryMin")) {
r = bus_cgroup_set_memory_protection(u, name, &c->memory_min, message, flags, reterr_error);
if (r > 0)
c->memory_min_set = true;
return r;
}
if (streq(name, "MemoryMin"))
return bus_cgroup_set_memory_protection(u, name, &c->memory_min, message, flags, reterr_error);
if (streq(name, "MemoryLow")) {
r = bus_cgroup_set_memory_protection(u, name, &c->memory_low, message, flags, reterr_error);
if (r > 0)
c->memory_low_set = true;
return r;
}
if (streq(name, "MemoryLow"))
return bus_cgroup_set_memory_protection(u, name, &c->memory_low, message, flags, reterr_error);
if (streq(name, "StartupMemoryLow")) {
r = bus_cgroup_set_memory_protection(u, name, &c->startup_memory_low, message, flags, reterr_error);
@ -1074,27 +1067,6 @@ int bus_cgroup_set_property(
return r;
}
if (streq(name, "DefaultMemoryMin")) {
r = bus_cgroup_set_memory_protection(u, name, &c->default_memory_min, message, flags, reterr_error);
if (r > 0)
c->default_memory_min_set = true;
return r;
}
if (streq(name, "DefaultMemoryLow")) {
r = bus_cgroup_set_memory_protection(u, name, &c->default_memory_low, message, flags, reterr_error);
if (r > 0)
c->default_memory_low_set = true;
return r;
}
if (streq(name, "DefaultStartupMemoryLow")) {
r = bus_cgroup_set_memory_protection(u, name, &c->default_startup_memory_low, message, flags, reterr_error);
if (r > 0)
c->default_startup_memory_low_set = true;
return r;
}
if (streq(name, "MemoryHigh"))
return bus_cgroup_set_memory(u, name, &c->memory_high, message, flags, reterr_error);
@ -1135,33 +1107,11 @@ int bus_cgroup_set_property(
return r;
}
if (streq(name, "MemoryMinScale")) {
r = bus_cgroup_set_memory_protection_scale(u, name, &c->memory_min, message, flags, reterr_error);
if (r > 0)
c->memory_min_set = true;
return r;
}
if (streq(name, "MemoryMinScale"))
return bus_cgroup_set_memory_protection_scale(u, name, &c->memory_min, message, flags, reterr_error);
if (streq(name, "MemoryLowScale")) {
r = bus_cgroup_set_memory_protection_scale(u, name, &c->memory_low, message, flags, reterr_error);
if (r > 0)
c->memory_low_set = true;
return r;
}
if (streq(name, "DefaultMemoryMinScale")) {
r = bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_min, message, flags, reterr_error);
if (r > 0)
c->default_memory_min_set = true;
return r;
}
if (streq(name, "DefaultMemoryLowScale")) {
r = bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_low, message, flags, reterr_error);
if (r > 0)
c->default_memory_low_set = true;
return r;
}
if (streq(name, "MemoryLowScale"))
return bus_cgroup_set_memory_protection_scale(u, name, &c->memory_low, message, flags, reterr_error);
if (streq(name, "MemoryHighScale"))
return bus_cgroup_set_memory_scale(u, name, &c->memory_high, message, flags, reterr_error);
@ -2041,8 +1991,8 @@ int bus_cgroup_set_property(
return 1;
}
/* deprecated CGroup v1 properties */
if (STR_IN_SET(name,
/* deprecated CGroup v1 properties */
"MemoryLimit",
"MemoryLimitScale",
"CPUShares",
@ -2053,7 +2003,13 @@ int bus_cgroup_set_property(
"BlockIODeviceWeight",
"BlockIOReadBandwidth",
"BlockIOWriteBandwidth",
"CPUAccounting")) { /* see comment in bus_cgroup_vtable */
/* see comments in bus_cgroup_vtable */
"CPUAccounting",
"DefaultMemoryMin",
"DefaultMemoryMinScale",
"DefaultMemoryLow",
"DefaultMemoryLowScale",
"DefaultStartupMemoryLow")) {
r = sd_bus_message_skip(message, NULL);
if (r < 0)

View File

@ -796,7 +796,7 @@ static int property_get_root_hash_sig(
static int bus_append_mount_options(
sd_bus_message *reply,
MountOptions *options) {
const MountOptions *options) {
int r;

View File

@ -194,7 +194,8 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_
return sd_bus_error_set(reterr_error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized");
if (is_image) {
r = bus_read_mount_options(message, reterr_error, &options, NULL, "");
r = bus_read_mount_options(message, reterr_error, &options,
/* in_out_format_str = */ NULL, /* separator = */ NULL);
if (r < 0)
return r;
}

View File

@ -267,49 +267,50 @@ int bus_read_mount_options(
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
_cleanup_free_ char *format_str = NULL;
const char *mount_options, *partition;
int r;
assert(message);
assert(ret_options);
assert(separator);
assert(!in_out_format_str == !separator);
r = sd_bus_message_enter_container(message, 'a', "(ss)");
if (r < 0)
return r;
const char *partition, *mount_options;
while ((r = sd_bus_message_read(message, "(ss)", &partition, &mount_options)) > 0) {
_cleanup_free_ char *escaped = NULL;
PartitionDesignator partition_designator;
if (chars_intersect(mount_options, WHITESPACE))
return sd_bus_error_setf(reterr_error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid mount options string, contains whitespace character(s): %s", mount_options);
"Invalid mount options string, contains whitespace character(s): %s", mount_options);
partition_designator = partition_designator_from_string(partition);
if (partition_designator < 0)
return sd_bus_error_setf(reterr_error, SD_BUS_ERROR_INVALID_ARGS, "Invalid partition name %s", partition);
/* Need to store the options with the escapes, so that they can be parsed again */
escaped = shell_escape(mount_options, ":");
if (!escaped)
return -ENOMEM;
if (!options) {
options = new0(MountOptions, 1);
if (!options)
return -ENOMEM;
}
if (!isempty(escaped)) {
if (!strextend_with_separator(&format_str, separator, partition, ":", escaped))
r = free_and_strdup(&options->options[partition_designator], mount_options);
if (r < 0)
return r;
if (in_out_format_str && !isempty(mount_options)) {
/* Need to store the options with the escapes, so that they can be parsed again */
_cleanup_free_ char *escaped = NULL;
escaped = shell_escape(mount_options, ":");
if (!escaped)
return -ENOMEM;
if (!options) {
options = new0(MountOptions, 1);
if (!options)
return -ENOMEM;
}
r = free_and_strdup(&options->options[partition_designator], mount_options);
r = strextendf_with_separator(&format_str, separator, "%s:%s", partition, escaped);
if (r < 0)
return r;
} else if (options)
options->options[partition_designator] = mfree(options->options[partition_designator]);
}
}
if (r < 0)
return r;
@ -318,11 +319,10 @@ int bus_read_mount_options(
if (r < 0)
return r;
if (options) {
if (in_out_format_str && !strextend_with_separator(in_out_format_str, separator, format_str))
return -ENOMEM;
*ret_options = TAKE_PTR(options);
}
if (in_out_format_str && !strextend_with_separator(in_out_format_str, separator, format_str))
return -ENOMEM;
*ret_options = TAKE_PTR(options);
return 0;
}

View File

@ -132,18 +132,6 @@ static int exec_cgroup_context_serialize(const CGroupContext *c, FILE *f) {
return r;
}
if (c->default_memory_min > 0) {
r = serialize_item_format(f, "exec-cgroup-context-default-memory-min", "%" PRIu64, c->default_memory_min);
if (r < 0)
return r;
}
if (c->default_memory_low > 0) {
r = serialize_item_format(f, "exec-cgroup-context-default-memory-low", "%" PRIu64, c->default_memory_low);
if (r < 0)
return r;
}
if (c->memory_min > 0) {
r = serialize_item_format(f, "exec-cgroup-context-memory-min", "%" PRIu64, c->memory_min);
if (r < 0)
@ -226,26 +214,6 @@ static int exec_cgroup_context_serialize(const CGroupContext *c, FILE *f) {
return r;
}
r = serialize_bool_elide(f, "exec-cgroup-context-default-memory-min-set", c->default_memory_min_set);
if (r < 0)
return r;
r = serialize_bool_elide(f, "exec-cgroup-context-default-memory-low-set", c->default_memory_low_set);
if (r < 0)
return r;
r = serialize_bool_elide(f, "exec-cgroup-context-default-startup-memory-low-set", c->default_startup_memory_low_set);
if (r < 0)
return r;
r = serialize_bool_elide(f, "exec-cgroup-context-memory-min-set", c->memory_min_set);
if (r < 0)
return r;
r = serialize_bool_elide(f, "exec-cgroup-context-memory-low-set", c->memory_low_set);
if (r < 0)
return r;
r = serialize_bool_elide(f, "exec-cgroup-context-startup-memory-low-set", c->startup_memory_low_set);
if (r < 0)
return r;
@ -532,14 +500,6 @@ static int exec_cgroup_context_deserialize(CGroupContext *c, FILE *f) {
r = safe_atou64(val, &c->startup_io_weight);
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-cgroup-context-default-memory-min="))) {
r = safe_atou64(val, &c->default_memory_min);
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-cgroup-context-default-memory-low="))) {
r = safe_atou64(val, &c->default_memory_low);
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-cgroup-context-memory-min="))) {
r = safe_atou64(val, &c->memory_min);
if (r < 0)
@ -597,31 +557,6 @@ static int exec_cgroup_context_deserialize(CGroupContext *c, FILE *f) {
r = safe_atou64(val, &c->tasks_max.scale);
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-cgroup-context-default-memory-min-set="))) {
r = parse_boolean(val);
if (r < 0)
return r;
c->default_memory_min_set = r;
} else if ((val = startswith(l, "exec-cgroup-context-default-memory-low-set="))) {
r = parse_boolean(val);
if (r < 0)
return r;
c->default_memory_low_set = r;
} else if ((val = startswith(l, "exec-cgroup-context-default-startup-memory-low-set="))) {
r = parse_boolean(val);
if (r < 0)
return r;
c->default_startup_memory_low_set = r;
} else if ((val = startswith(l, "exec-cgroup-context-memory-min-set="))) {
r = parse_boolean(val);
if (r < 0)
return r;
c->memory_min_set = r;
} else if ((val = startswith(l, "exec-cgroup-context-memory-low-set="))) {
r = parse_boolean(val);
if (r < 0)
return r;
c->memory_low_set = r;
} else if ((val = startswith(l, "exec-cgroup-context-startup-memory-low-set="))) {
r = parse_boolean(val);
if (r < 0)

View File

@ -223,11 +223,11 @@
{{type}}.CPUQuotaPeriodSec, config_parse_sec_def_infinity, 0, offsetof({{type}}, cgroup_context.cpu_quota_period_usec)
{{type}}.MemoryAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.memory_accounting)
{{type}}.MemoryMin, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
{{type}}.DefaultMemoryMin, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
{{type}}.DefaultMemoryLow, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
{{type}}.DefaultStartupMemoryLow, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
{{type}}.MemoryLow, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
{{type}}.StartupMemoryLow, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
{{type}}.DefaultMemoryMin, config_parse_warn_compat, DISABLED_LEGACY, 0
{{type}}.DefaultMemoryLow, config_parse_warn_compat, DISABLED_LEGACY, 0
{{type}}.DefaultStartupMemoryLow, config_parse_warn_compat, DISABLED_LEGACY, 0
{{type}}.MemoryHigh, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
{{type}}.StartupMemoryHigh, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
{{type}}.MemoryMax, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)

View File

@ -3790,9 +3790,7 @@ int config_parse_memory_limit(
uint64_t bytes = CGROUP_LIMIT_MAX;
int r;
if (isempty(rvalue) && STR_IN_SET(lvalue, "DefaultMemoryLow",
"DefaultMemoryMin",
"MemoryLow",
if (isempty(rvalue) && STR_IN_SET(lvalue, "MemoryLow",
"StartupMemoryLow",
"MemoryMin"))
bytes = CGROUP_LIMIT_MIN;
@ -3816,31 +3814,17 @@ int config_parse_memory_limit(
"StartupMemoryZSwapMax",
"MemoryLow",
"StartupMemoryLow",
"MemoryMin",
"DefaultMemoryLow",
"DefaultstartupMemoryLow",
"DefaultMemoryMin"))) {
"MemoryMin"))) {
log_syntax(unit, LOG_WARNING, filename, line, 0, "Memory limit '%s' out of range, ignoring.", rvalue);
return 0;
}
}
if (streq(lvalue, "DefaultMemoryLow")) {
c->default_memory_low = bytes;
c->default_memory_low_set = true;
} else if (streq(lvalue, "DefaultStartupMemoryLow")) {
c->default_startup_memory_low = bytes;
c->default_startup_memory_low_set = true;
} else if (streq(lvalue, "DefaultMemoryMin")) {
c->default_memory_min = bytes;
c->default_memory_min_set = true;
} else if (streq(lvalue, "MemoryMin")) {
if (streq(lvalue, "MemoryMin"))
c->memory_min = bytes;
c->memory_min_set = true;
} else if (streq(lvalue, "MemoryLow")) {
else if (streq(lvalue, "MemoryLow"))
c->memory_low = bytes;
c->memory_low_set = true;
} else if (streq(lvalue, "StartupMemoryLow")) {
else if (streq(lvalue, "StartupMemoryLow")) {
c->startup_memory_low = bytes;
c->startup_memory_low_set = true;
} else if (streq(lvalue, "MemoryHigh"))

View File

@ -1515,6 +1515,7 @@ static int mount_private_cgroup2fs(const MountEntry *m, const NamespaceParameter
static int mount_procfs(const MountEntry *m, const NamespaceParameters *p) {
_cleanup_free_ char *opts = NULL;
int r;
assert(m);
assert(p);
@ -1522,32 +1523,16 @@ static int mount_procfs(const MountEntry *m, const NamespaceParameters *p) {
if (p->protect_proc != PROTECT_PROC_DEFAULT ||
p->proc_subset != PROC_SUBSET_ALL) {
/* Starting with kernel 5.8 procfs' hidepid= logic is truly per-instance (previously it
* pretended to be per-instance but actually was per-namespace), hence let's make use of it
* if requested. To make sure this logic succeeds only on kernels where hidepid= is
* per-instance, we'll exclusively use the textual value for hidepid=, since support was
* added in the same commit: if it's supported it is thus also per-instance. */
opts = strjoin("hidepid=",
p->protect_proc == PROTECT_PROC_DEFAULT ? "off" : protect_proc_to_string(p->protect_proc));
if (!opts)
return -ENOMEM;
const char *hpv = p->protect_proc == PROTECT_PROC_DEFAULT ?
"off" :
protect_proc_to_string(p->protect_proc);
/* hidepid= support was added in 5.8, so we can use fsconfig()/fsopen() (which were added in
* 5.2) to check if hidepid= is supported. This avoids a noisy dmesg log by the kernel when
* trying to use hidepid= on systems where it isn't supported. The same applies for subset=.
* fsopen()/fsconfig() was also backported on some distros which allows us to detect
* hidepid=/subset= support in even more scenarios. */
if (mount_option_supported("proc", "hidepid", hpv) > 0) {
opts = strjoin("hidepid=", hpv);
if (!opts)
return -ENOMEM;
if (p->proc_subset != PROC_SUBSET_ALL) {
r = strextendf_with_separator(&opts, ",", "subset=%s", proc_subset_to_string(p->proc_subset));
if (r < 0)
return r;
}
if (p->proc_subset == PROC_SUBSET_PID &&
mount_option_supported("proc", "subset", "pid") > 0)
if (!strextend_with_separator(&opts, ",", "subset=pid"))
return -ENOMEM;
}
/* Mount a new instance, so that we get the one that matches our user namespace, if we are running in

View File

@ -5582,7 +5582,7 @@ int unit_fork_helper_process_full(Unit *u, const char *name, bool into_cgroup, F
}
int unit_fork_helper_process(Unit *u, const char *name, bool into_cgroup, PidRef *ret) {
return unit_fork_helper_process_full(u, name, into_cgroup, /* flags = */ 0, ret);
return unit_fork_helper_process_full(u, name, into_cgroup, /* flags= */ 0, ret);
}
int unit_fork_and_watch_rm_rf(Unit *u, char **paths, PidRef *ret_pid) {

View File

@ -259,12 +259,9 @@ int unit_cgroup_context_build_json(sd_json_variant **ret, const char *name, void
/* Memory Accounting and Control */
SD_JSON_BUILD_PAIR_BOOLEAN("MemoryAccounting", c->memory_accounting),
JSON_BUILD_PAIR_CONDITION_UNSIGNED(c->memory_min_set, "MemoryMin", c->memory_min),
JSON_BUILD_PAIR_CONDITION_UNSIGNED(c->default_memory_min_set, "DefaultMemoryMin", c->default_memory_min),
JSON_BUILD_PAIR_CONDITION_UNSIGNED(c->memory_low_set, "MemoryLow", c->memory_low),
JSON_BUILD_PAIR_CONDITION_UNSIGNED(c->default_memory_low_set, "DefaultMemoryLow", c->default_memory_low),
JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("MemoryMin", c->memory_min, CGROUP_LIMIT_MIN),
JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("MemoryLow", c->memory_low, CGROUP_LIMIT_MIN),
JSON_BUILD_PAIR_CONDITION_UNSIGNED(c->startup_memory_low_set, "StartupMemoryLow", c->startup_memory_low),
JSON_BUILD_PAIR_CONDITION_UNSIGNED(c->default_startup_memory_low_set, "DefaultStartupMemoryLow", c->default_startup_memory_low),
JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("MemoryHigh", c->memory_high, CGROUP_LIMIT_MAX),
JSON_BUILD_PAIR_CONDITION_UNSIGNED(c->startup_memory_high_set, "StartupMemoryHigh", c->startup_memory_high),
JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("MemoryMax", c->memory_max, CGROUP_LIMIT_MAX),

View File

@ -54,7 +54,7 @@ static int json_append_mount_options(sd_json_variant **v, MountOptions *options)
return 0;
for (PartitionDesignator j = 0; j < _PARTITION_DESIGNATOR_MAX; j++) {
if (!options->options[j])
if (isempty(options->options[j]))
continue;
r = sd_json_variant_append_arraybo(

View File

@ -126,6 +126,7 @@ static char *arg_tpm2_measure_keyslot_nvpcr = NULL;
static char *arg_link_keyring = NULL;
static char *arg_link_key_type = NULL;
static char *arg_link_key_description = NULL;
static char *arg_fixate_volume_key = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_cipher, freep);
STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
@ -143,6 +144,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_tpm2_pcrlock, freep);
STATIC_DESTRUCTOR_REGISTER(arg_link_keyring, freep);
STATIC_DESTRUCTOR_REGISTER(arg_link_key_type, freep);
STATIC_DESTRUCTOR_REGISTER(arg_link_key_description, freep);
STATIC_DESTRUCTOR_REGISTER(arg_fixate_volume_key, freep);
static const char* const passphrase_type_table[_PASSPHRASE_TYPE_MAX] = {
[PASSPHRASE_REGULAR] = "passphrase",
@ -651,6 +653,11 @@ static int parse_one_option(const char *option) {
#else
log_error("Build lacks libcryptsetup support for linking volume keys in user specified kernel keyrings upon device activation, ignoring: %s", option);
#endif
} else if ((val = startswith(option, "fixate-volume-key="))) {
r = free_and_strdup(&arg_fixate_volume_key, val);
if (r < 0)
return log_oom();
} else if (!streq(option, "x-initrd.attach"))
log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
@ -1050,24 +1057,29 @@ static int measure_volume_key(
* unprotected direct hash of the secret volume key over the wire to the TPM. Hence let's instead
* send a HMAC signature instead. */
_cleanup_free_ char *escaped = NULL;
escaped = xescape(name, ":"); /* avoid ambiguity around ":" once we join things below */
if (!escaped)
return log_oom();
_cleanup_free_ char *prefix = NULL;
_cleanup_free_ char *s = NULL;
s = strjoin("cryptsetup:", escaped, ":", strempty(crypt_get_uuid(cd)));
if (!s)
return log_oom();
/* Note: what is extended to the SHA256 bank here must match the expected hash of 'fixate-volume-key='
* calculated by cryptsetup_get_volume_key_id(). */
r = cryptsetup_get_volume_key_prefix(cd, name, &prefix);
if (r)
return log_error_errno(r, "Could not verify pcr banks: %m");
r = tpm2_pcr_extend_bytes(c, l ?: arg_tpm2_measure_banks, arg_tpm2_measure_pcr, &IOVEC_MAKE_STRING(s), &IOVEC_MAKE(volume_key, volume_key_size), TPM2_EVENT_VOLUME_KEY, s);
r = tpm2_pcr_extend_bytes(
c,
/* banks= */ l ?: arg_tpm2_measure_banks,
/* pcr_index = */ arg_tpm2_measure_pcr,
/* data = */ &IOVEC_MAKE_STRING(prefix),
/* secret = */ &IOVEC_MAKE(volume_key, volume_key_size),
/* event_type = */ TPM2_EVENT_VOLUME_KEY,
/* description = */ prefix);
if (r < 0)
return log_error_errno(r, "Could not extend PCR: %m");
log_struct(LOG_INFO,
LOG_MESSAGE_ID(SD_MESSAGE_TPM_PCR_EXTEND_STR),
LOG_MESSAGE("Successfully extended PCR index %u with '%s' and volume key (banks %s).", arg_tpm2_measure_pcr, s, joined),
LOG_ITEM("MEASURING=%s", s),
LOG_MESSAGE("Successfully extended PCR index %u with '%s' and volume key (banks %s).", arg_tpm2_measure_pcr, prefix, joined),
LOG_ITEM("MEASURING=%s", prefix),
LOG_ITEM("PCR=%u", arg_tpm2_measure_pcr),
LOG_ITEM("BANKS=%s", joined));
@ -1173,12 +1185,36 @@ static int measured_crypt_activate_by_volume_key(
/* A wrapper around crypt_activate_by_volume_key() which also measures to a PCR if that's requested. */
/* First, check if volume key digest matches the expectation. */
if (arg_fixate_volume_key) {
_cleanup_free_ char *key_id = NULL;
r = cryptsetup_get_volume_key_id(
cd,
/* volume_name= */ name,
/* volume_key= */ volume_key,
/* volume_key_size= */ volume_key_size,
/* ret= */ &key_id);
if (r < 0)
return log_error_errno(r, "Failed to get volume key id.");
if (!streq(arg_fixate_volume_key, key_id))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Volume key id: '%s' does not match the expectation: '%s'.",
key_id, arg_fixate_volume_key);
}
r = crypt_activate_by_volume_key(cd, name, volume_key, volume_key_size, flags);
if (r == -EEXIST) /* volume is already active */
return log_external_activation(r, name);
if (r < 0)
return r;
if (arg_tpm2_measure_pcr == UINT_MAX) {
log_debug("Not measuring volume key, deactivated.");
return 0;
}
if (volume_key_size > 0)
(void) measure_volume_key(cd, name, volume_key, volume_key_size); /* OK if fails */
else
@ -1204,14 +1240,12 @@ static int measured_crypt_activate_by_passphrase(
assert(cd);
/* A wrapper around crypt_activate_by_passphrase() which also measures to a PCR if that's
* requested. Note that we need the volume key for the measurement, and
* requested. Note that we may need the volume key for the measurement and/or for the comparison, and
* crypt_activate_by_passphrase() doesn't give us access to this. Hence, we operate indirectly, and
* retrieve the volume key first, and then activate through that. */
if (arg_tpm2_measure_pcr == UINT_MAX) {
log_debug("Not measuring volume key, deactivated.");
if (arg_tpm2_measure_pcr == UINT_MAX && !arg_fixate_volume_key)
goto shortcut;
}
r = crypt_get_volume_key_size(cd);
if (r < 0)
@ -1453,6 +1487,9 @@ static bool use_token_plugins(void) {
return false;
if (arg_tpm2_measure_keyslot_nvpcr)
return false;
/* Volume key is also needed if the expected key id is set */
if (arg_fixate_volume_key)
return false;
#endif
/* Disable tokens if we're in FIDO2 mode with manual parameters. */

View File

@ -340,11 +340,10 @@ static int map_all_fields(
void process_audit_string(Manager *m, int type, const char *data, size_t size) {
size_t n = 0, z;
uint64_t seconds, msec, id;
const char *p, *type_name;
const char *p, *type_name, *type_field_name, *mm;
char id_field[STRLEN("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)],
type_field[STRLEN("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)];
struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_AUDIT_FIELDS];
char *mm, *type_field_name;
int k;
assert(m);
@ -395,7 +394,11 @@ void process_audit_string(Manager *m, int type, const char *data, size_t size) {
type_field_name = strjoina("_AUDIT_TYPE_NAME=", type_name);
iovec[n++] = IOVEC_MAKE_STRING(type_field_name);
mm = strjoina("MESSAGE=", type_name, " ", p);
_cleanup_free_ char *mm_alloc = strjoin("MESSAGE=", type_name, " ", p);
if (mm_alloc)
mm = mm_alloc;
else
mm = strjoina("MESSAGE=", type_name, " (message is truncated because of OOM)");
iovec[n++] = IOVEC_MAKE_STRING(mm);
z = n;
@ -406,9 +409,8 @@ void process_audit_string(Manager *m, int type, const char *data, size_t size) {
TIMEVAL_STORE((usec_t) seconds * USEC_PER_SEC + (usec_t) msec * USEC_PER_MSEC),
LOG_NOTICE, 0);
/* free() all entries that map_all_fields() added. All others
* are allocated on the stack or are constant. */
/* free() all entries that map_all_fields() added. All others are allocated on the stack, constant,
* or freed by their _cleanup_ attributes. */
for (; z < n; z++)
free(iovec[z].iov_base);
}

View File

@ -5,6 +5,8 @@
#include "sd-forward.h"
#include "alloc-util.h" /* IWYU pragma: keep */
DECLARE_STRING_TABLE_LOOKUP(audit_type, int);
/* This is inspired by DNS TYPEnnn formatting */

View File

@ -117,8 +117,7 @@ int rtnl_resolve_ifname_full(
r = sd_netlink_call(*rtnl, message, 0, &reply);
if (r >= 0)
return parse_newlink_message(reply, ret_name, ret_altnames);
/* The kernels older than 76c9ac0ee878f6693d398d3a95ccaf85e1f597a6 (v5.5) return -EINVAL. */
if (!IN_SET(r, -ENODEV, -EINVAL))
if (r != -ENODEV)
return r;
}

View File

@ -1704,6 +1704,8 @@ static int varlink_idl_validate_symbol(const sd_varlink_symbol *symbol, sd_json_
static int varlink_idl_validate_field_element_type(const sd_varlink_field *field, sd_json_variant *v) {
assert(field);
assert(v);
assert(!sd_json_variant_is_null(v));
switch (field->field_type) {
@ -1764,7 +1766,8 @@ static int varlink_idl_validate_field_element_type(const sd_varlink_field *field
break;
case SD_VARLINK_ANY:
/* The any type accepts any JSON value, no validation needed */
/* The any type accepts any non-null JSON value, no validation needed. (Note that null is
* already handled by the caller.) */
break;
case _SD_VARLINK_FIELD_COMMENT:

View File

@ -2767,16 +2767,8 @@ static int reset_audit_loginuid(void) {
return 0;
r = write_string_file("/proc/self/loginuid", "4294967295", WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0) {
log_error_errno(r,
"Failed to reset audit login UID. This probably means that your kernel is too\n"
"old and you have audit enabled. Note that the auditing subsystem is known to\n"
"be incompatible with containers on old kernels. Please make sure to upgrade\n"
"your kernel or to off auditing with 'audit=0' on the kernel command line before\n"
"using systemd-nspawn. Sleeping for 5s... (%m)");
sleep(5);
}
if (r < 0)
return log_error_errno(r, "Failed to reset audit login UID: %m");
return 0;
}
@ -3775,7 +3767,7 @@ static int setup_unix_export_dir_outside(char **ret) {
"tmpfs",
q,
"tmpfs",
MS_NODEV|MS_NOEXEC|MS_NOSUID|ms_nosymfollow_supported(),
MS_NODEV|MS_NOEXEC|MS_NOSUID|MS_NOSYMFOLLOW,
"size=4M,nr_inodes=64,mode=0755");
if (r < 0)
return r;
@ -3789,7 +3781,7 @@ static int setup_unix_export_dir_outside(char **ret) {
/* what= */ NULL,
w,
/* fstype= */ NULL,
MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NODEV|MS_NOEXEC|MS_NOSUID|ms_nosymfollow_supported(),
MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NODEV|MS_NOEXEC|MS_NOSUID|MS_NOSYMFOLLOW,
/* options= */ NULL);
if (r < 0)
return r;
@ -3834,7 +3826,7 @@ static int setup_unix_export_host_inside(const char *directory, const char *unix
/* what= */ NULL,
p,
/* fstype= */ NULL,
MS_BIND|MS_REMOUNT|MS_NODEV|MS_NOEXEC|MS_NOSUID|ms_nosymfollow_supported(),
MS_BIND|MS_REMOUNT|MS_NODEV|MS_NOEXEC|MS_NOSUID|MS_NOSYMFOLLOW,
/* options= */ NULL);
if (r < 0)
return r;

View File

@ -27,6 +27,7 @@
#include "fileio.h"
#include "fs-util.h"
#include "glyph-util.h"
#include "image-policy.h"
#include "install.h"
#include "iovec-util.h"
#include "libmount-util.h"
@ -364,10 +365,12 @@ static int portable_extract_by_path(
const ImagePolicy *image_policy,
PortableMetadata **ret_os_release,
Hashmap **ret_unit_files,
ImagePolicy **ret_pinned_image_policy,
sd_bus_error *error) {
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
_cleanup_(portable_metadata_unrefp) PortableMetadata* os_release = NULL;
_cleanup_(image_policy_freep) ImagePolicy *pinned_image_policy = NULL;
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
int r;
@ -397,6 +400,7 @@ static int portable_extract_by_path(
} else if (r < 0)
return log_debug_errno(r, "Failed to set up loopback device for %s: %m", path);
else {
_cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
_cleanup_(rmdir_and_freep) char *tmpdir = NULL;
_cleanup_close_pair_ int seq[2] = EBADF_PAIR;
@ -449,6 +453,24 @@ static int portable_extract_by_path(
if (r < 0)
return r;
r = verity_settings_load(&verity, path, NULL, NULL);
if (r < 0)
return log_debug_errno(r, "Failed to load root hash: %m");
r = dissected_image_load_verity_sig_partition(m, d->fd, &verity);
if (r < 0)
return r;
r = dissected_image_guess_verity_roothash(m, &verity);
if (r < 0)
return r;
if (ret_pinned_image_policy) {
pinned_image_policy = image_policy_new_from_dissected(m, &verity);
if (!pinned_image_policy)
return -ENOMEM;
}
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, seq) < 0)
return log_debug_errno(errno, "Failed to allocated SOCK_SEQPACKET socket: %m");
@ -558,6 +580,9 @@ static int portable_extract_by_path(
if (ret_os_release)
*ret_os_release = TAKE_PTR(os_release);
if (ret_pinned_image_policy)
*ret_pinned_image_policy = TAKE_PTR(pinned_image_policy);
return 0;
}
@ -575,9 +600,12 @@ static int extract_image_and_extensions(
PortableMetadata **ret_os_release,
Hashmap **ret_unit_files,
char ***ret_valid_prefixes,
ImagePolicy **ret_pinned_root_image_policy,
ImagePolicy **ret_pinned_ext_image_policy,
sd_bus_error *error) {
_cleanup_free_ char *id = NULL, *id_like = NULL, *version_id = NULL, *sysext_level = NULL, *confext_level = NULL;
_cleanup_(image_policy_freep) ImagePolicy *pinned_root_image_policy = NULL, *pinned_ext_image_policy = NULL;
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
@ -669,6 +697,7 @@ static int extract_image_and_extensions(
image_policy,
&os_release,
&unit_files,
&pinned_root_image_policy,
error);
if (r < 0)
return r;
@ -700,6 +729,7 @@ static int extract_image_and_extensions(
ORDERED_HASHMAP_FOREACH(ext, extension_images) {
_cleanup_(portable_metadata_unrefp) PortableMetadata *extension_release_meta = NULL;
_cleanup_(image_policy_freep) ImagePolicy *policy = NULL;
_cleanup_hashmap_free_ Hashmap *extra_unit_files = NULL;
_cleanup_strv_free_ char **extension_release = NULL;
const char *e;
@ -713,6 +743,7 @@ static int extract_image_and_extensions(
image_policy,
&extension_release_meta,
&extra_unit_files,
&policy,
error);
if (r < 0)
return r;
@ -721,6 +752,19 @@ static int extract_image_and_extensions(
if (r < 0)
return r;
if (!pinned_ext_image_policy && policy)
pinned_ext_image_policy = TAKE_PTR(policy);
else if (policy) {
_cleanup_(image_policy_freep) ImagePolicy *intersected_policy = NULL;
/* There is a single policy for all extension images, so we need a union */
r = image_policy_union(pinned_ext_image_policy, policy, &intersected_policy);
if (r < 0)
return log_debug_errno(r, "Failed to merge extension image policies: %m");
free_and_replace(pinned_ext_image_policy, intersected_policy);
}
if (!validate_extension && !ret_valid_prefixes && !ret_extension_releases)
continue;
@ -768,6 +812,10 @@ static int extract_image_and_extensions(
*ret_unit_files = TAKE_PTR(unit_files);
if (ret_valid_prefixes)
*ret_valid_prefixes = TAKE_PTR(valid_prefixes);
if (ret_pinned_root_image_policy)
*ret_pinned_root_image_policy = TAKE_PTR(pinned_root_image_policy);
if (ret_pinned_ext_image_policy)
*ret_pinned_ext_image_policy = TAKE_PTR(pinned_ext_image_policy);
return 0;
}
@ -808,6 +856,8 @@ int portable_extract(
&os_release,
&unit_files,
ret_valid_prefixes ? &valid_prefixes : NULL,
/* pinned_root_image_policy= */ NULL,
/* pinned_ext_image_policy= */ NULL,
error);
if (r < 0)
return r;
@ -1110,6 +1160,8 @@ static int install_chroot_dropin(
ImageType type,
OrderedHashmap *extension_images,
OrderedHashmap *extension_releases,
const ImagePolicy *pinned_root_image_policy,
const ImagePolicy *pinned_ext_image_policy,
const PortableMetadata *m,
const PortableMetadata *os_release,
const char *dropin_dir,
@ -1152,6 +1204,18 @@ static int install_chroot_dropin(
"LogExtraFields=PORTABLE=", base_name, "\n"))
return -ENOMEM;
if (pinned_root_image_policy) {
_cleanup_free_ char *policy_str = NULL;
r = image_policy_to_string(pinned_root_image_policy, /* simplify= */ true, &policy_str);
if (r < 0)
return log_debug_errno(r, "Failed to serialize pinned image policy: %m");
if (!strextend(&text,
"RootImagePolicy=", policy_str, "\n"))
return -ENOMEM;
}
/* If we have a single image then PORTABLE= will point to it, so we add
* PORTABLE_NAME_AND_VERSION= with the os-release fields and we are done. But if we have
* extensions, PORTABLE= will point to the image where the current unit was found in. So we
@ -1201,6 +1265,18 @@ static int install_chroot_dropin(
"LogExtraFields=PORTABLE_EXTENSION=", extension_base_name, "\n"))
return -ENOMEM;
if (pinned_ext_image_policy) {
_cleanup_free_ char *policy_str = NULL;
r = image_policy_to_string(pinned_ext_image_policy, /* simplify= */ true, &policy_str);
if (r < 0)
return log_debug_errno(r, "Failed to serialize pinned image policy: %m");
if (!strextend(&text,
"ExtensionImagePolicy=", policy_str, "\n"))
return -ENOMEM;
}
/* Look for image/version identifiers in the extension release files. We
* look for all possible IDs, but typically only 1 or 2 will be set, so
* the number of fields added shouldn't be too large. We prefix the DDI
@ -1317,6 +1393,8 @@ static int attach_unit_file(
ImageType type,
OrderedHashmap *extension_images,
OrderedHashmap *extension_releases,
const ImagePolicy *pinned_root_image_policy,
const ImagePolicy *pinned_ext_image_policy,
const PortableMetadata *m,
const PortableMetadata *os_release,
const char *profile,
@ -1362,7 +1440,20 @@ static int attach_unit_file(
* is reloaded while we are creating things here: as long as only the drop-ins exist the unit doesn't exist at
* all for PID 1. */
r = install_chroot_dropin(image_path, type, extension_images, extension_releases, m, os_release, dropin_dir, flags, &chroot_dropin, changes, n_changes);
r = install_chroot_dropin(
image_path,
type,
extension_images,
extension_releases,
pinned_root_image_policy,
pinned_ext_image_policy,
m,
os_release,
dropin_dir,
flags,
&chroot_dropin,
changes,
n_changes);
if (r < 0)
return r;
@ -1631,6 +1722,7 @@ int portable_attach(
size_t *n_changes,
sd_bus_error *error) {
_cleanup_(image_policy_freep) ImagePolicy *pinned_root_image_policy = NULL, *pinned_ext_image_policy = NULL;
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
@ -1656,6 +1748,8 @@ int portable_attach(
&os_release,
&unit_files,
&valid_prefixes,
&pinned_root_image_policy,
&pinned_ext_image_policy,
error);
if (r < 0)
return r;
@ -1720,8 +1814,20 @@ int portable_attach(
}
HASHMAP_FOREACH(item, unit_files) {
r = attach_unit_file(&paths, image->path, image->type, extension_images, extension_releases,
item, os_release, profile, flags, changes, n_changes);
r = attach_unit_file(
&paths,
image->path,
image->type,
extension_images,
extension_releases,
pinned_root_image_policy,
pinned_ext_image_policy,
item,
os_release,
profile,
flags,
changes,
n_changes);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to attach unit '%s': %m", item->name);
}

View File

@ -318,6 +318,7 @@ typedef struct PartitionEncryptedVolume {
char *name;
char *keyfile;
char *options;
bool fixate_volume_key;
} PartitionEncryptedVolume;
static PartitionEncryptedVolume* partition_encrypted_volume_free(PartitionEncryptedVolume *c) {
@ -2547,7 +2548,8 @@ static int config_parse_encrypted_volume(
void *data,
void *userdata) {
_cleanup_free_ char *volume = NULL, *keyfile = NULL, *options = NULL;
_cleanup_free_ char *volume = NULL, *keyfile = NULL, *options = NULL, *extra = NULL;
bool fixate_volume_key = false;
Partition *p = ASSERT_PTR(data);
int r;
@ -2558,7 +2560,7 @@ static int config_parse_encrypted_volume(
const char *q = rvalue;
r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNQUOTE,
&volume, &keyfile, &options);
&volume, &keyfile, &options, &extra);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
@ -2589,10 +2591,29 @@ static int config_parse_encrypted_volume(
if (!p->encrypted_volume)
return log_oom();
for (const char *e = extra;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&e, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
"Failed to parse extra options '%s', ignoring", word);
break;
}
if (r == 0)
break;
if (streq(word, "fixate-volume-key"))
fixate_volume_key = true;
else
log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown extra option '%s', ignoring", word);
}
*p->encrypted_volume = (PartitionEncryptedVolume) {
.name = TAKE_PTR(volume),
.keyfile = TAKE_PTR(keyfile),
.options = TAKE_PTR(options),
.fixate_volume_key = fixate_volume_key,
};
return 0;
@ -5174,6 +5195,44 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
if (r < 0)
return log_error_errno(r, "Failed to LUKS2 format future partition: %m");
if (p->encrypted_volume && p->encrypted_volume->fixate_volume_key) {
_cleanup_free_ char *key_id = NULL, *hash_option = NULL;
r = sym_crypt_get_volume_key_size(cd);
if (r < 0)
return log_error_errno(r, "Failed to determine volume key size: %m");
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume key has zero size and 'fixate-volume-key' is used");
_cleanup_(iovec_done) struct iovec vk = {
.iov_base = malloc(r),
.iov_len = r,
};
if (!vk.iov_base)
return log_oom();
r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, (char *) vk.iov_base, &vk.iov_len, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to get volume key: %m");
r = cryptsetup_get_volume_key_id(
cd,
/* volume_name= */ p->encrypted_volume->name,
/* volume_key= */ vk.iov_base,
/* volume_key_size= */ vk.iov_len,
/* ret= */ &key_id);
if (r < 0)
return log_error_errno(r, "Failed to get volume key hash: %m");
hash_option = strjoin("fixate-volume-key=", key_id);
if (!hash_option)
return log_oom();
if (!strextend_with_separator(&p->encrypted_volume->options, ",", hash_option))
return log_oom();
}
if (IN_SET(p->encrypt, ENCRYPT_KEY_FILE, ENCRYPT_KEY_FILE_TPM2)) {
/* Use partition-specific key if available, otherwise fall back to global key */
struct iovec *iovec_key = arg_key.iov_base ? &arg_key : &p->key;
@ -10719,14 +10778,6 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
r = context_fstab(context);
if (r < 0)
return r;
r = context_crypttab(context);
if (r < 0)
return r;
r = context_update_verity_size(context);
if (r < 0)
return r;
@ -10782,6 +10833,14 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
r = context_fstab(context);
if (r < 0)
return r;
r = context_crypttab(context);
if (r < 0)
return r;
(void) context_dump(context, /* late= */ true);
context_disarm_auto_removal(context);

View File

@ -10,6 +10,7 @@
BUS_DEFINE_PROPERTY_GET_GLOBAL(bus_property_get_bool_false, "b", 0);
BUS_DEFINE_PROPERTY_GET_GLOBAL(bus_property_get_bool_true, "b", 1);
BUS_DEFINE_PROPERTY_GET_GLOBAL(bus_property_get_uint64_0, "t", UINT64_C(0));
BUS_DEFINE_PROPERTY_GET_GLOBAL(bus_property_get_uint64_max, "t", UINT64_MAX);
int bus_property_get_bool(

View File

@ -107,4 +107,5 @@ int bus_property_get_pidfdid(sd_bus *bus, const char *path, const char *interfac
/* For deprecated properties. */
int bus_property_get_bool_false(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *reterr_error);
int bus_property_get_bool_true(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *reterr_error);
int bus_property_get_uint64_0(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *reterr_error);
int bus_property_get_uint64_max(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *reterr_error);

View File

@ -342,8 +342,6 @@ static int bus_append_parse_resource_limit(sd_bus_message *m, const char *field,
if (isempty(eq) || streq(eq, "infinity")) {
uint64_t x = streq(eq, "infinity") ? CGROUP_LIMIT_MAX :
STR_IN_SET(field,
"DefaultMemoryLow",
"DefaultMemoryMin",
"MemoryLow",
"MemoryMin") ? CGROUP_LIMIT_MIN : CGROUP_LIMIT_MAX;
@ -2348,8 +2346,6 @@ static const BusProperty cgroup_properties[] = {
{ "DisableControllers", bus_append_strv },
{ "Delegate", bus_append_parse_delegate },
{ "MemoryMin", bus_append_parse_resource_limit },
{ "DefaultMemoryLow", bus_append_parse_resource_limit },
{ "DefaultMemoryMin", bus_append_parse_resource_limit },
{ "MemoryLow", bus_append_parse_resource_limit },
{ "MemoryHigh", bus_append_parse_resource_limit },
{ "MemoryMax", bus_append_parse_resource_limit },
@ -2386,6 +2382,8 @@ static const BusProperty cgroup_properties[] = {
{ "BlockIOReadBandwidth", warn_deprecated },
{ "BlockIOWriteBandwidth", warn_deprecated },
{ "CPUAccounting", warn_deprecated },
{ "DefaultMemoryMin", warn_deprecated },
{ "DefaultMemoryLow", warn_deprecated },
{ NULL, bus_try_append_parse_cgroup_io_limit, cgroup_io_limits_list },
{}

View File

@ -7,6 +7,9 @@
#include "alloc-util.h"
#include "cryptsetup-util.h"
#include "dlfcn-util.h"
#include "escape.h"
#include "hexdecoct.h"
#include "hmac.h"
#include "log.h"
#include "parse-util.h"
#include "string-util.h"
@ -198,6 +201,64 @@ int cryptsetup_add_token_json(struct crypt_device *cd, sd_json_variant *v) {
return 0;
}
int cryptsetup_get_volume_key_prefix(
struct crypt_device *cd,
const char *volume_name,
char **ret) {
_cleanup_free_ char *volume = NULL;
const char *uuid;
char *s;
uuid = sym_crypt_get_uuid(cd);
if (!uuid)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to get LUKS UUID.");
if (volume_name)
/* avoid ambiguity around ":" once we join things below */
volume = xescape(volume_name, ":");
else
volume = strjoin("luks-", uuid);
if (!volume)
return log_oom_debug();
s = strjoin("cryptsetup:", volume, ":", uuid);
if (!s)
return log_oom_debug();
*ret = s;
return 0;
}
/* The hash must match what measure_volume_key() extends to the SHA256 bank of the TPM2. */
int cryptsetup_get_volume_key_id(
struct crypt_device *cd,
const char *volume_name,
const void *volume_key,
size_t volume_key_size,
char **ret) {
_cleanup_free_ char *prefix = NULL;
uint8_t digest[SHA256_DIGEST_SIZE];
char *hex;
int r;
r = cryptsetup_get_volume_key_prefix(cd, volume_name, &prefix);
if (r < 0)
return log_debug_errno(r, "Failed to get LUKS volume key prefix.");
hmac_sha256(volume_key, volume_key_size, prefix, strlen(prefix), digest);
hex = hexmem(digest, sizeof(digest));
if (!hex)
return log_oom_debug();
*ret = hex;
return 0;
}
#endif
int dlopen_cryptsetup(void) {

View File

@ -66,6 +66,9 @@ int cryptsetup_set_minimal_pbkdf(struct crypt_device *cd);
int cryptsetup_get_token_as_json(struct crypt_device *cd, int idx, const char *verify_type, sd_json_variant **ret);
int cryptsetup_add_token_json(struct crypt_device *cd, sd_json_variant *v);
int cryptsetup_get_volume_key_prefix(struct crypt_device *cd, const char *volume_name, char **ret);
int cryptsetup_get_volume_key_id(struct crypt_device *cd, const char *volume_name, const void *volume_key,
size_t volume_key_size, char **ret);
#endif
int dlopen_cryptsetup(void);

View File

@ -471,7 +471,7 @@ static int partition_is_luks2_integrity(int part_fd, uint64_t offset, uint64_t s
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read LUKS JSON header.");
json[sz] = '\0';
r = sd_json_parse(json, /* flags = */ 0, &v, /* reterr_line = */ NULL, /* reterr_column = */ NULL);
r = sd_json_parse(json, /* flags= */ 0, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse LUKS JSON header.");
@ -567,7 +567,7 @@ static int dissected_image_probe_filesystems(
m->encrypted = true;
if (p->mount_node_fd >= 0)
r = partition_is_luks2_integrity(p->mount_node_fd, /* offset = */ 0, /* size = */ UINT64_MAX);
r = partition_is_luks2_integrity(p->mount_node_fd, /* offset= */ 0, /* size= */ UINT64_MAX);
else
r = partition_is_luks2_integrity(fd, p->offset, p->size);
if (r < 0)
@ -1168,7 +1168,7 @@ static int dissect_image(
if (verity_settings_data_covers(verity, PARTITION_ROOT))
found_flags = iovec_is_set(&verity->root_hash_sig) ? PARTITION_POLICY_SIGNED : PARTITION_POLICY_VERITY;
else if (encrypted) {
r = partition_is_luks2_integrity(fd, /* offset = */ 0, /* size = */ UINT64_MAX);
r = partition_is_luks2_integrity(fd, /* offset= */ 0, /* size= */ UINT64_MAX);
if (r < 0)
return r;
@ -2383,7 +2383,7 @@ int partition_pick_mount_options(
case PARTITION_ESP:
case PARTITION_XBOOTLDR:
flags |= MS_NOSUID|MS_NOEXEC|ms_nosymfollow_supported();
flags |= MS_NOSUID|MS_NOEXEC|MS_NOSYMFOLLOW;
/* The ESP might contain a pre-boot random seed. Let's make this unaccessible to regular
* userspace. ESP/XBOOTLDR is almost certainly VFAT, hence if we don't know assume it is. */
@ -4631,9 +4631,7 @@ int mount_options_set_and_consume(MountOptions **options, PartitionDesignator d,
}
}
free_and_replace((*options)->options[d], s);
return 0;
return free_and_replace((*options)->options[d], s);
}
int mount_options_dup(const MountOptions *source, MountOptions **ret) {
@ -4644,7 +4642,7 @@ int mount_options_dup(const MountOptions *source, MountOptions **ret) {
options = new0(MountOptions, 1);
if (!options)
return log_oom();
return log_oom_debug();
for (PartitionDesignator d = 0; d < _PARTITION_DESIGNATOR_MAX; d++)
if (source->options[d]) {

View File

@ -1060,7 +1060,6 @@ static int fw_nftables_add_local_dnat_internal(
sd_netlink_message *messages[3] = {};
_unused_ _cleanup_(netlink_message_unref_manyp) sd_netlink_message **unref = messages;
static bool ipv6_supported = true;
uint32_t data[5], key[2], dlen;
size_t msgcnt = 0;
int r;
@ -1069,9 +1068,6 @@ static int fw_nftables_add_local_dnat_internal(
assert(add || !previous_remote);
assert(IN_SET(af, AF_INET, AF_INET6));
if (!ipv6_supported && af == AF_INET6)
return -EOPNOTSUPP;
if (!IN_SET(protocol, IPPROTO_TCP, IPPROTO_UDP))
return -EPROTONOSUPPORT;
@ -1125,14 +1121,6 @@ static int fw_nftables_add_local_dnat_internal(
assert(msgcnt < ELEMENTSOF(messages));
r = sd_nfnl_call_batch(nfnl, messages, msgcnt, NFNL_DEFAULT_TIMEOUT_USECS);
if (r == -EOVERFLOW && af == AF_INET6) {
/* The current implementation of DNAT in systemd requires kernel's
* fdb9c405e35bdc6e305b9b4e20ebc141ed14fc81 (v5.8), and the older kernel returns
* -EOVERFLOW. Let's treat the error as -EOPNOTSUPP. */
log_debug_errno(r, "The current implementation of IPv6 DNAT in systemd requires kernel 5.8 or newer, ignoring: %m");
ipv6_supported = false;
return -EOPNOTSUPP;
}
if (r < 0)
return r;

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
#include "dissect-image.h"
#include "extract-word.h"
#include "image-policy.h"
#include "log.h"
@ -176,6 +177,28 @@ PartitionPolicyFlags image_policy_get_exhaustively(const ImagePolicy *policy, Pa
return flags;
}
static PartitionPolicyFlags policy_flag_from_fstype(const char *s) {
if (!s)
return _PARTITION_POLICY_FLAGS_INVALID;
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;
}
static PartitionPolicyFlags policy_flag_from_string_one(const char *s) {
assert(s);
@ -207,22 +230,8 @@ 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;
return policy_flag_from_fstype(s);
}
PartitionPolicyFlags partition_policy_flags_from_string(const char *s, bool graceful) {
@ -275,6 +284,60 @@ static ImagePolicy* image_policy_new(size_t n_policies) {
return p;
}
ImagePolicy* image_policy_new_from_dissected(const DissectedImage *image, const VeritySettings *verity) {
assert(image);
ImagePolicy *image_policy = image_policy_new(_PARTITION_DESIGNATOR_MAX);
if (!image_policy)
return NULL;
/* Default to 'absent', only what we find is allowed to be used */
image_policy->default_flags = PARTITION_POLICY_ABSENT;
for (PartitionDesignator pd = 0; pd < _PARTITION_DESIGNATOR_MAX; pd++) {
PartitionPolicyFlags f = 0;
if (!image->partitions[pd].found)
f |= PARTITION_POLICY_ABSENT;
else {
if (streq_ptr(image->partitions[pd].fstype, "crypto_LUKS"))
f |= PARTITION_POLICY_ENCRYPTED;
else {
PartitionPolicyFlags fstype_flag = policy_flag_from_fstype(image->partitions[pd].fstype);
if (fstype_flag >= 0)
f |= fstype_flag;
}
if (!verity || verity->designator < 0 || verity->designator == pd) {
if (image->verity_sig_ready || (verity && iovec_is_set(&verity->root_hash_sig)))
f |= PARTITION_POLICY_SIGNED;
else if (image->verity_ready || (verity && iovec_is_set(&verity->root_hash)))
f |= PARTITION_POLICY_VERITY;
}
if (!image->single_file_system) {
if (image->partitions[pd].growfs)
f |= PARTITION_POLICY_GROWFS_ON;
else
f |= PARTITION_POLICY_GROWFS_OFF;
if (image->partitions[pd].rw)
f |= PARTITION_POLICY_READ_ONLY_OFF;
else
f |= PARTITION_POLICY_READ_ONLY_ON;
}
}
image_policy->policies[pd] = (PartitionPolicy) {
.designator = pd,
.flags = f,
};
image_policy->n_policies++;
}
return image_policy;
}
int image_policy_from_string(const char *s, bool graceful, ImagePolicy **ret) {
_cleanup_free_ ImagePolicy *p = NULL;
uint64_t dmask = 0;
@ -785,21 +848,35 @@ static bool partition_policy_flags_has_unspecified(PartitionPolicyFlags flags) {
return false;
}
int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret) {
static PartitionPolicyFlags policy_flags_or(PartitionPolicyFlags a, PartitionPolicyFlags b) {
return a | b;
}
static PartitionPolicyFlags policy_flags_and(PartitionPolicyFlags a, PartitionPolicyFlags b) {
return a & b;
}
static int policy_intersect_or_union(
const ImagePolicy *a,
const ImagePolicy *b,
PartitionPolicyFlags (*op)(PartitionPolicyFlags a, PartitionPolicyFlags b),
ImagePolicy **ret) {
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
/* Calculates the intersection of the specified policies, i.e. only what is permitted in both. This
* might fail with -ENAVAIL if the intersection is an "impossible policy". For example, if a root
* partition my neither be used, nor be absent, nor be unused then this is considered
* "impossible". */
assert(op);
/* Calculates the intersection or union of the specified policies, i.e. only what is permitted in
* both or either. This might fail with -ENAVAIL if the intersection is an "impossible policy". For
* example, if a root partition my neither be used, nor be absent, nor be unused then this is
* considered "impossible". */
p = image_policy_new(_PARTITION_DESIGNATOR_MAX);
if (!p)
return -ENOMEM;
p->default_flags =
partition_policy_flags_extend(image_policy_default(a)) &
partition_policy_flags_extend(image_policy_default(b));
p->default_flags = op(partition_policy_flags_extend(image_policy_default(a)),
partition_policy_flags_extend(image_policy_default(b)));
if (partition_policy_flags_has_unspecified(p->default_flags)) /* Intersection empty? */
return -ENAVAIL;
@ -824,10 +901,12 @@ int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePoli
return y;
/* Mask it */
z = x & y;
z = op(x, y);
/* Check if the intersection is empty for this partition. If so, generate a clear error */
if (partition_policy_flags_has_unspecified(z))
/* Check if the intersection is empty for this partition. If so, generate a clear error.
* If the partition has to be absent, then it won't have read-only/growfs flags, as
* image_policy_get_exhaustively() intentionally strips them, so skip the check. */
if (z != PARTITION_POLICY_ABSENT && partition_policy_flags_has_unspecified(z))
return -ENAVAIL;
df = partition_policy_normalized_flags(
@ -857,6 +936,14 @@ int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePoli
return 0;
}
int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret) {
return policy_intersect_or_union(a, b, policy_flags_and, ret);
}
int image_policy_union(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret) {
return policy_intersect_or_union(a, b, policy_flags_or, ret);
}
ImagePolicy* image_policy_free(ImagePolicy *p) {
return mfree(p);
}

View File

@ -2,6 +2,7 @@
#pragma once
#include "conf-parser-forward.h"
#include "dissect-image.h"
#include "shared-forward.h"
#include "gpt.h"
@ -111,7 +112,9 @@ bool image_policy_equal(const ImagePolicy *a, const ImagePolicy *b); /* ch
int image_policy_equivalent(const ImagePolicy *a, const ImagePolicy *b); /* checks if the outcome is the same, i.e. for all partitions results in the same decisions. */
int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret);
int image_policy_union(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret);
ImagePolicy* image_policy_new_from_dissected(const DissectedImage *image, const VeritySettings *verity);
ImagePolicy* image_policy_free(ImagePolicy *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(ImagePolicy*, image_policy_free);

View File

@ -349,7 +349,7 @@ int journal_importer_process_data(JournalImporter *imp) {
return 0;
}
/* replace \n with = */
/* replace '\n' with '=' */
line[n-1] = '=';
imp->field_len = n;

View File

@ -64,9 +64,6 @@ static bool is_in_survivor_cgroup(const PidRef *pid) {
}
r = cg_get_xattr_bool(cgroup_path, "user.survive_final_kill_signal");
/* user xattr support was added to kernel v5.7, try with the trusted namespace as a fallback */
if (ERRNO_IS_NEG_XATTR_ABSENT(r))
r = cg_get_xattr_bool(cgroup_path, "trusted.survive_final_kill_signal");
if (r < 0 && !ERRNO_IS_NEG_XATTR_ABSENT(r))
log_debug_errno(r,
"Failed to get survive_final_kill_signal xattr of %s, ignoring: %m",

View File

@ -1951,7 +1951,7 @@ int trigger_automount_at(int dir_fd, const char *path) {
unsigned long credentials_fs_mount_flags(bool ro) {
/* A tight set of mount flags for credentials mounts */
return MS_NODEV|MS_NOEXEC|MS_NOSUID|ms_nosymfollow_supported()|(ro ? MS_RDONLY : 0);
return MS_NODEV|MS_NOEXEC|MS_NOSUID|MS_NOSYMFOLLOW|(ro ? MS_RDONLY : 0);
}
int fsmount_credentials_fs(int *ret_fsfd) {

View File

@ -166,7 +166,7 @@ int make_mount_point_inode_from_path(const char *source, const char *dest, mode_
int trigger_automount_at(int dir_fd, const char *path);
unsigned long credentials_fs_mount_flags(bool ro);
unsigned long credentials_fs_mount_flags(bool ro) _const_;
int fsmount_credentials_fs(int *ret_fsfd);
int mount_credentials_fs(const char *path);

View File

@ -5441,6 +5441,11 @@ int tpm2_seal(Tpm2Context *c,
seal_key_handle);
primary_alg = primary_public->publicArea.type;
/* Propagate fixedTPM/fixedParent flags from sealing key to hmac key */
hmac_template.objectAttributes = (hmac_template.objectAttributes & ~(TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT)) |
(primary_public->publicArea.objectAttributes & (TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT));
} else {
if (seal_key_handle != 0)
log_debug("Using primary alg sealing, but seal key handle also provided; ignoring seal key handle.");

View File

@ -151,7 +151,7 @@ typedef enum Tpm2UserspaceEventType {
DECLARE_STRING_TABLE_LOOKUP(tpm2_userspace_event_type, Tpm2UserspaceEventType);
int tpm2_pcr_extend_bytes(Tpm2Context *c, char **banks, unsigned pcr_index, const struct iovec *data, const struct iovec *secret, Tpm2UserspaceEventType event, const char *description);
int tpm2_pcr_extend_bytes(Tpm2Context *c, char **banks, unsigned pcr_index, const struct iovec *data, const struct iovec *secret, Tpm2UserspaceEventType event_type, const char *description);
int tpm2_nvpcr_get_index(const char *name, uint32_t *ret);
int tpm2_nvpcr_extend_bytes(Tpm2Context *c, const Tpm2Handle *session, const char *name, const struct iovec *data, const struct iovec *secret, Tpm2UserspaceEventType event_type, const char *description);
int tpm2_nvpcr_acquire_anchor_secret(struct iovec *ret, bool sync_secondary);

View File

@ -111,15 +111,9 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.resource-control.html#MemoryMin=bytes,%20MemoryLow=bytes"),
SD_VARLINK_DEFINE_FIELD(MemoryMin, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.resource-control.html#MemoryMin=bytes,%20MemoryLow=bytes"),
SD_VARLINK_DEFINE_FIELD(DefaultMemoryMin, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.resource-control.html#MemoryMin=bytes,%20MemoryLow=bytes"),
SD_VARLINK_DEFINE_FIELD(MemoryLow, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.resource-control.html#MemoryMin=bytes,%20MemoryLow=bytes"),
SD_VARLINK_DEFINE_FIELD(DefaultMemoryLow, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.resource-control.html#MemorySwapMax=bytes"),
SD_VARLINK_DEFINE_FIELD(StartupMemoryLow, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.resource-control.html#MemoryMin=bytes,%20MemoryLow=bytes"),
SD_VARLINK_DEFINE_FIELD(DefaultStartupMemoryLow, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.resource-control.html#MemoryHigh=bytes"),
SD_VARLINK_DEFINE_FIELD(MemoryHigh, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.resource-control.html#MemoryHigh=bytes"),

View File

@ -302,10 +302,6 @@ typedef struct UnitStatusInfo {
uint64_t io_read_bytes;
uint64_t io_write_bytes;
uint64_t default_memory_min;
uint64_t default_memory_low;
uint64_t default_startup_memory_low;
/* Exec Quotas */
QuotaInfo exec_directories_quota[_EXEC_DIRECTORY_TYPE_MAX];
@ -2248,9 +2244,6 @@ static int show_one(
{ "MemorySwapPeak", "t", NULL, offsetof(UnitStatusInfo, memory_swap_peak) },
{ "MemoryZSwapCurrent", "t", NULL, offsetof(UnitStatusInfo, memory_zswap_current) },
{ "MemoryAvailable", "t", NULL, offsetof(UnitStatusInfo, memory_available) },
{ "DefaultMemoryMin", "t", NULL, offsetof(UnitStatusInfo, default_memory_min) },
{ "DefaultMemoryLow", "t", NULL, offsetof(UnitStatusInfo, default_memory_low) },
{ "DefaultStartupMemoryLow", "t", NULL, offsetof(UnitStatusInfo, default_startup_memory_low) },
{ "MemoryMin", "t", NULL, offsetof(UnitStatusInfo, memory_min) },
{ "MemoryLow", "t", NULL, offsetof(UnitStatusInfo, memory_low) },
{ "StartupMemoryLow", "t", NULL, offsetof(UnitStatusInfo, startup_memory_low) },

View File

@ -540,9 +540,6 @@ executables += [
'sources' : files('test-cgroup-mask.c'),
'dependencies' : common_test_dependencies,
},
core_test_template + {
'sources' : files('test-cgroup-unit-default.c'),
},
core_test_template + {
'sources' : files('test-chown-rec.c'),
},

View File

@ -1,136 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "cgroup.h"
#include "manager.h"
#include "rm-rf.h"
#include "tests.h"
#include "unit.h"
TEST_RET(default_memory_low, .sd_booted = true) {
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
_cleanup_(manager_freep) Manager *m = NULL;
Unit *root, *dml,
*dml_passthrough, *dml_passthrough_empty, *dml_passthrough_set_dml, *dml_passthrough_set_ml,
*dml_override, *dml_override_empty,
*dml_discard, *dml_discard_empty, *dml_discard_set_ml;
uint64_t dml_tree_default;
int r;
r = enter_cgroup_subroot(NULL);
if (r == -ENOMEDIUM)
return log_tests_skipped("cgroupfs not available");
_cleanup_free_ char *unit_dir = NULL;
ASSERT_OK(get_testdata_dir("test-cgroup-unit-default", &unit_dir));
ASSERT_OK(setenv_unit_path(unit_dir));
assert_se(runtime_dir = setup_fake_runtime_dir());
r = manager_new(RUNTIME_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m);
if (IN_SET(r, -EPERM, -EACCES)) {
log_error_errno(r, "manager_new: %m");
return log_tests_skipped("cannot create manager");
}
ASSERT_OK(r);
ASSERT_OK(manager_startup(m, NULL, NULL, NULL));
/* dml.slice has DefaultMemoryLow=50. Beyond that, individual subhierarchies look like this:
*
* 1. dml-passthrough.slice sets MemoryLow=100. This should not affect its children, as only
* DefaultMemoryLow is propagated, not MemoryLow. As such, all leaf services should end up with
* memory.low as 50, inherited from dml.slice, *except* for dml-passthrough-set-ml.service, which
* should have the value of 0, as it has MemoryLow explicitly set.
*
*
* dml.slice
*
* MemoryLow=100
*
* dml-passthrough.slice
*
*
* no new settings DefaultMemoryLow=15 MemoryLow=0
*
* dml-passthrough-empty.service dml-passthrough-set-dml.service dml-passthrough-set-ml.service
*
*
* 2. dml-override.slice sets DefaultMemoryLow=10. As such, dml-override-empty.service should also
* end up with a memory.low of 10. dml-override.slice should still have a memory.low of 50.
*
*
* dml.slice
*
* DefaultMemoryLow=10
*
* dml-override.slice
*
* no new settings
*
* dml-override-empty.service
*
*
* 3. dml-discard.slice sets DefaultMemoryLow= with no rvalue. As such,
* dml-discard-empty.service should end up with a value of 0.
* dml-discard-set-ml.service sets MemoryLow=15, and as such should have that override the
* reset DefaultMemoryLow value. dml-discard.slice should still have an eventual memory.low of 50.
*
*
* dml.slice
*
* DefaultMemoryLow=
*
* dml-discard.slice
*
*
* no new settings MemoryLow=15
*
* dml-discard-empty.service dml-discard-set-ml.service
*
*/
ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml.slice", NULL, &dml));
ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough.slice", NULL, &dml_passthrough));
assert_se(UNIT_GET_SLICE(dml_passthrough) == dml);
ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough-empty.service", NULL, &dml_passthrough_empty));
assert_se(UNIT_GET_SLICE(dml_passthrough_empty) == dml_passthrough);
ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-dml.service", NULL, &dml_passthrough_set_dml));
assert_se(UNIT_GET_SLICE(dml_passthrough_set_dml) == dml_passthrough);
ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-ml.service", NULL, &dml_passthrough_set_ml));
assert_se(UNIT_GET_SLICE(dml_passthrough_set_ml) == dml_passthrough);
ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-override.slice", NULL, &dml_override));
assert_se(UNIT_GET_SLICE(dml_override) == dml);
ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-override-empty.service", NULL, &dml_override_empty));
assert_se(UNIT_GET_SLICE(dml_override_empty) == dml_override);
ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-discard.slice", NULL, &dml_discard));
assert_se(UNIT_GET_SLICE(dml_discard) == dml);
ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-discard-empty.service", NULL, &dml_discard_empty));
assert_se(UNIT_GET_SLICE(dml_discard_empty) == dml_discard);
ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-discard-set-ml.service", NULL, &dml_discard_set_ml));
assert_se(UNIT_GET_SLICE(dml_discard_set_ml) == dml_discard);
assert_se(root = UNIT_GET_SLICE(dml));
assert_se(!UNIT_GET_SLICE(root));
assert_se(unit_get_ancestor_memory_low(root) == CGROUP_LIMIT_MIN);
assert_se(unit_get_ancestor_memory_low(dml) == CGROUP_LIMIT_MIN);
dml_tree_default = unit_get_cgroup_context(dml)->default_memory_low;
assert_se(dml_tree_default == 50);
assert_se(unit_get_ancestor_memory_low(dml_passthrough) == 100);
assert_se(unit_get_ancestor_memory_low(dml_passthrough_empty) == dml_tree_default);
assert_se(unit_get_ancestor_memory_low(dml_passthrough_set_dml) == 50);
assert_se(unit_get_ancestor_memory_low(dml_passthrough_set_ml) == 0);
assert_se(unit_get_ancestor_memory_low(dml_override) == dml_tree_default);
assert_se(unit_get_ancestor_memory_low(dml_override_empty) == 10);
assert_se(unit_get_ancestor_memory_low(dml_discard) == dml_tree_default);
assert_se(unit_get_ancestor_memory_low(dml_discard_empty) == CGROUP_LIMIT_MIN);
assert_se(unit_get_ancestor_memory_low(dml_discard_set_ml) == 15);
return 0;
}
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -142,14 +142,17 @@ TEST(extend) {
assert_se(partition_policy_flags_extend(PARTITION_POLICY_GROWFS_ON) == (PARTITION_POLICY_GROWFS_ON|_PARTITION_POLICY_USE_MASK|_PARTITION_POLICY_READ_ONLY_MASK));
}
static void test_policy_intersect_one(const char *a, const char *b, const char *c) {
static void test_policy_intersect_one(const char *a, const char *b, const char *c, bool intersect) {
_cleanup_(image_policy_freep) ImagePolicy *x = NULL, *y = NULL, *z = NULL, *t = NULL;
assert_se(image_policy_from_string(a, /* graceful= */ false, &x) >= 0);
assert_se(image_policy_from_string(b, /* graceful= */ false, &y) >= 0);
assert_se(image_policy_from_string(c, /* graceful= */ false, &z) >= 0);
assert_se(image_policy_intersect(x, y, &t) >= 0);
if (intersect)
assert_se(image_policy_intersect(x, y, &t) >= 0);
else
assert_se(image_policy_union(x, y, &t) >= 0);
_cleanup_free_ char *s1 = NULL, *s2 = NULL, *s3 = NULL, *s4 = NULL;
assert_se(image_policy_to_string(x, false, &s1) >= 0);
@ -157,21 +160,33 @@ static void test_policy_intersect_one(const char *a, const char *b, const char *
assert_se(image_policy_to_string(z, false, &s3) >= 0);
assert_se(image_policy_to_string(t, false, &s4) >= 0);
log_info("%s ^ %s → %s vs. %s", s1, s2, s3, s4);
log_info("%s %s %s → %s vs. %s", s1, intersect ? "^" : "U", s2, s3, s4);
assert_se(image_policy_equivalent(z, t) > 0);
}
TEST(image_policy_intersect) {
test_policy_intersect_one("", "", "");
test_policy_intersect_one("-", "-", "-");
test_policy_intersect_one("*", "*", "*");
test_policy_intersect_one("~", "~", "~");
test_policy_intersect_one("root=verity+signed", "root=signed+verity", "root=verity+signed");
test_policy_intersect_one("root=verity+signed", "root=signed", "root=signed");
test_policy_intersect_one("root=verity+signed", "root=verity", "root=verity");
test_policy_intersect_one("root=open", "root=verity", "root=verity");
test_policy_intersect_one("root=open", "=verity+ignore", "root=verity+ignore:=ignore");
test_policy_intersect_one("", "", "", /* intersect= */ true);
test_policy_intersect_one("-", "-", "-", /* intersect= */ true);
test_policy_intersect_one("*", "*", "*", /* intersect= */ true);
test_policy_intersect_one("~", "~", "~", /* intersect= */ true);
test_policy_intersect_one("root=verity+signed", "root=signed+verity", "root=verity+signed", /* intersect= */ true);
test_policy_intersect_one("root=verity+signed", "root=signed", "root=signed", /* intersect= */ true);
test_policy_intersect_one("root=verity+signed", "root=verity", "root=verity", /* intersect= */ true);
test_policy_intersect_one("root=open", "root=verity", "root=verity", /* intersect= */ true);
test_policy_intersect_one("root=open", "=verity+ignore", "root=verity+ignore:=ignore", /* intersect= */ true);
}
TEST(image_policy_union) {
test_policy_intersect_one("root=verity+signed", "root=signed+verity", "root=verity+signed", /* intersect= */ false);
test_policy_intersect_one("root=verity+signed", "root=signed", "root=verity+signed", /* intersect= */ false);
test_policy_intersect_one("root=verity+signed", "root=verity", "root=verity+signed", /* intersect= */ false);
test_policy_intersect_one("root=signed", "root=verity", "root=verity+signed", /* intersect= */ false);
test_policy_intersect_one("root=signed:=absent", "root=verity:=unused", "root=verity+signed", /* intersect= */ false);
test_policy_intersect_one("root=open", "root=verity", "root=open", /* intersect= */ false);
test_policy_intersect_one("root=open", "=verity+ignore", "root=open:=verity+ignore", /* intersect= */ false);
test_policy_intersect_one("root=open:usr=absent", "root=open:usr=absent", "root=open:usr=absent", /* intersect= */ false);
}
static void test_policy_ignore_designators_one(const char *a, const PartitionDesignator array[], size_t n, const char *b) {
@ -265,4 +280,382 @@ TEST(partition_policy_determine_fstype) {
ASSERT_FALSE(encrypted);
}
TEST(image_policy_new_from_dissected) {
_cleanup_(image_policy_freep) ImagePolicy *policy = NULL;
DissectedImage image;
VeritySettings verity;
uint8_t dummy_data[4];
/* Test 1: Empty image - all partitions should be absent */
image = (DissectedImage) {};
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
ASSERT_NOT_NULL(policy);
ASSERT_EQ(image_policy_default(policy), PARTITION_POLICY_ABSENT);
ASSERT_EQ(image_policy_n_entries(policy), (size_t) _PARTITION_DESIGNATOR_MAX);
/* All partitions should have PARTITION_POLICY_ABSENT */
for (PartitionDesignator pd = 0; pd < _PARTITION_DESIGNATOR_MAX; pd++)
ASSERT_EQ(policy->policies[pd].flags, (PartitionPolicyFlags) PARTITION_POLICY_ABSENT);
policy = image_policy_free(policy);
/* Test 2: Image with a single ext4 root partition */
image = (DissectedImage) {
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = (char*) "ext4",
},
},
};
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
ASSERT_NOT_NULL(policy);
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EXT4 | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
ASSERT_EQ(policy->policies[PARTITION_USR].flags, (PartitionPolicyFlags) PARTITION_POLICY_ABSENT);
policy = image_policy_free(policy);
/* Test 3: Image with encrypted root partition (LUKS) */
image = (DissectedImage) {
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = (char*) "crypto_LUKS",
},
},
};
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
ASSERT_NOT_NULL(policy);
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_ENCRYPTED | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
policy = image_policy_free(policy);
/* Test 4: Image with verity ready (without signature) */
image = (DissectedImage) {
.verity_ready = true,
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = (char*) "squashfs",
},
},
};
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
ASSERT_NOT_NULL(policy);
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_SQUASHFS | PARTITION_POLICY_VERITY | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
policy = image_policy_free(policy);
/* Test 5: Image with verity signature ready */
image = (DissectedImage) {
.verity_sig_ready = true,
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = (char*) "erofs",
},
},
};
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
ASSERT_NOT_NULL(policy);
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EROFS | PARTITION_POLICY_SIGNED | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
policy = image_policy_free(policy);
/* Test 6: Image with growfs enabled */
image = (DissectedImage) {
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = (char*) "btrfs",
.growfs = true,
},
},
};
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
ASSERT_NOT_NULL(policy);
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_BTRFS | PARTITION_POLICY_GROWFS_ON | PARTITION_POLICY_READ_ONLY_ON));
policy = image_policy_free(policy);
/* Test 7: Multiple partitions with different filesystems */
image = (DissectedImage) {
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = (char*) "ext4",
},
[PARTITION_USR] = {
.found = true,
.fstype = (char*) "xfs",
},
[PARTITION_HOME] = {
.found = true,
.fstype = (char*) "btrfs",
.growfs = true,
},
[PARTITION_ESP] = {
.found = true,
.fstype = (char*) "vfat",
},
},
};
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
ASSERT_NOT_NULL(policy);
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EXT4 | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
ASSERT_EQ(policy->policies[PARTITION_USR].flags, (PartitionPolicyFlags) (PARTITION_POLICY_XFS | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
ASSERT_EQ(policy->policies[PARTITION_HOME].flags, (PartitionPolicyFlags) (PARTITION_POLICY_BTRFS | PARTITION_POLICY_GROWFS_ON | PARTITION_POLICY_READ_ONLY_ON));
ASSERT_EQ(policy->policies[PARTITION_ESP].flags, (PartitionPolicyFlags) (PARTITION_POLICY_VFAT | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
ASSERT_EQ(policy->policies[PARTITION_SWAP].flags, (PartitionPolicyFlags) PARTITION_POLICY_ABSENT);
policy = image_policy_free(policy);
/* Test 8: VeritySettings with root_hash set (no signature) */
dummy_data[0] = 0xde; dummy_data[1] = 0xad; dummy_data[2] = 0xbe; dummy_data[3] = 0xef;
verity = (VeritySettings) {
.designator = _PARTITION_DESIGNATOR_INVALID,
.root_hash = IOVEC_MAKE(dummy_data, sizeof(dummy_data)),
};
image = (DissectedImage) {
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = (char*) "squashfs",
},
},
};
policy = image_policy_new_from_dissected(&image, &verity);
ASSERT_NOT_NULL(policy);
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_SQUASHFS | PARTITION_POLICY_VERITY | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
policy = image_policy_free(policy);
/* Test 9: VeritySettings with root_hash_sig set */
dummy_data[0] = 0x01; dummy_data[1] = 0x02; dummy_data[2] = 0x03; dummy_data[3] = 0x04;
verity = (VeritySettings) {
.designator = _PARTITION_DESIGNATOR_INVALID,
.root_hash_sig = IOVEC_MAKE(dummy_data, sizeof(dummy_data)),
};
image = (DissectedImage) {
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = (char*) "erofs",
},
},
};
policy = image_policy_new_from_dissected(&image, &verity);
ASSERT_NOT_NULL(policy);
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EROFS | PARTITION_POLICY_SIGNED | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
policy = image_policy_free(policy);
/* Test 10: VeritySettings with designator targeting specific partition */
dummy_data[0] = 0xab; dummy_data[1] = 0xcd;
verity = (VeritySettings) {
.designator = PARTITION_USR,
.root_hash = IOVEC_MAKE(dummy_data, 2),
};
image = (DissectedImage) {
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = (char*) "ext4",
},
[PARTITION_USR] = {
.found = true,
.fstype = (char*) "squashfs",
},
},
};
policy = image_policy_new_from_dissected(&image, &verity);
ASSERT_NOT_NULL(policy);
/* Root should NOT have verity since verity targets USR */
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EXT4 | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
/* USR should have verity */
ASSERT_EQ(policy->policies[PARTITION_USR].flags, (PartitionPolicyFlags) (PARTITION_POLICY_SQUASHFS | PARTITION_POLICY_VERITY | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
policy = image_policy_free(policy);
/* Test 11: Unknown filesystem type (should have no fstype flag) */
image = (DissectedImage) {
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = (char*) "unknown_fs",
},
},
};
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
ASSERT_NOT_NULL(policy);
/* Should only have GROWFS_OFF and READ_ONLY_ON for unknown filesystem */
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
policy = image_policy_free(policy);
/* Test 12: NULL filesystem type (should have no fstype flag) */
image = (DissectedImage) {
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = NULL,
},
},
};
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
ASSERT_NOT_NULL(policy);
/* Should only have GROWFS_OFF and READ_ONLY_ON for NULL fstype */
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
policy = image_policy_free(policy);
/* Test 13: Combination of verity_ready from image and verity settings - image takes precedence for sig */
dummy_data[0] = 0x11; dummy_data[1] = 0x22;
verity = (VeritySettings) {
.designator = _PARTITION_DESIGNATOR_INVALID,
.root_hash = IOVEC_MAKE(dummy_data, 2),
};
image = (DissectedImage) {
.verity_sig_ready = true, /* This should take precedence */
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = (char*) "squashfs",
},
},
};
policy = image_policy_new_from_dissected(&image, &verity);
ASSERT_NOT_NULL(policy);
/* verity_sig_ready should result in SIGNED, not just VERITY */
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_SQUASHFS | PARTITION_POLICY_SIGNED | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
policy = image_policy_free(policy);
/* Test 14: All known filesystem types */
image = (DissectedImage) {
.partitions = {
[PARTITION_ROOT] = { .found = true, .fstype = (char*) "ext4" },
[PARTITION_USR] = { .found = true, .fstype = (char*) "btrfs" },
[PARTITION_HOME] = { .found = true, .fstype = (char*) "xfs" },
[PARTITION_SRV] = { .found = true, .fstype = (char*) "f2fs" },
[PARTITION_VAR] = { .found = true, .fstype = (char*) "erofs" },
[PARTITION_TMP] = { .found = true, .fstype = (char*) "squashfs" },
[PARTITION_ESP] = { .found = true, .fstype = (char*) "vfat" },
},
};
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
ASSERT_NOT_NULL(policy);
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EXT4 | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
ASSERT_EQ(policy->policies[PARTITION_USR].flags, (PartitionPolicyFlags) (PARTITION_POLICY_BTRFS | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
ASSERT_EQ(policy->policies[PARTITION_HOME].flags, (PartitionPolicyFlags) (PARTITION_POLICY_XFS | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
ASSERT_EQ(policy->policies[PARTITION_SRV].flags, (PartitionPolicyFlags) (PARTITION_POLICY_F2FS | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
ASSERT_EQ(policy->policies[PARTITION_VAR].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EROFS | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
ASSERT_EQ(policy->policies[PARTITION_TMP].flags, (PartitionPolicyFlags) (PARTITION_POLICY_SQUASHFS | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
ASSERT_EQ(policy->policies[PARTITION_ESP].flags, (PartitionPolicyFlags) (PARTITION_POLICY_VFAT | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
policy = image_policy_free(policy);
/* Test 15: Encrypted partition with verity (LUKS takes precedence, no verity flag) */
image = (DissectedImage) {
.verity_ready = true,
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = (char*) "crypto_LUKS",
},
},
};
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
ASSERT_NOT_NULL(policy);
/* crypto_LUKS check happens first, then verity is added */
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_ENCRYPTED | PARTITION_POLICY_VERITY | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
policy = image_policy_free(policy);
/* Test 16: Multiple flags combined - encrypted + growfs */
image = (DissectedImage) {
.partitions = {
[PARTITION_HOME] = {
.found = true,
.fstype = (char*) "crypto_LUKS",
.growfs = true,
},
},
};
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
ASSERT_NOT_NULL(policy);
ASSERT_EQ(policy->policies[PARTITION_HOME].flags, (PartitionPolicyFlags) (PARTITION_POLICY_ENCRYPTED | PARTITION_POLICY_GROWFS_ON | PARTITION_POLICY_READ_ONLY_ON));
policy = image_policy_free(policy);
/* Test 17: single_file_system=true should NOT set growfs or read-only flags */
image = (DissectedImage) {
.single_file_system = true,
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = (char*) "ext4",
.rw = true,
.growfs = true,
},
},
};
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
ASSERT_NOT_NULL(policy);
/* single_file_system=true means no growfs/read-only flags */
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) PARTITION_POLICY_EXT4);
policy = image_policy_free(policy);
/* Test 18: rw=true should set READ_ONLY_OFF instead of READ_ONLY_ON */
image = (DissectedImage) {
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = (char*) "ext4",
.rw = true,
},
},
};
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
ASSERT_NOT_NULL(policy);
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EXT4 | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_OFF));
policy = image_policy_free(policy);
/* Test 19: rw=true with growfs=true */
image = (DissectedImage) {
.partitions = {
[PARTITION_ROOT] = {
.found = true,
.fstype = (char*) "btrfs",
.rw = true,
.growfs = true,
},
},
};
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
ASSERT_NOT_NULL(policy);
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_BTRFS | PARTITION_POLICY_GROWFS_ON | PARTITION_POLICY_READ_ONLY_OFF));
}
DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -359,10 +359,6 @@ TEST(is_mount_point_at) {
ASSERT_OK(is_mount_point_at(fd, "regular", 0));
}
TEST(ms_nosymfollow_supported) {
log_info("MS_NOSYMFOLLOW supported: %s", yes_no(ms_nosymfollow_supported()));
}
TEST(mount_option_supported) {
int r;

View File

@ -537,4 +537,38 @@ TEST(enums_idl) {
TEST_IDL_ENUM(ResolveSupport, resolve_support, vl_type_ResolveSupport);
}
static SD_VARLINK_DEFINE_METHOD(
AnyTestStrict,
SD_VARLINK_DEFINE_INPUT(foo, SD_VARLINK_ANY, 0),
SD_VARLINK_DEFINE_INPUT(foo2, SD_VARLINK_ANY, 0),
SD_VARLINK_DEFINE_INPUT(foo3, SD_VARLINK_ANY, 0),
SD_VARLINK_DEFINE_INPUT(foo4, SD_VARLINK_ANY, 0));
static SD_VARLINK_DEFINE_METHOD(
AnyTestNullable,
SD_VARLINK_DEFINE_INPUT(foo, SD_VARLINK_ANY, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(foo2, SD_VARLINK_ANY, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(foo3, SD_VARLINK_ANY, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(foo4, SD_VARLINK_ANY, SD_VARLINK_NULLABLE));
TEST(any) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
ASSERT_OK(sd_json_buildo(&v,
SD_JSON_BUILD_PAIR_STRING("foo", "bar"),
SD_JSON_BUILD_PAIR_INTEGER("foo2", 47),
SD_JSON_BUILD_PAIR_NULL("foo3"),
SD_JSON_BUILD_PAIR_BOOLEAN("foo4", true)));
/* "any" shall mean any type but null */
const char *bad_field = NULL;
ASSERT_ERROR(varlink_idl_validate_method_call(&vl_method_AnyTestStrict, v, /* flags= */ 0, &bad_field), ENOANO);
ASSERT_STREQ(bad_field, "foo3");
/* "any?" shall many truly any type */
bad_field = NULL;
ASSERT_OK(varlink_idl_validate_method_call(&vl_method_AnyTestNullable, v, /* flags= */ 0, &bad_field));
ASSERT_NULL(bad_field);
}
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -2408,7 +2408,8 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
if (r < 0)
return log_oom();
FOREACH_ARRAY(mount, arg_runtime_mounts.mounts, arg_runtime_mounts.n_mounts) {
for (size_t j = 0; j < arg_runtime_mounts.n_mounts; j++) {
RuntimeMount *m = arg_runtime_mounts.mounts + j;
_cleanup_free_ char *listen_address = NULL;
_cleanup_(fork_notify_terminate) PidRef child = PIDREF_NULL;
@ -2417,9 +2418,9 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
r = start_virtiofsd(
unit,
mount->source,
/* source_uid= */ mount->source_uid,
/* target_uid= */ mount->target_uid,
m->source,
/* source_uid= */ m->source_uid,
/* target_uid= */ m->target_uid,
/* uid_range= */ 1U,
runtime_dir,
sd_socket_activate,
@ -2444,7 +2445,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
return log_oom();
_cleanup_free_ char *id = NULL;
if (asprintf(&id, "mnt%zi", mount - arg_runtime_mounts.mounts) < 0)
if (asprintf(&id, "mnt%zu", j) < 0)
return log_oom();
if (strv_extendf(&cmdline, "socket,id=%s,path=%s", id, escaped_listen_address) < 0)
@ -2456,12 +2457,12 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
if (strv_extendf(&cmdline, "vhost-user-fs-pci,queue-size=1024,chardev=%1$s,tag=%1$s", id) < 0)
return log_oom();
_cleanup_free_ char *clean_target = xescape(mount->target, "\":");
_cleanup_free_ char *clean_target = xescape(m->target, "\":");
if (!clean_target)
return log_oom();
if (strv_extendf(&arg_kernel_cmdline_extra, "systemd.mount-extra=\"%s:%s:virtiofs:%s\"",
id, clean_target, mount->read_only ? "ro" : "rw") < 0)
id, clean_target, m->read_only ? "ro" : "rw") < 0)
return log_oom();
}

View File

@ -1,9 +1,10 @@
/*/* -whitespace
/fuzz-bus-match/* binary
/fuzz-compress/* binary
/fuzz-dhcp*/* binary
/fuzz-dns-packet/* binary
/fuzz-fido-id-desc/ binary
/fuzz-lldp-rx/* binary
/fuzz-ndisc-rs/* binary
/*/* generated
/*/* -whitespace
/fuzz-bus-match/* binary
/fuzz-compress/* binary
/fuzz-dhcp*/* binary
/fuzz-dns-packet/* binary
/fuzz-fido-id-desc/* binary
/fuzz-journald-audit/* binary
/fuzz-lldp-rx/* binary
/fuzz-ndisc-rs/* binary
/*/* generated

File diff suppressed because one or more lines are too long

View File

@ -332,7 +332,6 @@ if install_tests
'journal-data',
'knot-data',
'test-cgroup-mask',
'test-cgroup-unit-default',
'test-engine',
'test-execute',
'test-fstab-generator',

View File

@ -1,8 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML discard empty service
[Service]
Slice=dml-discard.slice
Type=oneshot
ExecStart=true

View File

@ -1,9 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML discard set ml service
[Service]
Slice=dml-discard.slice
Type=oneshot
ExecStart=true
MemoryLow=15

View File

@ -1,6 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML discard slice
[Slice]
DefaultMemoryLow=

View File

@ -1,8 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML override empty service
[Service]
Slice=dml-override.slice
Type=oneshot
ExecStart=true

View File

@ -1,6 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML override slice
[Slice]
DefaultMemoryLow=10

View File

@ -1,8 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML passthrough empty service
[Service]
Slice=dml-passthrough.slice
Type=oneshot
ExecStart=true

View File

@ -1,9 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML passthrough set DML service
[Service]
Slice=dml-passthrough.slice
Type=oneshot
ExecStart=true
DefaultMemoryLow=15

View File

@ -1,9 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML passthrough set ML service
[Service]
Slice=dml-passthrough.slice
Type=oneshot
ExecStart=true
MemoryLow=0

View File

@ -1,6 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML passthrough slice
[Slice]
MemoryLow=100

View File

@ -1,6 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML slice
[Slice]
DefaultMemoryLow=50

View File

@ -25,6 +25,10 @@ systemctl is-active minimal-app0.service
systemctl is-active minimal-app0-foo.service
systemctl is-active minimal-app0-bar.service && exit 1
# Ensure pinning by policy works
cat /run/systemd/system.attached/minimal-app0-foo.service.d/20-portable.conf
grep -q -F 'root=signed+squashfs:' /run/systemd/system.attached/minimal-app0-foo.service.d/20-portable.conf
portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0
portablectl is-attached minimal-app0
@ -91,6 +95,9 @@ status="$(portablectl is-attached --extension app0 minimal_0)"
grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
# Ensure pinning by policy works
grep -q -F 'RootImagePolicy=root=signed+squashfs:' /run/systemd/system.attached/app0.service.d/20-portable.conf >/dev/null
grep -q -F 'ExtensionImagePolicy=root=signed+squashfs:' /run/systemd/system.attached/app0.service.d/20-portable.conf >/dev/null
portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0

View File

@ -1942,6 +1942,61 @@ EOF
cmp "$imgs/test1.img" "$imgs/test2.img"
}
testcase_luks2_keyhash() {
local defs imgs output root
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
root="$(mktemp --directory "/var/test-repart.root.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs' '$root'" RETURN
chmod 0755 "$defs"
echo "*** testcase for fixate-volume-key ***"
volume="test-repart-lukskeyhash-$RANDOM"
tee "$defs/root.conf" <<EOF
[Partition]
Type=linux-generic
Format=ext4
Encrypt=key-file
EncryptedVolume=$volume:::fixate-volume-key
EOF
systemd-repart --pretty=yes \
--definitions "$defs" \
--empty=create \
--size=100M \
--seed="$seed" \
--dry-run=no \
--offline="$OFFLINE" \
--generate-crypttab="$imgs/crypttab" \
"$imgs/enckeyhash.img"
loop="$(losetup -P --show --find "$imgs/enckeyhash.img")"
udevadm wait --timeout=60 --settle "${loop:?}p1"
touch "$imgs/empty-password"
# Check that the volume can be attached with the correct hash
expected_hash="$(grep UUID= "$imgs/crypttab" | sed s,.*fixate-volume-key=,,)"
echo "Expected hash: $expected_hash"
echo "Trying to attach the volume"
systemd-cryptsetup attach $volume "${loop}p1" "$imgs/empty-password" "fixate-volume-key=$expected_hash"
echo "Trying to detach the volume"
systemd-cryptsetup detach $volume
echo "Success!"
# Check that the volume cannot be attached with incorrect hash
echo "Trying to attach the volume with wrong hash"
systemd-cryptsetup attach $volume "${loop}p1" "$imgs/empty-password" "fixate-volume-key=aaaaaabbbbbbccccccddddddeeeeeeffffff1111112222223333334444445555" && exit 1
# Verify the volume is not attached
[ ! -f "/dev/mapper/$volume" ] || exit 1
losetup -d "$loop"
}
OFFLINE="yes"
run_testcases