]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #25389 from fbuihuu/update-test-for-opensuse
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 18 Nov 2022 20:58:51 +0000 (05:58 +0900)
committerGitHub <noreply@github.com>
Fri, 18 Nov 2022 20:58:51 +0000 (05:58 +0900)
Update test/ for openSUSE

199 files changed:
.github/codeql-queries/UninitializedVariableWithCleanup.ql
.github/workflows/mkosi.yml
NEWS
README
TODO
docs/BOOT_LOADER_INTERFACE.md
docs/ENVIRONMENT.md
docs/RANDOM_SEEDS.md
hwdb.d/60-keyboard.hwdb
man/hostnamectl.xml
man/org.freedesktop.systemd1.xml
man/repart.d.xml
man/systemd-boot.xml
man/systemd-dissect.xml
man/systemd-measure.xml
man/systemd-repart.xml
man/systemd.resource-control.xml
meson.build
mkosi.build
mkosi.conf.d/10-systemd.conf
mkosi.conf.d/arch/10-mkosi.arch
mkosi.conf.d/debian/10-mkosi.debian
mkosi.conf.d/fedora/10-mkosi.fedora
mkosi.conf.d/opensuse/10-mkosi.opensuse
mkosi.conf.d/ubuntu/10-mkosi.ubuntu
mkosi.postinst
po/fi.po
src/analyze/analyze-calendar.c
src/analyze/analyze-cat-config.c
src/analyze/analyze-filesystems.c
src/analyze/analyze-inspect-elf.c
src/analyze/analyze-security.c
src/analyze/analyze-syscall-filter.c
src/analyze/analyze-timespan.c
src/analyze/analyze-timestamp.c
src/basic/chase-symlinks.c
src/basic/chase-symlinks.h
src/basic/conf-files.c
src/basic/fileio.c
src/basic/filesystems.c
src/basic/fs-util.c
src/basic/locale-util.c
src/basic/log.h
src/basic/missing_syscall.h
src/basic/nulstr-util.c
src/basic/nulstr-util.h
src/basic/path-lookup.c
src/basic/path-util.c
src/basic/path-util.h
src/basic/process-util.c
src/basic/random-util.h
src/basic/sigbus.c
src/basic/signal-util.c
src/basic/signal-util.h
src/basic/stat-util.c
src/basic/stat-util.h
src/basic/strv.c
src/basic/strv.h
src/basic/tmpfile-util.c
src/basic/tmpfile-util.h
src/boot/bootctl.c
src/boot/efi/boot.c
src/boot/efi/drivers.c
src/boot/efi/efi-string.h
src/boot/efi/meson.build
src/boot/efi/random-seed.c
src/boot/efi/util.h
src/boot/efi/vmm.c [new file with mode: 0644]
src/boot/efi/vmm.h [new file with mode: 0644]
src/boot/measure.c
src/boot/pcrphase.c
src/core/bpf-devices.c
src/core/bpf-lsm.c
src/core/cgroup.c
src/core/cgroup.h
src/core/crash-handler.c
src/core/dbus-cgroup.c
src/core/efi-random.c
src/core/efi-random.h
src/core/kmod-setup.c
src/core/load-fragment-gperf.gperf.in
src/core/load-fragment.c
src/core/main.c
src/core/namespace.c
src/cryptsetup/cryptsetup.c
src/delta/delta.c
src/dissect/dissect.c
src/fuzz/fuzz-compress.c
src/home/homework-cifs.c
src/home/homework-fscrypt.c
src/home/homework.c
src/hostname/hostnamectl.c
src/id128/id128.c
src/import/pull-tar.c
src/journal-remote/microhttpd-util.h
src/journal/journalctl.c
src/journal/journald-audit.c
src/journal/journald-context.c
src/journal/journald-kmsg.c
src/journal/journald-native.c
src/journal/journald-server.c
src/journal/journald-server.h
src/journal/journald-stream.c
src/journal/journald-syslog.c
src/journal/test-journal-interleaving.c
src/kernel-install/kernel-install.in
src/libsystemd/sd-bus/bus-creds.c
src/libsystemd/sd-bus/sd-bus.c
src/libsystemd/sd-device/device-private.c
src/libsystemd/sd-device/test-sd-device.c
src/libsystemd/sd-hwdb/sd-hwdb.c
src/libsystemd/sd-journal/journal-file.c
src/libsystemd/sd-journal/sd-journal.c
src/libsystemd/sd-path/sd-path.c
src/locale/localectl.c
src/locale/localed-util.c
src/machine/machine-dbus.c
src/machine/machinectl.c
src/nspawn/nspawn-seccomp.c
src/nspawn/nspawn.c
src/partition/repart.c
src/random-seed/random-seed.c
src/resolve/resolvectl.c
src/resolve/resolved-dns-question.h
src/resolve/resolved-dns-server.h
src/resolve/resolved-dns-trust-anchor.c
src/resolve/resolved-link-bus.c
src/resolve/resolved-link.c
src/resolve/resolved-link.h
src/shared/acpi-fpdt.c
src/shared/ask-password-api.c
src/shared/base-filesystem.c
src/shared/bootspec.c
src/shared/btrfs-util.c
src/shared/bus-print-properties.c
src/shared/bus-unit-util.c
src/shared/cgroup-show.c
src/shared/condition.c
src/shared/conf-parser.c
src/shared/copy.c
src/shared/copy.h
src/shared/cryptsetup-util.c
src/shared/cryptsetup-util.h
src/shared/dev-setup.c
src/shared/discover-image.c
src/shared/dissect-image.c
src/shared/dissect-image.h
src/shared/find-esp.c
src/shared/format-table.c
src/shared/format-table.h
src/shared/fstab-util.c
src/shared/gpt.c
src/shared/gpt.h
src/shared/hwdb-util.c
src/shared/import-util.c
src/shared/install.h
src/shared/json.c
src/shared/json.h
src/shared/kbd-util.c
src/shared/libfido2-util.c
src/shared/mkfs-util.c
src/shared/mkfs-util.h
src/shared/module-util.c
src/shared/resize-fs.h
src/shared/resolve-util.h
src/shared/rm-rf.h
src/shared/seccomp-util.c
src/shared/tmpfile-util-label.c
src/shared/tpm2-util.c
src/shared/udev-util.c
src/systemctl/fuzz-systemctl-parse-argv.c
src/systemctl/systemctl-show.c
src/sysupdate/sysupdate-partition.c
src/sysupdate/sysupdate-resource.c
src/sysupdate/sysupdate-resource.h
src/sysupdate/sysupdate-transfer.c
src/test/meson.build
src/test/test-compress-benchmark.c
src/test/test-condition.c
src/test/test-copy.c
src/test/test-format-table.c
src/test/test-fs-util.c
src/test/test-gpt.c
src/test/test-hashmap-plain.c
src/test/test-nulstr-util.c [new file with mode: 0644]
src/test/test-sd-hwdb.c
src/test/test-seccomp.c
src/test/test-strbuf.c
src/test/test-strv.c
src/test/test-uid-range.c
src/timedate/timedatectl.c
src/tmpfiles/tmpfiles.c
src/udev/udev-rules.c
test/TEST-58-REPART/test.sh
test/fuzz/fuzz-unit-file/directives-all.service
test/test-functions
test/units/testsuite-58.sh
test/units/testsuite-75.sh
units/systemd-boot-system-token.service

index e514111f282c0f2639a72f8eb3fda06a43f61293..dadc6cb1b5ee3c45360dc870cb95958be2f4e90d 100644 (file)
@@ -20,7 +20,7 @@ import semmle.code.cpp.controlflow.StackVariableReachability
   * since they don't do anything illegal even when the variable is uninitialized
   */
 predicate cleanupFunctionDenyList(string fun) {
-  fun = "erase_char"
+  fun = "erase_char" or fun = "erase_obj"
 }
 
 /**
index dbdae760c2941df4b837ac9163542a0292454014..e8f3bae01a321f4fdc48b6993d4591353fe8c1e8 100644 (file)
@@ -9,10 +9,37 @@ on:
     branches:
       - main
       - v[0-9]+-stable
+    paths:
+      - '**'
+      - '!README*'
+      - '!LICENSE*'
+      - '!LICENSES/**'
+      - '!TODO'
+      - '!docs/**'
+      - '!man/**'
+      - '!catalog/**'
+      - '!shell-completion/**'
+      - '!po/**'
+      - '!.**'
+      - '.github/**'
+
   pull_request:
     branches:
       - main
       - v[0-9]+-stable
+    paths:
+      - '**'
+      - '!README*'
+      - '!LICENSE*'
+      - '!LICENSES/**'
+      - '!TODO'
+      - '!docs/**'
+      - '!man/**'
+      - '!catalog/**'
+      - '!shell-completion/**'
+      - '!po/**'
+      - '!.**'
+      - '.github/**'
 
 permissions:
   contents: read
@@ -72,19 +99,19 @@ jobs:
         EOF
 
     - name: Build ${{ matrix.distro }}
-      run: sudo python3 -m mkosi build
+      run: sudo mkosi build
 
     - name: Show ${{ matrix.distro }} image summary
-      run: sudo python3 -m mkosi summary
+      run: sudo mkosi summary
 
     - name: Boot ${{ matrix.distro }} systemd-nspawn
-      run: sudo python3 -m mkosi boot ${{ env.KERNEL_CMDLINE }}
+      run: sudo mkosi boot ${{ env.KERNEL_CMDLINE }}
 
     - name: Check ${{ matrix.distro }} systemd-nspawn
-      run: sudo python3 -m mkosi shell bash -c "[[ -e /testok ]] || { cat /failed-services; exit 1; }"
+      run: sudo mkosi shell bash -c "[[ -e /testok ]] || { cat /failed-services; exit 1; }"
 
     - name: Boot ${{ matrix.distro }} QEMU
-      run: sudo timeout -k 30 10m python3 -m mkosi qemu
+      run: sudo timeout -k 30 10m mkosi qemu
 
     - name: Check ${{ matrix.distro }} QEMU
-      run: sudo python3 -m mkosi shell bash -c "[[ -e /testok ]] || { cat /failed-services; exit 1; }"
+      run: sudo mkosi shell bash -c "[[ -e /testok ]] || { cat /failed-services; exit 1; }"
diff --git a/NEWS b/NEWS
index 86a99386706878b2ef33f7cb5cf73401fc7f54fe..4868748a3e91a29942293c53fdbd9d3851ffc714 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,25 @@
 systemd System and Service Manager
 
+CHANGES WITH 253 in spe:
+
+        Changes in sd-boot, bootctl, and the Boot Loader Specification:
+
+       * systemd-boot now passes its random seed directly to the kernel's RNG
+         via the LINUX_EFI_RANDOM_SEED_TABLE_GUID configuration table, which
+         means the RNG gets seeded very early in boot before userspace has
+         started.
+
+       * systemd-boot will pass a random seed when secure boot is enabled if
+         it can additionally get a random seed from EFI itself, via EFI's RNG
+         protocol or a prior seed in LINUX_EFI_RANDOM_SEED_TABLE_GUID from a
+         preceding bootloader.
+
+       * The random seed stored in ESP is now refreshed whenever
+         systemd-random-seed.service is run.
+
+       * systemd-boot handles various seed inputs using a domain- and
+         field-separated hashing scheme.
+
 CHANGES WITH 252 🎃:
 
         Announcements of Future Feature Removals:
diff --git a/README b/README
index 92e3ca0f48644975ee35ed66cca0ef62d6170fc5..d8c279f9fa287d6a133b4df1aa23d808d24f9c79 100644 (file)
--- a/README
+++ b/README
@@ -17,7 +17,7 @@ BUG REPORTS:
         https://github.com/systemd/systemd/issues
 
 OLDER DOCUMENTATION:
-        http://0pointer.de/blog/projects/systemd.html
+        https://0pointer.de/blog/projects/systemd.html
         https://www.freedesktop.org/wiki/Software/systemd
 
 AUTHOR:
@@ -128,6 +128,11 @@ REQUIREMENTS:
 
         Required for signed Verity images support:
           CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG
+        Required to verify signed Verity images using keys enrolled in the MoK
+        (Machine-Owner Key) keyring:
+          CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG_SECONDARY_KEYRING
+          CONFIG_IMA_ARCH_POLICY
+          CONFIG_INTEGRITY_MACHINE_KEYRING
 
         Required for RestrictFileSystems= in service units:
           CONFIG_BPF
diff --git a/TODO b/TODO
index 88b4ed34d7a1e0763dbd1f2838e0e47e1685beb2..cd80d05e7a0e5ddd2ed9b092e49f64162a0a6a31 100644 (file)
--- a/TODO
+++ b/TODO
@@ -115,8 +115,33 @@ Deprecations and removals:
 
 * H2 2023: remove support for unmerged-usr
 
+* 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.
+
 Features:
 
+* maybe prohibit setuid() to the nobody user, to lock things down, via seccomp.
+  the nobody is not a user any code should run under, ever, as that user would
+  possibly get a lot of access to resources it really shouldn't be getting
+  access to due to the userns + nfs semantics of the user. Alternatively: use
+  the seccomp log action, and allow it.
+
+* sd-boot: add a new PE section .bls or so that carries a cpio with additional
+  boot loader entries (both type1 and type2). Then when initializing, find this
+  section, iterate through it and populate menu with it. cpio is simple enough
+  to make a parser for this reasonably robust. use same path structures as in
+  the ESP. Similar add one for signature key drop-ins.
+
+* add a new EFI tool "sd-fetch" or so. It looks in a PE section ".url" for an
+  URL, then downloads the file from it using UEFI HTTP APIs, and executes it.
+  Usecase: provide a minimal ESP with sd-boot and a couple of these sd-fetch
+  binaries in place of UKIs, and download them on-the-fly.
+
+* bootctl: warn if ESP is mounted world-readable (and in particular the seed).
+
+* sd-stub: call process_random_seed() the same way sd-boot does.
+
 * maybe: systemd-loop-generator that sets up loopback devices if requested via kernel
   cmdline. usecase: include encrypted/verity root fs in UKI.
 
@@ -449,18 +474,6 @@ Features:
 
 * pick up creds from EFI vars
 
-* sd-stub/sd-boot: write RNG seed to LINUX_EFI_RANDOM_SEED_TABLE_GUID config
-  table as well. (and possibly drop our efi var). Current kernels will pick up
-  the seed from there already, if EFI_RNG_PROTOCOL is not implemented by
-  firmware.
-
-* sd-boot: include domain specific hash string in hash function for random seed
-  plus sizes of everything. also include DMI/SMBIOS blob
-
-* sd-stub: invoke random seed logic the same way as in sd-boot, except if
-  random seed EFI variable is already set. That way, the variable set will be
-  set in all cases: if you just use sd-stub, or just sd-boot, or both.
-
 * sd-boot: we probably should include all BootXY EFI variable defined boot
   entries in our menu, and then suppress ourselves. Benefit: instant
   compatibility with all other OSes which register things there, in particular
@@ -738,13 +751,6 @@ Features:
   extending the command line to enable vsock on the VM, and using fw_cfg to
   configure socket address.
 
-* sd-boot: rework random seed handling following recent kernel changes: always
-  pass seed to kernel, but credit only if secure boot is used
-
-* sd-boot: also include the hyperv "vm generation id" in the random seed hash,
-  to cover nicely for machine clones. It's found in the ACPI tables, which
-  should be easily accessible from UEFI.
-
 * sd-boot: add menu item for shutdown? or hotkey?
 
 * sd-device has an API to create an sd_device object from a device id, but has
index fc9336085bd70d1d6818025903f422399da55da7..5be4d1ad17dec71b2b0803b3c3c93eae83a92f8c 100644 (file)
@@ -80,12 +80,6 @@ variables. All EFI variables use the vendor UUID
   * `1 << 5` → The boot loader supports looking for boot menu entries in the Extended Boot Loader Partition.
   * `1 << 6` → The boot loader supports passing a random seed to the OS.
 
-* The EFI variable `LoaderRandomSeed` contains a binary random seed if set. It
-  is set by the boot loader to pass an entropy seed read from the ESP to the OS.
-  The system manager then credits this seed to the kernel's entropy pool. It is
-  the responsibility of the boot loader to ensure the quality and integrity of
-  the random seed.
-
 * The EFI variable `LoaderSystemToken` contains binary random data,
   persistently set by the OS installer. Boot loaders that support passing
   random seeds to the OS should use this data and combine it with the random
@@ -107,8 +101,7 @@ that directory is empty, and only if no other file systems are mounted
 there. The `systemctl reboot --boot-loader-entry=…` and `systemctl reboot
 --boot-loader-menu=…` commands rely on the `LoaderFeatures` ,
 `LoaderConfigTimeoutOneShot`, `LoaderEntries`, `LoaderEntryOneShot`
-variables. `LoaderRandomSeed` is read by PID during early boot and credited to
-the kernel's random pool.
+variables.
 
 ## Boot Loader Entry Identifiers
 
index ab3add6031aef7e6138b936e3eb1f25cbb0f93bd..b4aaf10100bdc209b57e0f6e5bb6aa98b1cba04d 100644 (file)
@@ -85,6 +85,12 @@ All tools:
 * `$SYSTEMD_MEMPOOL=0` — if set, the internal memory caching logic employed by
   hash tables is turned off, and libc `malloc()` is used for all allocations.
 
+* `$SYSTEMD_UTF8=` — takes a boolean value, and overrides whether to generate
+  non-ASCII special glyphs at various places (i.e. "→" instead of
+  "->"). Usually this is deterined automatically, based on $LC_CTYPE, but in
+  scenarios where locale definitions are not installed it might make sense to
+  override this check explicitly.
+
 * `$SYSTEMD_EMOJI=0` — if set, tools such as `systemd-analyze security` will
   not output graphical smiley emojis, but ASCII alternatives instead. Note that
   this only controls use of Unicode emoji glyphs, and has no effect on other
@@ -467,7 +473,12 @@ SYSTEMD_HOME_DEBUG_SUFFIX=foo \
 
 `systemd-journald`:
 
-* `$SYSTEMD_JOURNAL_COMPACT` - Takes a boolean. If enabled, journal files are written
+* `$SYSTEMD_JOURNAL_COMPACT`  Takes a boolean. If enabled, journal files are written
   in a more compact format that reduces the amount of disk space required by the
   journal. Note that journal files in compact mode are limited to 4G to allow use of
   32-bit offsets. Enabled by default.
+
+`systemd-pcrphase`:
+
+* `$SYSTEMD_PCRPHASE_STUB_VERIFY` – Takes a boolean. If false the requested
+  measurement is done even if no EFI stub usage was reported via EFI variables.
index 3dc27f55525c5b233f71f60ddbbff2e053673f07..b7240f0d896f4bb5b5dd5adbb336d70d18fe644b 100644 (file)
@@ -197,28 +197,39 @@ boot, in order to ensure the entropy pool is filled up quickly.
    generate sufficient data), to generate a new random seed file to store in
    the ESP as well as a random seed to pass to the OS kernel. The new random
    seed file for the ESP is then written to the ESP, ensuring this is completed
-   before the OS is invoked. Very early during initialization PID 1 will read
-   the random seed provided in the EFI variable and credit it fully to the
-   kernel's entropy pool.
-
-   This mechanism is able to safely provide an initialized entropy pool already
-   in the `initrd` and guarantees that different seeds are passed from the boot
-   loader to the OS on every boot (in a way that does not allow regeneration of
-   an old seed file from a new seed file). Moreover, when an OS image is
-   replicated between multiple images and the random seed is not reset, this
-   will still result in different random seeds being passed to the OS, as the
-   per-machine 'system token' is specific to the physical host, and not
-   included in OS disk images. If the 'system token' is properly initialized
-   and kept sufficiently secret it should not be possible to regenerate the
-   entropy pool of different machines, even if this seed is the only source of
-   entropy.
+   before the OS is invoked.
+
+   The kernel then reads the random seed that the boot loader passes to it, via
+   the EFI configuration table entry, `LINUX_EFI_RANDOM_SEED_TABLE_GUID`
+   (1ce1e5bc-7ceb-42f2-81e5-8aadf180f57b), which is allocated with pool memory
+   of type `EfiACPIReclaimMemory`. Its contents have the form:
+   ```
+   struct linux_efi_random_seed {
+       u32     size; // of the 'seed' array in bytes
+       u8      seed[];
+   };
+   ```
+   The size field is generally set to 32 bytes, and the seed field includes a
+   hashed representation of any prior seed in `LINUX_EFI_RANDOM_SEED_TABLE_GUID`
+   together with the new seed.
+
+   This mechanism is able to safely provide an initialized entropy pool before
+   userspace even starts and guarantees that different seeds are passed from
+   the boot loader to the OS on every boot (in a way that does not allow
+   regeneration of an old seed file from a new seed file). Moreover, when an OS
+   image is replicated between multiple images and the random seed is not
+   reset, this will still result in different random seeds being passed to the
+   OS, as the per-machine 'system token' is specific to the physical host, and
+   not included in OS disk images. If the 'system token' is properly
+   initialized and kept sufficiently secret it should not be possible to
+   regenerate the entropy pool of different machines, even if this seed is the
+   only source of entropy.
 
    Note that the writes to the ESP needed to maintain the random seed should be
-   minimal. The size of the random seed file is directly derived from the Linux
-   kernel's entropy pool size, which defaults to 512 bytes. This means updating
-   the random seed in the ESP should be doable safely with a single sector
-   write (since hard-disk sectors typically happen to be 512 bytes long, too),
-   which should be safe even with FAT file system drivers built into
+   minimal. Because the size of the random seed file is generally set to 32 bytes,
+   updating the random seed in the ESP should be doable safely with a single
+   sector write (since hard-disk sectors typically happen to be 512 bytes long,
+   too), which should be safe even with FAT file system drivers built into
    low-quality EFI firmwares.
 
    As a special restriction: in virtualized environments PID 1 will refrain
index 60af8fde4db6677e295f958a48087372ec903bf1..498a4c5f5ec2024b3a7308845c1c779b124948a1 100644 (file)
@@ -296,6 +296,9 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnCompaq*:pn*Evo*N*:*
  KEYBOARD_KEY_9e=email
  KEYBOARD_KEY_9f=homepage
 
+evdev:name:AT Translated Set 2 keyboard:dmi:bvn*:bvr*:svnCompaq:pn*:pvr*:rvn*:rnN14KP6*
+ KEYBOARD_KEY_76=f21                                    # Fn+f2 toggle touchpad
+
 evdev:input:b0003v049Fp0051*
 evdev:input:b0003v049Fp008D*
  KEYBOARD_KEY_0c0011=presentation
index 6933c68e3820a62ae63ecee603ba6cd5ffe834aa..49bad01ded08f8e8aa4b65f5d6aab6a03a10b429 100644 (file)
         <listitem><para>If <command>status</command> is invoked (or no explicit command is given) and one of these
         switches is specified, <command>hostnamectl</command> will print out just this selected hostname.</para>
 
-        <para>If used with <command>set-hostname</command>, only the selected hostnames will be updated. When more
+        <para>If used with <command>hostname</command>, only the selected hostnames will be updated. When more
         than one of these switches are specified, all the specified hostnames will be updated. </para></listitem>
       </varlistentry>
 
index 5e08b3523411d1681c629ed3b75a830bd698d0d1..758f52fe4432c90272356dddc2d6f0ccf301cfb4 100644 (file)
@@ -2707,6 +2707,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemorySwapMax = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryZSwapMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryLimit = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly s DevicePolicy = '...';
@@ -3278,6 +3280,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <!--property MemorySwapMax is not documented!-->
 
+    <!--property MemoryZSwapMax is not documented!-->
+
     <!--property MemoryLimit is not documented!-->
 
     <!--property DevicePolicy is not documented!-->
@@ -3858,6 +3862,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemorySwapMax"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryZSwapMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryLimit"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="DevicePolicy"/>
@@ -4595,6 +4601,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemorySwapMax = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryZSwapMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryLimit = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly s DevicePolicy = '...';
@@ -5190,6 +5198,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <!--property MemorySwapMax is not documented!-->
 
+    <!--property MemoryZSwapMax is not documented!-->
+
     <!--property MemoryLimit is not documented!-->
 
     <!--property DevicePolicy is not documented!-->
@@ -5764,6 +5774,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemorySwapMax"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryZSwapMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryLimit"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="DevicePolicy"/>
@@ -6390,6 +6402,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemorySwapMax = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryZSwapMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryLimit = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly s DevicePolicy = '...';
@@ -6913,6 +6927,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <!--property MemorySwapMax is not documented!-->
 
+    <!--property MemoryZSwapMax is not documented!-->
+
     <!--property MemoryLimit is not documented!-->
 
     <!--property DevicePolicy is not documented!-->
@@ -7405,6 +7421,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemorySwapMax"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryZSwapMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryLimit"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="DevicePolicy"/>
@@ -8158,6 +8176,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemorySwapMax = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryZSwapMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryLimit = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly s DevicePolicy = '...';
@@ -8667,6 +8687,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <!--property MemorySwapMax is not documented!-->
 
+    <!--property MemoryZSwapMax is not documented!-->
+
     <!--property MemoryLimit is not documented!-->
 
     <!--property DevicePolicy is not documented!-->
@@ -9145,6 +9167,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemorySwapMax"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryZSwapMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryLimit"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="DevicePolicy"/>
@@ -9757,6 +9781,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemorySwapMax = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryZSwapMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryLimit = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly s DevicePolicy = '...';
@@ -9908,6 +9934,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
 
     <!--property MemorySwapMax is not documented!-->
 
+    <!--property MemoryZSwapMax is not documented!-->
+
     <!--property MemoryLimit is not documented!-->
 
     <!--property DevicePolicy is not documented!-->
@@ -10066,6 +10094,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemorySwapMax"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryZSwapMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryLimit"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="DevicePolicy"/>
@@ -10246,6 +10276,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemorySwapMax = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryZSwapMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryLimit = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly s DevicePolicy = '...';
@@ -10415,6 +10447,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
 
     <!--property MemorySwapMax is not documented!-->
 
+    <!--property MemoryZSwapMax is not documented!-->
+
     <!--property MemoryLimit is not documented!-->
 
     <!--property DevicePolicy is not documented!-->
@@ -10601,6 +10635,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemorySwapMax"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryZSwapMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryLimit"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="DevicePolicy"/>
index ebbb31cc20e687be2ab4e977478778060a2f34cb..9d483b381959dedc0063d8a64c6fb6e85f414544 100644 (file)
         below. Defaults to <literal>%t</literal>. To disable split artifact generation for a partition, set
         <varname>SplitName=</varname> to <literal>-</literal>.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>Minimize=</varname></term>
+
+        <listitem><para>Takes a boolean. Disabled by default. If enabled, the partition is created at least
+        as big as required for the minimal file system of the type specified by <varname>Format=</varname>,
+        taking into account the sources configured with  <varname>CopyFiles=</varname>. Note that unless the
+        filesystem is a read-only filesystem, <command>systemd-repart</command> will have to populate the
+        filesystem twice, so enabling this option might slow down repart when populating large partitions.
+        </para></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index 0eee532f90af00114537add52de23ec9cf119cca..f96c4c6512464d4f612a9c590b250f4d26f6fcf4 100644 (file)
         to view this data. </para></listitem>
       </varlistentry>
 
-      <varlistentry>
-        <term><varname>LoaderRandomSeed</varname></term>
-
-        <listitem><para>A binary random seed <command>systemd-boot</command> may optionally pass to the
-        OS. This is a volatile EFI variable that is hashed at boot from the combination of a random seed
-        stored in the ESP (in <filename>/loader/random-seed</filename>) and a "system token" persistently
-        stored in the EFI variable <varname>LoaderSystemToken</varname> (see below). During early OS boot the
-        system manager reads this variable and passes it to the OS kernel's random pool, crediting the full
-        entropy it contains. This is an efficient way to ensure the system starts up with a fully initialized
-        kernel random pool — as early as the initrd phase. <command>systemd-boot</command> reads
-        the random seed from the ESP, combines it with the "system token", and both derives a new random seed
-        to update in-place the seed stored in the ESP, and the random seed to pass to the OS from it via
-        SHA256 hashing in counter mode. This ensures that different physical systems that boot the same
-        "golden" OS image — i.e. containing the same random seed file in the ESP — will still pass a
-        different random seed to the OS. It is made sure the random seed stored in the ESP is fully
-        overwritten before the OS is booted, to ensure different random seed data is used between subsequent
-        boots.</para>
-
-        <para>See <ulink url="https://systemd.io/RANDOM_SEEDS">Random Seeds</ulink> for
-        further information.</para></listitem>
-      </varlistentry>
-
       <varlistentry>
         <term><varname>LoaderSystemToken</varname></term>
 
     <filename>/etc/kernel/tries</filename> when a boot loader entry is first created.</para>
   </refsect1>
 
+  <refsect1>
+    <title>Using systemd-boot in virtual machines.</title>
+
+    <para>When using qemu with OVMF (UEFI Firmware for virtual machines) the <option>-kernel</option> switch
+    works not only for linux kernels, but for any EFI binary, including sd-boot and unified linux
+    kernels.  Example command line for loading sd-boot on x64:</para>
+
+    <para>
+      <command>qemu-system-x86_64 <replaceable>[ ... ]</replaceable>
+      -kernel /usr/lib/systemd/boot/efi/systemd-bootx64.efi</command>
+    </para>
+
+    <para>systemd-boot will detect that it was started directly instead of being loaded from ESP and will
+    search for the ESP in that case, taking into account boot order information from the hypervisor (if
+    available).</para>
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
     <para>
index 0b6bf793cea4a58b3ce2731c326df93876cf1911..b9404082671bc11b26b9f6f8a9f4f03d0ad706c7 100644 (file)
@@ -52,7 +52,7 @@
     <title>Description</title>
 
     <para><command>systemd-dissect</command> is a tool for introspecting and interacting with file system OS
-    disk images, specifically Discoverable Disk Images (DDIs). It supports five different operations:</para>
+    disk images, specifically Discoverable Disk Images (DDIs). It supports four different operations:</para>
 
     <orderedlist>
       <listitem><para>Show general OS image information, including the image's
index 46fc9796549c065e7b56578c0743cb72ec290121..f3b2834b2ec23274c7f7ce5df91c1d2bc7727453 100644 (file)
     --add-section .initrd=initrd.cpio --change-section-vma .initrd=0x3000000 \
     --add-section .splash=splash.bmp --change-section-vma .splash=0x100000 \
     --add-section .dtb=devicetree.dtb --change-section-vma .dtb=0x40000 \
-    --add-section .pcrsig=tpm2-pcr-signature.json --change-section-vma .splash=0x80000 \
-    --add-section .pcrpkey=tpm2-pcr-public.pem --change-section-vma .splash=0x90000 \
+    --add-section .pcrsig=tpm2-pcr-signature.json --change-section-vma .pcrsig=0x80000 \
+    --add-section .pcrpkey=tpm2-pcr-public.pem --change-section-vma .pcrpkey=0x90000 \
     /usr/lib/systemd/boot/efi/linuxx64.efi.stub \
     foo.efi</programlisting>
 
index 3585cbf10766cb6d051f4a2e28575113edb4af6d..6559fceedb47c1465be1a3f88d19f29829bad880 100644 (file)
         if <option>--split</option> is enabled.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--include-partitions=</option><arg rep="repeat">PARTITION</arg></term>
+        <term><option>--exclude-partitions=</option><arg rep="repeat">PARTITION</arg></term>
+
+        <listitem><para>These options specify which partition types <command>systemd-repart</command> should
+        operate on. If <option>--include-partitions=</option> is used, all partitions that aren't specified
+        are excluded. If <option>--exclude-partitions=</option> is used, all partitions that are specified
+        are excluded. Both options take a comma separated list of GPT partition type UUIDs or identifiers
+        (see <varname>Type=</varname> in
+        <citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>). All
+        partitions that are excluded using these options are still taken into account when calculating the
+        sizes and offsets of other partitions, but aren't actually written to the disk image. The net effect
+        of this option is that if you run <command>systemd-repart</command> again without these options, the
+        missing partitions will be added as if they had not been excluded the first time
+        <command>systemd-repart</command> was executed.</para></listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
       <xi:include href="standard-options.xml" xpointer="no-pager" />
index 2a0e40a17d2a4a6c3ab5f01bfc3246318d2e95c2..b2b15d8e53cc910db11da74b82424fc491a50db1 100644 (file)
 
       <varlistentry>
         <term><varname>MemorySwapMax=<replaceable>bytes</replaceable></varname></term>
+        <term><varname>MemoryZSwapMax=<replaceable>bytes</replaceable></varname></term>
 
         <listitem>
-          <para>Specify the absolute limit on swap usage of the executed processes in this unit.</para>
+          <para>Specify the absolute limit on (z)swap usage of the executed processes in this unit.</para>
 
           <para>Takes a swap size in bytes. If the value is suffixed with K, M, G or T, the specified swap size is
           parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. If assigned the
-          special value <literal>infinity</literal>, no swap limit is applied. This controls the
-          <literal>memory.swap.max</literal> control group attribute. For details about this control group attribute,
+          special value <literal>infinity</literal>, no swap limit is applied. These settings control the
+          <literal>memory.(z)swap.max</literal> control group attributes. For details about these control group attributes,
           see <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
         </listitem>
       </varlistentry>
index 0fd0129820bf6c55ac373ac2aa12ae92a95caf7f..37d46dfae2a9aa16981e7956c891462595b41e65 100644 (file)
@@ -599,6 +599,10 @@ foreach ident : [
                                  #include <unistd.h>
                                  #include <signal.h>
                                  #include <sys/wait.h>'''],
+        ['rt_tgsigqueueinfo', '''#include <stdlib.h>
+                                 #include <unistd.h>
+                                 #include <signal.h>
+                                 #include <sys/wait.h>'''],
         ['mallinfo',          '''#include <malloc.h>'''],
         ['mallinfo2',         '''#include <malloc.h>'''],
         ['execveat',          '''#include <unistd.h>'''],
@@ -1325,7 +1329,10 @@ if want_libcryptsetup != 'false' and not skip_deps
 
         foreach ident : ['crypt_set_metadata_size',
                          'crypt_activate_by_signed_key',
-                         'crypt_token_max']
+                         'crypt_token_max',
+                         'crypt_reencrypt_init_by_passphrase',
+                         'crypt_reencrypt',
+                         'crypt_set_data_offset']
                 have_ident = have and cc.has_function(
                         ident,
                         prefix : '#include <libcryptsetup.h>',
@@ -1487,11 +1494,14 @@ if want_tpm2 != 'false' and not skip_deps
         tpm2 = dependency('tss2-esys tss2-rc tss2-mu',
                           required : want_tpm2 == 'true')
         have = tpm2.found()
+        have_esys3 = tpm2.version().version_compare('>= 3.0.0')
 else
         have = false
+        have_esys3 = false
         tpm2 = []
 endif
 conf.set10('HAVE_TPM2', have)
+conf.set10('HAVE_TSS2_ESYS3', have_esys3)
 
 want_elfutils = get_option('elfutils')
 if want_elfutils != 'false' and not skip_deps
@@ -3444,15 +3454,14 @@ public_programs += executable(
         install : true,
         install_dir : rootlibexecdir)
 
-executable(
+public_programs += executable(
         'systemd-ac-power',
         'src/ac-power/ac-power.c',
         include_directories : includes,
         link_with : [libshared],
         dependencies : [versiondep],
         install_rpath : rootpkglibdir,
-        install : true,
-        install_dir : rootlibexecdir)
+        install : true)
 
 public_programs += executable(
         'systemd-detect-virt',
index cbf82811cf2be7a1cc5ae2d9cec26da2761fe67f..5f6208dd887473f0be2234adcd9107594dfb0646 100755 (executable)
@@ -61,7 +61,7 @@ done
 # version 5.6.0 to satisfy meson which makes bpf work on CentOS Stream 8 as well.
 if [ "$(grep '^ID=' /etc/os-release)" = "ID=\"centos\"" ] && [ "$(grep '^VERSION=' /etc/os-release)" = "VERSION=\"8\"" ]; then
         cp /usr/sbin/bpftool /usr/sbin/bpftool.real
-        cat > /usr/sbin/bpftool <<EOF
+        cat >/usr/sbin/bpftool <<EOF
 #!/bin/sh
 if [ "\$1" = --version ]; then
         echo 5.6.0
@@ -220,7 +220,7 @@ if [ -n "$IMAGE_VERSION" ] ; then
                 -e '/^IMAGE_VERSION=/!p' \
                 -e "\$aIMAGE_VERSION=$IMAGE_VERSION" <$OSRELEASEFILE >"/tmp/os-release.tmp"
 
-        cat /tmp/os-release.tmp > "$DESTDIR"/usr/lib/os-release
+        cat /tmp/os-release.tmp >"$DESTDIR"/usr/lib/os-release
         rm /tmp/os-release.tmp
 fi
 
@@ -238,7 +238,7 @@ if [ -n "$SANITIZERS" ]; then
 
         mkdir -p "$DESTDIR/etc/systemd/system.conf.d"
 
-        cat > "$DESTDIR/etc/systemd/system.conf.d/10-asan.conf" <<EOF
+        cat >"$DESTDIR/etc/systemd/system.conf.d/10-asan.conf" <<EOF
 [Manager]
 ManagerEnvironment=ASAN_OPTIONS=$ASAN_OPTIONS\\
                    UBSAN_OPTIONS=$UBSAN_OPTIONS\\
@@ -253,7 +253,7 @@ EOF
         # sanitizer failures appear directly on the user's console.
         mkdir -p "$DESTDIR/etc/systemd/system/systemd-journald.service.d"
 
-        cat > "$DESTDIR/etc/systemd/system/systemd-journald.service.d/10-stdout-tty.conf" <<EOF
+        cat >"$DESTDIR/etc/systemd/system/systemd-journald.service.d/10-stdout-tty.conf" <<EOF
 [Service]
 StandardOutput=tty
 EOF
@@ -265,7 +265,7 @@ EOF
 
         mkdir -p "$DESTDIR/etc/systemd/system/console-getty.service.d"
 
-        cat > "$DESTDIR/etc/systemd/system/console-getty.service.d/10-no-vhangup.conf" <<EOF
+        cat >"$DESTDIR/etc/systemd/system/console-getty.service.d/10-no-vhangup.conf" <<EOF
 [Service]
 TTYVHangup=no
 CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG
@@ -274,7 +274,7 @@ fi
 
 # Make sure services aren't enabled by default on Debian/Ubuntu.
 mkdir -p "$DESTDIR/etc/systemd/system-preset"
-echo "disable *" > "$DESTDIR/etc/systemd/system-preset/99-mkosi.preset"
+echo "disable *" >"$DESTDIR/etc/systemd/system-preset/99-mkosi.preset"
 
 if [ -d mkosi.kernel/ ]; then
         cd "$SRCDIR/mkosi.kernel"
@@ -307,6 +307,10 @@ if [ -d mkosi.kernel/ ]; then
                 --enable MEMCG \
                 --enable MEMCG_SWAP \
                 --enable MEMCG_KMEM \
+                --enable IMA_ARCH_POLICY \
+                --enable DM_VERITY_VERIFY_ROOTHASH_SIG \
+                --enable DM_VERITY_VERIFY_ROOTHASH_SIG_SECONDARY_KEYRING \
+                --enable INTEGRITY_MACHINE_KEYRING \
                 --enable NETFILTER_ADVANCED \
                 --enable NF_CONNTRACK_MARK
 
index 5bc13f919abb4ecfb7ad57881fde019f9d5aa444..f9e4d086160ed3a3c2bef1b3b24b7d93b7df720c 100644 (file)
@@ -21,6 +21,8 @@ Packages=
         coreutils
         diffutils
         dnsmasq
+        dosfstools
+        e2fsprogs
         findutils
         gcc # For sanitizer libraries
         gdb
@@ -29,6 +31,7 @@ Packages=
         kexec-tools
         kmod
         less
+        mtools
         nano
         nftables
         openssl
@@ -40,6 +43,7 @@ Packages=
         util-linux
         valgrind
         wireguard-tools
+        xfsprogs
         zsh
 
 BuildPackages=
index 883dc1fcd5c8ad9355c29be000cf101ba1606900..993e3dd3441ac1585267ac6d105c79c24c200f3c 100644 (file)
@@ -11,8 +11,10 @@ Distribution=arch
 [Content]
 Packages=
         alsa-lib
+        btrfs-progs
         compsize
         dhcp
+        f2fs-tools
         fuse2
         gnutls
         iproute
index b2da9b6232792554d7e8305fb7ca4172a509d000..c52d2e9e965f03a0841907ce2726bf703c66e6df 100644 (file)
@@ -9,7 +9,9 @@ Release=testing
 
 [Content]
 Packages=
+        btrfs-progs
         cryptsetup-bin
+        f2fs-tools
         fdisk
         fuse
         gcc # Provides libasan/libubsan
index c76f479956cfd4a799aa8e81787739ccec10df41..5f92aab95cdb705364e3cddba61f04e6705eb606 100644 (file)
@@ -10,9 +10,11 @@ Release=37
 [Content]
 Packages=
         alsa-lib
+        btrfs-progs
         compsize
         cryptsetup
         dhcp-server
+        f2fs-tools
         fuse
         glib2
         glibc-minimal-langpack
index 7a212237f2f46b0a19a982e7e4182a8c497e09fd..417827f7c0d4100f1bdabf476dec12438dc9e0d9 100644 (file)
@@ -9,7 +9,9 @@ Release=tumbleweed
 
 [Content]
 Packages=
+        btrfs-progs
         dbus-1
+        f2fs-tools
         fuse
         gcc # Provides libasan/libubsan
         glibc-32bit
index c7badf5742a5039c2ed606677c4649e6624ea74d..346b129e521295b70e7dfccb59a53b5ddfeacfbd 100644 (file)
@@ -10,7 +10,9 @@ Repositories=main,universe
 
 [Content]
 Packages=
+        btrfs-progs
         cryptsetup-bin
+        f2fs-tools
         fdisk
         fuse
         gcc # Provides libasan/libubsan
index fb59d3111523c32df0734b77e762421e198bb2de..cebbf29250dc826e82d336472ea0759664895382 100755 (executable)
@@ -2,11 +2,11 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
 if [ "$1" = "final" ]; then
-    if command -v bootctl > /dev/null && [ -d "/efi" ]; then
+    if command -v bootctl >/dev/null && [ -d "/efi" ]; then
         bootctl install
     fi
 
-    cat >> /root/.gdbinit <<EOF
+    cat >>/root/.gdbinit <<EOF
 set debuginfod enabled off
 set build-id-verbose 0
 EOF
index c1472e66fb5d82fa0151f7d9b6bee51acbb102f4..4cf9c48c2e0e259ec82699edeb23fdbb2b9aeda2 100644 (file)
--- a/po/fi.po
+++ b/po/fi.po
@@ -2,12 +2,13 @@
 #
 # Finnish translation of systemd.
 # Jan Kuparinen <copper_fin@hotmail.com>, 2021, 2022.
+# Ricky Tigg <ricky.tigg@gmail.com>, 2022.
 msgid ""
 msgstr ""
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2022-10-20 10:35+0200\n"
-"PO-Revision-Date: 2022-10-22 17:19+0000\n"
-"Last-Translator: Jan Kuparinen <copper_fin@hotmail.com>\n"
+"PO-Revision-Date: 2022-11-10 19:19+0000\n"
+"Last-Translator: Ricky Tigg <ricky.tigg@gmail.com>\n"
 "Language-Team: Finnish <https://translate.fedoraproject.org/projects/systemd/"
 "master/fi/>\n"
 "Language: fi\n"
@@ -15,7 +16,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.14.1\n"
+"X-Generator: Weblate 4.14.2\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -119,7 +120,7 @@ msgstr "Todennus vaaditaan käyttäjän kotialueen salasanan vaihtamiseksi."
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
 msgid "Set hostname"
-msgstr "Määritä isäntänimi"
+msgstr "Määritä konenimi"
 
 #: src/hostname/org.freedesktop.hostname1.policy:21
 msgid "Authentication is required to set the local hostname."
index 952fe95c2cfdc939123ce7365de0642f3faeed0d..6daab0844367ce814c0c38073328993954288186 100644 (file)
@@ -24,21 +24,15 @@ static int test_calendar_one(usec_t n, const char *p) {
         if (r < 0)
                 return log_error_errno(r, "Failed to format calendar specification '%s': %m", p);
 
-        table = table_new("name", "value");
+        table = table_new_vertical();
         if (!table)
                 return log_oom();
 
-        table_set_header(table, false);
-
         assert_se(cell = table_get_cell(table, 0, 0));
         r = table_set_ellipsize_percent(table, cell, 100);
         if (r < 0)
                 return r;
 
-        r = table_set_align_percent(table, cell, 100);
-        if (r < 0)
-                return r;
-
         assert_se(cell = table_get_cell(table, 0, 1));
         r = table_set_ellipsize_percent(table, cell, 100);
         if (r < 0)
@@ -46,14 +40,14 @@ static int test_calendar_one(usec_t n, const char *p) {
 
         if (!streq(t, p)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Original form:",
+                                   TABLE_FIELD, "Original form",
                                    TABLE_STRING, p);
                 if (r < 0)
                         return table_log_add_error(r);
         }
 
         r = table_add_many(table,
-                           TABLE_STRING, "Normalized form:",
+                           TABLE_FIELD, "Normalized form",
                            TABLE_STRING, t);
         if (r < 0)
                 return table_log_add_error(r);
@@ -65,7 +59,7 @@ static int test_calendar_one(usec_t n, const char *p) {
                 if (r == -ENOENT) {
                         if (i == 0) {
                                 r = table_add_many(table,
-                                                   TABLE_STRING, "Next elapse:",
+                                                   TABLE_FIELD, "Next elapse",
                                                    TABLE_STRING, "never",
                                                    TABLE_SET_COLOR, ansi_highlight_yellow());
                                 if (r < 0)
@@ -78,7 +72,7 @@ static int test_calendar_one(usec_t n, const char *p) {
 
                 if (i == 0) {
                         r = table_add_many(table,
-                                           TABLE_STRING, "Next elapse:",
+                                           TABLE_FIELD, "Next elapse",
                                            TABLE_TIMESTAMP, next,
                                            TABLE_SET_COLOR, ansi_highlight_blue());
                         if (r < 0)
@@ -91,7 +85,7 @@ static int test_calendar_one(usec_t n, const char *p) {
                         else
                                 k = 0;
 
-                        r = table_add_cell_stringf(table, NULL, "Iter. #%u:", i+1);
+                        r = table_add_cell_stringf_full(table, NULL, TABLE_FIELD, "Iteration #%u", i+1);
                         if (r < 0)
                                 return table_log_add_error(r);
 
@@ -104,14 +98,14 @@ static int test_calendar_one(usec_t n, const char *p) {
 
                 if (!in_utc_timezone()) {
                         r = table_add_many(table,
-                                           TABLE_STRING, "(in UTC):",
+                                           TABLE_FIELD, "(in UTC)",
                                            TABLE_TIMESTAMP_UTC, next);
                         if (r < 0)
                                 return table_log_add_error(r);
                 }
 
                 r = table_add_many(table,
-                                   TABLE_STRING, "From now:",
+                                   TABLE_FIELD, "From now",
                                    TABLE_TIMESTAMP_RELATIVE, next);
                 if (r < 0)
                         return table_log_add_error(r);
index a30662b49f70c7b83ed79ac8265849f53e6569ed..08184cedfe2fc8f8ff57efae451d94fbae246499 100644 (file)
@@ -23,8 +23,6 @@ int verb_cat_config(int argc, char *argv[], void *userdata) {
                         print_separator();
 
                 if (path_is_absolute(*arg)) {
-                        const char *dir;
-
                         NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) {
                                 t = path_startswith(*arg, dir);
                                 if (t)
index e30b3a6ac6d7b8a0aa13a3fbfc08ab251dcf0c18..582e04eac9132866368b806df11ca05bbbd5041c 100644 (file)
@@ -50,8 +50,6 @@ static int load_available_kernel_filesystems(Set **ret) {
 }
 
 static void filesystem_set_remove(Set *s, const FilesystemSet *set) {
-        const char *filesystem;
-
         NULSTR_FOREACH(filesystem, set->value) {
                 if (filesystem[0] == '@')
                         continue;
@@ -61,7 +59,6 @@ static void filesystem_set_remove(Set *s, const FilesystemSet *set) {
 }
 
 static void dump_filesystem_set(const FilesystemSet *set) {
-        const char *filesystem;
         int r;
 
         if (!set)
@@ -119,7 +116,6 @@ int verb_filesystems(int argc, char *argv[], void *userdata) {
 
         if (strv_isempty(strv_skip(argv, 1))) {
                 _cleanup_set_free_ Set *kernel = NULL, *known = NULL;
-                const char *fs;
                 int k;
 
                 NULSTR_FOREACH(fs, filesystem_sets[FILESYSTEM_SET_KNOWN].value)
index 155c611c7176abcff29b025e94683a1bef433f6f..cb6692e2771d814c6c1ca315c68f5f0f6303d5e8 100644 (file)
@@ -34,17 +34,13 @@ static int analyze_elf(char **filenames, JsonFormatFlags json_flags) {
                 if (r < 0)
                         return log_error_errno(r, "Parsing \"%s\" as ELF object failed: %m", abspath);
 
-                t = table_new("", "");
+                t = table_new_vertical();
                 if (!t)
                         return log_oom();
 
-                r = table_set_align_percent(t, TABLE_HEADER_CELL(0), 100);
-                if (r < 0)
-                        return table_log_add_error(r);
-
                 r = table_add_many(
                                 t,
-                                TABLE_STRING, "path:",
+                                TABLE_FIELD, "path",
                                 TABLE_STRING, abspath);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -62,15 +58,9 @@ static int analyze_elf(char **filenames, JsonFormatFlags json_flags) {
                                  * metadata is parsed recursively in core files, so there might be
                                  * multiple modules. */
                                 if (STR_IN_SET(module_name, "elfType", "elfArchitecture")) {
-                                        _cleanup_free_ char *suffixed = NULL;
-
-                                        suffixed = strjoin(module_name, ":");
-                                        if (!suffixed)
-                                                return log_oom();
-
                                         r = table_add_many(
                                                         t,
-                                                        TABLE_STRING, suffixed,
+                                                        TABLE_FIELD, module_name,
                                                         TABLE_STRING, json_variant_string(module_json));
                                         if (r < 0)
                                                 return table_log_add_error(r);
@@ -91,7 +81,7 @@ static int analyze_elf(char **filenames, JsonFormatFlags json_flags) {
                                 if (!streq(abspath, module_name)) {
                                         r = table_add_many(
                                                         t,
-                                                        TABLE_STRING, "module name:",
+                                                        TABLE_FIELD, "module name",
                                                         TABLE_STRING, module_name);
                                         if (r < 0)
                                                 return table_log_add_error(r);
@@ -99,15 +89,9 @@ static int analyze_elf(char **filenames, JsonFormatFlags json_flags) {
 
                                 JSON_VARIANT_OBJECT_FOREACH(field_name, field, module_json)
                                         if (json_variant_is_string(field)) {
-                                                _cleanup_free_ char *suffixed = NULL;
-
-                                                suffixed = strjoin(field_name, ":");
-                                                if (!suffixed)
-                                                        return log_oom();
-
                                                 r = table_add_many(
                                                                 t,
-                                                                TABLE_STRING, suffixed,
+                                                                TABLE_FIELD, field_name,
                                                                 TABLE_STRING, json_variant_string(field));
                                                 if (r < 0)
                                                         return table_log_add_error(r);
@@ -115,8 +99,6 @@ static int analyze_elf(char **filenames, JsonFormatFlags json_flags) {
                         }
                 }
                 if (json_flags & JSON_FORMAT_OFF) {
-                        (void) table_set_header(t, true);
-
                         r = table_print(t, NULL);
                         if (r < 0)
                                 return table_log_print_error(r);
index 69eab91bdb6719cca1e589c77ca1d858d68076a7..585dd01f689fffdd2ff806005b00a89bf4c44aff 100644 (file)
@@ -567,8 +567,6 @@ static int assess_system_call_architectures(
 }
 
 static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilterSet *f, const char **ret_offending_syscall) {
-        const char *syscall;
-
         NULSTR_FOREACH(syscall, f->value) {
                 if (syscall[0] == '@') {
                         const SyscallFilterSet *g;
index 308b1724e5d56d7c20393b5192e8101684448b07..50662da0627095abec1e141798d28991f52d00c3 100644 (file)
@@ -59,8 +59,6 @@ static int load_kernel_syscalls(Set **ret) {
 }
 
 static void syscall_set_remove(Set *s, const SyscallFilterSet *set) {
-        const char *syscall;
-
         if (!set)
                 return;
 
@@ -73,8 +71,6 @@ static void syscall_set_remove(Set *s, const SyscallFilterSet *set) {
 }
 
 static void dump_syscall_filter(const SyscallFilterSet *set) {
-        const char *syscall;
-
         printf("%s%s%s\n"
                "    # %s\n",
                ansi_highlight(),
@@ -93,7 +89,6 @@ int verb_syscall_filters(int argc, char *argv[], void *userdata) {
 
         if (strv_isempty(strv_skip(argv, 1))) {
                 _cleanup_set_free_ Set *kernel = NULL, *known = NULL;
-                const char *sys;
                 int k = 0;  /* explicit initialization to appease gcc */
 
                 NULSTR_FOREACH(sys, syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].value)
index f244ace04ac0af596cbcac900ef12e5a2a303cc6..3fdf0f964c14cece4ff7078348cdb06a76852074 100644 (file)
@@ -22,39 +22,33 @@ int verb_timespan(int argc, char *argv[], void *userdata) {
                         return r;
                 }
 
-                table = table_new("name", "value");
+                table = table_new_vertical();
                 if (!table)
                         return log_oom();
 
-                table_set_header(table, false);
-
                 assert_se(cell = table_get_cell(table, 0, 0));
                 r = table_set_ellipsize_percent(table, cell, 100);
                 if (r < 0)
                         return r;
 
-                r = table_set_align_percent(table, cell, 100);
-                if (r < 0)
-                        return r;
-
                 assert_se(cell = table_get_cell(table, 0, 1));
                 r = table_set_ellipsize_percent(table, cell, 100);
                 if (r < 0)
                         return r;
 
                 r = table_add_many(table,
-                                   TABLE_STRING, "Original:",
+                                   TABLE_FIELD, "Original",
                                    TABLE_STRING, *input_timespan);
                 if (r < 0)
                         return table_log_add_error(r);
 
-                r = table_add_cell_stringf(table, NULL, "%ss:", special_glyph(SPECIAL_GLYPH_MU));
+                r = table_add_cell_stringf_full(table, NULL, TABLE_FIELD, "%ss", special_glyph(SPECIAL_GLYPH_MU));
                 if (r < 0)
                         return table_log_add_error(r);
 
                 r = table_add_many(table,
                                    TABLE_UINT64, output_usecs,
-                                   TABLE_STRING, "Human:",
+                                   TABLE_FIELD, "Human",
                                    TABLE_TIMESPAN, output_usecs,
                                    TABLE_SET_COLOR, ansi_highlight());
                 if (r < 0)
index 79764e014e57d1da16231ee8e44e1e02c6483c36..97de4387aba650882799ed0066b3d2e5641fd5bd 100644 (file)
@@ -18,30 +18,24 @@ static int test_timestamp_one(const char *p) {
                 return r;
         }
 
-        table = table_new("name", "value");
+        table = table_new_vertical();
         if (!table)
                 return log_oom();
 
-        table_set_header(table, false);
-
         assert_se(cell = table_get_cell(table, 0, 0));
         r = table_set_ellipsize_percent(table, cell, 100);
         if (r < 0)
                 return r;
 
-        r = table_set_align_percent(table, cell, 100);
-        if (r < 0)
-                return r;
-
         assert_se(cell = table_get_cell(table, 0, 1));
         r = table_set_ellipsize_percent(table, cell, 100);
         if (r < 0)
                 return r;
 
         r = table_add_many(table,
-                           TABLE_STRING, "Original form:",
+                           TABLE_FIELD, "Original form",
                            TABLE_STRING, p,
-                           TABLE_STRING, "Normalized form:",
+                           TABLE_FIELD, "Normalized form",
                            TABLE_TIMESTAMP, usec,
                            TABLE_SET_COLOR, ansi_highlight_blue());
         if (r < 0)
@@ -49,13 +43,13 @@ static int test_timestamp_one(const char *p) {
 
         if (!in_utc_timezone()) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "(in UTC):",
+                                   TABLE_FIELD, "(in UTC)",
                                    TABLE_TIMESTAMP_UTC, usec);
                 if (r < 0)
                         return table_log_add_error(r);
         }
 
-        r = table_add_cell(table, NULL, TABLE_STRING, "UNIX seconds:");
+        r = table_add_cell(table, NULL, TABLE_FIELD, "UNIX seconds");
         if (r < 0)
                 return table_log_add_error(r);
 
@@ -70,7 +64,7 @@ static int test_timestamp_one(const char *p) {
                 return r;
 
         r = table_add_many(table,
-                           TABLE_STRING, "From now:",
+                           TABLE_FIELD, "From now",
                            TABLE_TIMESTAMP_RELATIVE, usec);
         if (r < 0)
                 return table_log_add_error(r);
index a0d29427d33a8333c450664cb683c3a008793bad..0bb07000bad8922b10e83a4793cb6bd059997df0 100644 (file)
@@ -57,6 +57,21 @@ static int log_autofs_mount_point(int fd, const char *path, ChaseSymlinksFlags f
                                  strna(n1), path);
 }
 
+static int log_prohibited_symlink(int fd, ChaseSymlinksFlags flags) {
+        _cleanup_free_ char *n1 = NULL;
+
+        assert(fd >= 0);
+
+        if (!FLAGS_SET(flags, CHASE_WARN))
+                return -EREMCHG;
+
+        (void) fd_get_path(fd, &n1);
+
+        return log_warning_errno(SYNTHETIC_ERRNO(EREMCHG),
+                                 "Detected symlink where not symlink is allowed at %s, refusing.",
+                                 strna(n1));
+}
+
 int chase_symlinks_at(
                 int dir_fd,
                 const char *path,
@@ -291,6 +306,9 @@ int chase_symlinks_at(
                 if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
                         _cleanup_free_ char *destination = NULL;
 
+                        if (flags & CHASE_PROHIBIT_SYMLINKS)
+                                return log_prohibited_symlink(child, flags);
+
                         /* This is a symlink, in this case read the destination. But let's make sure we
                          * don't follow symlinks without bounds. */
                         if (--max_follow <= 0)
@@ -442,9 +460,7 @@ int chase_symlinks(
                                               SYNTHETIC_ERRNO(ECHRNG),
                                               "Specified path '%s' is outside of specified root directory '%s', refusing to resolve.",
                                               absolute, root);
-        }
 
-        if (root) {
                 fd = open(root, O_CLOEXEC|O_DIRECTORY|O_PATH);
                 if (fd < 0)
                         return -errno;
index 6ea0b178759f30b0a436fe1669f3463b75ee434a..af0fcf155a26973c3adedc59ce4faee975cb1f83 100644 (file)
@@ -19,6 +19,7 @@ typedef enum ChaseSymlinksFlags {
                                             * Note: this may do an NSS lookup, hence this flag cannot be used in PID 1. */
         CHASE_AT_RESOLVE_IN_ROOT = 1 << 8, /* Same as openat2()'s RESOLVE_IN_ROOT flag, symlinks are resolved
                                             * relative to the given directory fd instead of root. */
+        CHASE_PROHIBIT_SYMLINKS  = 1 << 9, /* Refuse all symlinks */
 } ChaseSymlinksFlags;
 
 bool unsafe_transition(const struct stat *a, const struct stat *b);
index 348f7dcc7018a87dd6befe427d19a8ebda1ec154..3fbb2cda0010957de24fc4a5c7fab65bd36084c2 100644 (file)
@@ -13,6 +13,7 @@
 #include "hashmap.h"
 #include "log.h"
 #include "macro.h"
+#include "nulstr-util.h"
 #include "path-util.h"
 #include "set.h"
 #include "sort-util.h"
index a5c44f184a9cae5c901a5d018d684217e877bdac..5078cae2df1ec4f3b7fec0a766965d629ccae49c 100644 (file)
@@ -21,6 +21,7 @@
 #include "log.h"
 #include "macro.h"
 #include "mkdir.h"
+#include "nulstr-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "socket-util.h"
index b11cbb9d65a1c417be269878d3a4e423e269547a..7d34e4e9ad0489cfaeca8a25f444e0bcab7a5124 100644 (file)
@@ -28,7 +28,6 @@ int fs_type_from_string(const char *name, const statfs_f_type_t **ret) {
 }
 
 bool fs_in_group(const struct statfs *s, FilesystemGroups fs_group) {
-        const char *fs;
         int r;
 
         NULSTR_FOREACH(fs, filesystem_sets[fs_group].value) {
index c70926c12cab0cccd1b7edf5acb6737f22c4d2f0..4d24cd59de96a7ffccb21ba30e0bc9746d5b8a24 100644 (file)
@@ -197,6 +197,8 @@ int readlink_and_make_absolute(const char *p, char **r) {
 int chmod_and_chown_at(int dir_fd, const char *path, mode_t mode, uid_t uid, gid_t gid) {
         _cleanup_close_ int fd = -1;
 
+        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+
         if (path) {
                 /* Let's acquire an O_PATH fd, as precaution to change mode/owner on the same file */
                 fd = openat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
index 2f486a4d92ca0f2fb33d2509b881fe69410be373..40c6e46ab888e2760d9bc21c20df281b0f28c8f0 100644 (file)
@@ -286,8 +286,9 @@ void init_gettext(void) {
 }
 
 bool is_locale_utf8(void) {
-        const char *set;
         static int cached_answer = -1;
+        const char *set;
+        int r;
 
         /* Note that we default to 'true' here, since today UTF8 is
          * pretty much supported everywhere. */
@@ -295,6 +296,13 @@ bool is_locale_utf8(void) {
         if (cached_answer >= 0)
                 goto out;
 
+        r = getenv_bool_secure("SYSTEMD_UTF8");
+        if (r >= 0) {
+                cached_answer = r;
+                goto out;
+        } else if (r != -ENXIO)
+                log_debug_errno(r, "Failed to parse $SYSTEMD_UTF8, ignoring: %m");
+
         if (!setlocale(LC_ALL, "")) {
                 cached_answer = true;
                 goto out;
index c51941c14167a0165ad56e8bbe3a8fb5ebe762d0..2b1ac5f8c640a4881db4e806c69e35b0ec9ddf59 100644 (file)
@@ -375,15 +375,12 @@ typedef struct LogRateLimit {
         RateLimit ratelimit;
 } LogRateLimit;
 
-#define log_ratelimit_internal(_level, _error, _format, _file, _line, _func, ...)        \
+#define log_ratelimit_internal(_level, _error, _ratelimit, _format, _file, _line, _func, ...)        \
 ({                                                                              \
         int _log_ratelimit_error = (_error);                                    \
         int _log_ratelimit_level = (_level);                                    \
         static LogRateLimit _log_ratelimit = {                                  \
-                .ratelimit = {                                                  \
-                        .interval = 1 * USEC_PER_SEC,                           \
-                        .burst = 1,                                             \
-                },                                                              \
+                .ratelimit = (_ratelimit),                                      \
         };                                                                      \
         unsigned _num_dropped_errors = ratelimit_num_dropped(&_log_ratelimit.ratelimit); \
         if (_log_ratelimit_error != _log_ratelimit.error || _log_ratelimit_level != _log_ratelimit.level) { \
@@ -391,18 +388,35 @@ typedef struct LogRateLimit {
                 _log_ratelimit.error = _log_ratelimit_error;                    \
                 _log_ratelimit.level = _log_ratelimit_level;                    \
         }                                                                       \
-        if (ratelimit_below(&_log_ratelimit.ratelimit))                         \
+        if (log_get_max_level() == LOG_DEBUG || ratelimit_below(&_log_ratelimit.ratelimit)) \
                 _log_ratelimit_error = _num_dropped_errors > 0                  \
-                ? log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format " (Dropped %u similar message(s))", __VA_ARGS__, _num_dropped_errors) \
-                : log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format, __VA_ARGS__); \
+                ? log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format " (Dropped %u similar message(s))", ##__VA_ARGS__, _num_dropped_errors) \
+                : log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format, ##__VA_ARGS__); \
         _log_ratelimit_error;                                                   \
 })
 
-#define log_ratelimit_full_errno(level, error, format, ...)             \
+#define log_ratelimit_full_errno(level, error, _ratelimit, format, ...)             \
         ({                                                              \
                 int _level = (level), _e = (error);                     \
                 _e = (log_get_max_level() >= LOG_PRI(_level))           \
-                        ? log_ratelimit_internal(_level, _e, format, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
+                        ? log_ratelimit_internal(_level, _e, _ratelimit, format, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__) \
                         : -ERRNO_VALUE(_e);                             \
                 _e < 0 ? _e : -ESTRPIPE;                                \
         })
+
+#define log_ratelimit_full(level, _ratelimit, format, ...)                          \
+        log_ratelimit_full_errno(level, 0, _ratelimit, format, ##__VA_ARGS__)
+
+/* Normal logging */
+#define log_ratelimit_info(...)      log_ratelimit_full(LOG_INFO,    __VA_ARGS__)
+#define log_ratelimit_notice(...)    log_ratelimit_full(LOG_NOTICE,  __VA_ARGS__)
+#define log_ratelimit_warning(...)   log_ratelimit_full(LOG_WARNING, __VA_ARGS__)
+#define log_ratelimit_error(...)     log_ratelimit_full(LOG_ERR,     __VA_ARGS__)
+#define log_ratelimit_emergency(...) log_ratelimit_full(log_emergency_level(), __VA_ARGS__)
+
+/* Logging triggered by an errno-like error */
+#define log_ratelimit_info_errno(error, ...)      log_ratelimit_full_errno(LOG_INFO,    error, __VA_ARGS__)
+#define log_ratelimit_notice_errno(error, ...)    log_ratelimit_full_errno(LOG_NOTICE,  error, __VA_ARGS__)
+#define log_ratelimit_warning_errno(error, ...)   log_ratelimit_full_errno(LOG_WARNING, error, __VA_ARGS__)
+#define log_ratelimit_error_errno(error, ...)     log_ratelimit_full_errno(LOG_ERR,     error, __VA_ARGS__)
+#define log_ratelimit_emergency_errno(error, ...) log_ratelimit_full_errno(log_emergency_level(), error, __VA_ARGS__)
index d54e59fdf970cf37dad85ed6284488b8b092daf5..6ad89da541bbc219d2baa8071c81158e64a079c0 100644 (file)
@@ -363,6 +363,20 @@ static inline int missing_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info)
 
 /* ======================================================================= */
 
+#if !HAVE_RT_TGSIGQUEUEINFO
+static inline int missing_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, siginfo_t *info) {
+#  if defined __NR_rt_tgsigqueueinfo && __NR_rt_tgsigqueueinfo >= 0
+        return syscall(__NR_rt_tgsigqueueinfo, tgid, tid, sig, info);
+#  else
+#    error "__NR_rt_tgsigqueueinfo not defined"
+#  endif
+}
+
+#  define rt_tgsigqueueinfo missing_rt_tgsigqueueinfo
+#endif
+
+/* ======================================================================= */
+
 #if !HAVE_EXECVEAT
 static inline int missing_execveat(int dirfd, const char *pathname,
                                    char *const argv[], char *const envp[],
index dbafc8c4c94544fc0f1f553d20eba71dac6ca777..44b88ca75353f9b043ff043f34884e03ebd66c20 100644 (file)
 
 #include "nulstr-util.h"
 #include "string-util.h"
+#include "strv.h"
 
-const char* nulstr_get(const char *nulstr, const char *needle) {
-        const char *i;
+char** strv_parse_nulstr(const char *s, size_t l) {
+        /* l is the length of the input data, which will be split at NULs into elements of the resulting
+         * strv. Hence, the number of items in the resulting strv will be equal to one plus the number of NUL
+         * bytes in the l bytes starting at s, unless s[l-1] is NUL, in which case the final empty string is
+         * not stored in the resulting strv, and length is equal to the number of NUL bytes.
+         *
+         * Note that contrary to a normal nulstr which cannot contain empty strings, because the input data
+         * is terminated by any two consequent NUL bytes, this parser accepts empty strings in s. */
+
+        _cleanup_strv_free_ char **v = NULL;
+        size_t c = 0, i = 0;
+
+        assert(s || l <= 0);
+
+        if (l <= 0)
+                return new0(char*, 1);
+
+        for (const char *p = s; p < s + l; p++)
+                if (*p == 0)
+                        c++;
+
+        if (s[l-1] != 0)
+                c++;
+
+        v = new0(char*, c+1);
+        if (!v)
+                return NULL;
+
+        for (const char *p = s; p < s + l; ) {
+                const char *e;
+
+                e = memchr(p, 0, s + l - p);
+
+                v[i] = memdup_suffix0(p, e ? e - p : s + l - p);
+                if (!v[i])
+                        return NULL;
+
+                i++;
+
+                if (!e)
+                        break;
+
+                p = e + 1;
+        }
+
+        assert(i == c);
+
+        return TAKE_PTR(v);
+}
+
+char** strv_split_nulstr(const char *s) {
+        _cleanup_strv_free_ char **l = NULL;
 
+        /* This parses a nulstr, without specification of size, and stops at an empty string. This cannot
+         * parse nulstrs with embedded empty strings hence, as an empty string is an end marker. Use
+         * strv_parse_nulstr() above to parse a nulstr with embedded empty strings (which however requires a
+         * size to be specified) */
+
+        NULSTR_FOREACH(i, s)
+                if (strv_extend(&l, i) < 0)
+                        return NULL;
+
+        return l ? TAKE_PTR(l) : strv_new(NULL);
+}
+
+int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
+        /* Builds a nulstr and returns it together with the size. An extra NUL byte will be appended (⚠️ but
+         * not included in the size! ⚠️). This is done so that the nulstr can be used both in
+         * strv_parse_nulstr() and in NULSTR_FOREACH()/strv_split_nulstr() contexts, i.e. with and without a
+         * size parameter. In the former case we can include empty strings, in the latter case we cannot (as
+         * that is the end marker).
+         *
+         * When NULSTR_FOREACH()/strv_split_nulstr() is used it is often assumed that the nulstr ends in two
+         * NUL bytes (which it will, if not empty). To ensure that this assumption *always* holds, we'll
+         * return a buffer with two NUL bytes in that case, but return a size of zero. */
+
+        _cleanup_free_ char *m = NULL;
+        size_t n = 0;
+
+        assert(ret);
+        assert(ret_size);
+
+        STRV_FOREACH(i, l) {
+                size_t z;
+
+                z = strlen(*i);
+
+                if (!GREEDY_REALLOC(m, n + z + 2))
+                        return -ENOMEM;
+
+                memcpy(m + n, *i, z + 1);
+                n += z + 1;
+        }
+
+        if (!m) {
+                /* return a buffer with an extra NUL, so that the assumption that we always have two trailing NULs holds */
+                m = new0(char, 2);
+                if (!m)
+                        return -ENOMEM;
+
+                n = 0;
+        } else
+                /* Make sure there is a second extra NUL at the end of resulting nulstr (not counted in return size) */
+                m[n] = '\0';
+
+        *ret = TAKE_PTR(m);
+        *ret_size = n;
+
+        return 0;
+}
+
+const char* nulstr_get(const char *nulstr, const char *needle) {
         if (!nulstr)
                 return NULL;
 
index 1d1fbc19c04284d9b64327f327b5ab280e2f9f42..19f4edd3846de365642b998a6baec702625b470b 100644 (file)
@@ -1,17 +1,36 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include <errno.h>
+#include <macro.h>
 #include <stdbool.h>
 #include <string.h>
 
 #define NULSTR_FOREACH(i, l)                                    \
-        for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
+        for (typeof(*(l)) *(i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
 
 #define NULSTR_FOREACH_PAIR(i, j, l)                             \
-        for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i))
+        for (typeof(*(l)) *(i) = (l), *(j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i))
 
 const char* nulstr_get(const char *nulstr, const char *needle);
 
 static inline bool nulstr_contains(const char *nulstr, const char *needle) {
         return nulstr_get(nulstr, needle);
 }
+
+char** strv_parse_nulstr(const char *s, size_t l);
+char** strv_split_nulstr(const char *s);
+int strv_make_nulstr(char * const *l, char **p, size_t *n);
+
+static inline int strv_from_nulstr(char ***ret, const char *nulstr) {
+        char **t;
+
+        assert(ret);
+
+        t = strv_split_nulstr(nulstr);
+        if (!t)
+                return -ENOMEM;
+
+        *ret = t;
+        return 0;
+}
index 36f386254b72e7432f73584fafc2efd89ec202be..c99e9d8786cb1d7b1f21ae1a0cab82b9ee4960ad 100644 (file)
@@ -880,7 +880,7 @@ char **env_generator_binary_paths(bool is_system) {
 }
 
 int find_portable_profile(const char *name, const char *unit, char **ret_path) {
-        const char *p, *dot;
+        const char *dot;
 
         assert(name);
         assert(ret_path);
index f6c2d662b277f4734d2b25b3c19aa900647016c8..bf93990fde1db8422f37ca8bfdf46b3a0083bff1 100644 (file)
@@ -221,33 +221,6 @@ int path_make_relative_parent(const char *from_child, const char *to, char **ret
         return path_make_relative(from, to, ret);
 }
 
-int path_make_relative_cwd(const char *p, char **ret) {
-        char *c;
-        int r;
-
-        assert(p);
-        assert(ret);
-
-        if (path_is_absolute(p)) {
-                _cleanup_free_ char *cwd = NULL;
-
-                r = safe_getcwd(&cwd);
-                if (r < 0)
-                        return r;
-
-                r = path_make_relative(cwd, p, &c);
-                if (r < 0)
-                        return r;
-        } else {
-                c = strdup(p);
-                if (!c)
-                        return -ENOMEM;
-        }
-
-        *ret = TAKE_PTR(c);
-        return 0;
-}
-
 char* path_startswith_strv(const char *p, char **set) {
         STRV_FOREACH(s, set) {
                 char *t;
index f93455df3b1347c809d4f73ba5918ecc216ccec5..22d3632e6ef2c4f24959bd6ab0bd1e23f6e5a87c 100644 (file)
@@ -62,7 +62,6 @@ int safe_getcwd(char **ret);
 int path_make_absolute_cwd(const char *p, char **ret);
 int path_make_relative(const char *from, const char *to, char **ret);
 int path_make_relative_parent(const char *from_child, const char *to, char **ret);
-int path_make_relative_cwd(const char *from, char **ret);
 char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_;
 static inline char* path_startswith(const char *path, const char *prefix) {
         return path_startswith_full(path, prefix, true);
index dd913bc3238e6a9fe90a6aee57d22bc11124d83c..183ff8b84eb4f7b408047f82028b3bc909ffccf5 100644 (file)
@@ -37,6 +37,7 @@
 #include "missing_sched.h"
 #include "missing_syscall.h"
 #include "namespace-util.h"
+#include "nulstr-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
index 2d99807272f3002baa06a0f301093af10a503de6..b1a4d10971fce86fe54161a3f0db2dfff8640dde 100644 (file)
@@ -23,6 +23,7 @@ static inline uint32_t random_u32(void) {
 /* Some limits on the pool sizes when we deal with the kernel random pool */
 #define RANDOM_POOL_SIZE_MIN 32U
 #define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U)
+#define RANDOM_EFI_SEED_SIZE 32U
 
 size_t random_pool_size(void);
 
index d570b1df4783583bfce01464b61b3858c4a9dba7..7e5a493f6b03b57e75c6be10368e588e39f75222 100644 (file)
@@ -10,6 +10,7 @@
 #include "missing_syscall.h"
 #include "process-util.h"
 #include "sigbus.h"
+#include "signal-util.h"
 
 #define SIGBUS_QUEUE_MAX 64
 
@@ -102,7 +103,7 @@ static void sigbus_handler(int sn, siginfo_t *si, void *data) {
 
         if (si->si_code != BUS_ADRERR || !si->si_addr) {
                 assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0);
-                rt_sigqueueinfo(getpid_cached(), SIGBUS, si);
+                propagate_signal(sn, si);
                 return;
         }
 
index b61c18b2dec14970c78078cb98d90d401b515d41..7875ca69bb7d2444764dbebbbfff647e2f6a16e1 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "errno-util.h"
 #include "macro.h"
+#include "missing_syscall.h"
 #include "parse-util.h"
 #include "signal-util.h"
 #include "stdio-util.h"
@@ -282,3 +283,20 @@ int pop_pending_signal_internal(int sig, ...) {
 
         return r; /* Returns the signal popped */
 }
+
+void propagate_signal(int sig, siginfo_t *siginfo) {
+        pid_t p;
+
+        /* To be called from a signal handler. Will raise the same signal again, in our process + in our threads.
+         *
+         * Note that we use raw_getpid() instead of getpid_cached(). We might have forked with raw_clone()
+         * earlier (see PID 1), and hence let's go to the raw syscall here. In particular as this is not
+         * performance sensitive code.
+         *
+         * Note that we use kill() rather than raise() as fallback, for similar reasons. */
+
+        p = raw_getpid();
+
+        if (rt_tgsigqueueinfo(p, gettid(), sig, siginfo) < 0)
+                assert_se(kill(p, sig) >= 0);
+}
index 36372c19bd8a989d910764b3330c2509d97b5558..ad2ba841c6eccfffeb38ef2e50a650dc0c00648e 100644 (file)
@@ -65,3 +65,5 @@ int signal_is_blocked(int sig);
 
 int pop_pending_signal_internal(int sig, ...);
 #define pop_pending_signal(...) pop_pending_signal_internal(__VA_ARGS__, -1)
+
+void propagate_signal(int sig, siginfo_t *siginfo);
index 51adaca9d070ce10d0838a3801d460586bc83fd5..97dbcaac66a3348bd7a1b87985f0a532c7c683c2 100644 (file)
@@ -15,6 +15,7 @@
 #include "fileio.h"
 #include "filesystems.h"
 #include "fs-util.h"
+#include "hash-funcs.h"
 #include "macro.h"
 #include "missing_fs.h"
 #include "missing_magic.h"
@@ -441,3 +442,20 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s
 
         return 0;
 }
+
+void inode_hash_func(const struct stat *q, struct siphash *state) {
+        siphash24_compress(&q->st_dev, sizeof(q->st_dev), state);
+        siphash24_compress(&q->st_ino, sizeof(q->st_ino), state);
+}
+
+int inode_compare_func(const struct stat *a, const struct stat *b) {
+        int r;
+
+        r = CMP(a->st_dev, b->st_dev);
+        if (r != 0)
+                return r;
+
+        return CMP(a->st_ino, b->st_ino);
+}
+
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_hash_ops, struct stat, inode_hash_func, inode_compare_func, free);
index f9519d8cbde3591cb142d059238a641e5751f449..de11c0cf7c2b9503fdb8a68f2fccb324e13530b1 100644 (file)
@@ -11,6 +11,7 @@
 
 #include "macro.h"
 #include "missing_stat.h"
+#include "siphash24.h"
 
 int is_symlink(const char *path);
 int is_dir_full(int atfd, const char *fname, bool follow);
@@ -96,3 +97,7 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s
                 struct new_statx nsx;           \
         } var
 #endif
+
+void inode_hash_func(const struct stat *q, struct siphash *state);
+int inode_compare_func(const struct stat *a, const struct stat *b);
+extern const struct hash_ops inode_hash_ops;
index eea34ca68d9a98f6317b190b5927f3536c40e7e6..74e87046cca9dac00e30a8361a6021acb33f184d 100644 (file)
@@ -623,121 +623,6 @@ char** strv_remove(char **l, const char *s) {
         return l;
 }
 
-char** strv_parse_nulstr(const char *s, size_t l) {
-        /* l is the length of the input data, which will be split at NULs into
-         * elements of the resulting strv. Hence, the number of items in the resulting strv
-         * will be equal to one plus the number of NUL bytes in the l bytes starting at s,
-         * unless s[l-1] is NUL, in which case the final empty string is not stored in
-         * the resulting strv, and length is equal to the number of NUL bytes.
-         *
-         * Note that contrary to a normal nulstr which cannot contain empty strings, because
-         * the input data is terminated by any two consequent NUL bytes, this parser accepts
-         * empty strings in s.
-         */
-
-        size_t c = 0, i = 0;
-        char **v;
-
-        assert(s || l <= 0);
-
-        if (l <= 0)
-                return new0(char*, 1);
-
-        for (const char *p = s; p < s + l; p++)
-                if (*p == 0)
-                        c++;
-
-        if (s[l-1] != 0)
-                c++;
-
-        v = new0(char*, c+1);
-        if (!v)
-                return NULL;
-
-        for (const char *p = s; p < s + l; ) {
-                const char *e;
-
-                e = memchr(p, 0, s + l - p);
-
-                v[i] = strndup(p, e ? e - p : s + l - p);
-                if (!v[i]) {
-                        strv_free(v);
-                        return NULL;
-                }
-
-                i++;
-
-                if (!e)
-                        break;
-
-                p = e + 1;
-        }
-
-        assert(i == c);
-
-        return v;
-}
-
-char** strv_split_nulstr(const char *s) {
-        const char *i;
-        char **r = NULL;
-
-        NULSTR_FOREACH(i, s)
-                if (strv_extend(&r, i) < 0) {
-                        strv_free(r);
-                        return NULL;
-                }
-
-        if (!r)
-                return strv_new(NULL);
-
-        return r;
-}
-
-int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
-        /* A valid nulstr with two NULs at the end will be created, but
-         * q will be the length without the two trailing NULs. Thus the output
-         * string is a valid nulstr and can be iterated over using NULSTR_FOREACH,
-         * and can also be parsed by strv_parse_nulstr as long as the length
-         * is provided separately.
-         */
-
-        _cleanup_free_ char *m = NULL;
-        size_t n = 0;
-
-        assert(ret);
-        assert(ret_size);
-
-        STRV_FOREACH(i, l) {
-                size_t z;
-
-                z = strlen(*i);
-
-                if (!GREEDY_REALLOC(m, n + z + 2))
-                        return -ENOMEM;
-
-                memcpy(m + n, *i, z + 1);
-                n += z + 1;
-        }
-
-        if (!m) {
-                m = new0(char, 1);
-                if (!m)
-                        return -ENOMEM;
-                n = 1;
-        } else
-                /* make sure there is a second extra NUL at the end of resulting nulstr */
-                m[n] = '\0';
-
-        assert(n > 0);
-        *ret = m;
-        *ret_size = n - 1;
-
-        m = NULL;
-
-        return 0;
-}
-
 bool strv_overlap(char * const *a, char * const *b) {
         STRV_FOREACH(i, a)
                 if (strv_contains(b, *i))
index d6f5ac6ba581958471b9499c356020b59ba061b1..87a7038a54744ab18b9cf4b89a00ed86db4e5a78 100644 (file)
@@ -124,20 +124,6 @@ static inline char *strv_join(char * const *l, const char *separator) {
         return strv_join_full(l, separator, NULL, false);
 }
 
-char** strv_parse_nulstr(const char *s, size_t l);
-char** strv_split_nulstr(const char *s);
-int strv_make_nulstr(char * const *l, char **p, size_t *n);
-
-static inline int strv_from_nulstr(char ***a, const char *nulstr) {
-        char **t;
-
-        t = strv_split_nulstr(nulstr);
-        if (!t)
-                return -ENOMEM;
-        *a = t;
-        return 0;
-}
-
 bool strv_overlap(char * const *a, char * const *b) _pure_;
 
 #define _STRV_FOREACH_BACKWARDS(s, l, h, i)                             \
index e0e58472d37314b0e763ee51f6357c9f68b23e10..dbbd54027e6ac74295f272e9a9aa7bce0531cdfc 100644 (file)
 #include "tmpfile-util.h"
 #include "umask-util.h"
 
-int fopen_temporary_at(int dir_fd, const char *path, FILE **ret_file, char **ret_temp_path) {
+static int fopen_temporary_internal(int dir_fd, const char *path, FILE **ret_file) {
         _cleanup_fclose_ FILE *f = NULL;
-        _cleanup_free_ char *t = NULL;
         _cleanup_close_ int fd = -1;
         int r;
 
-        if (path) {
-                r = tempfn_random(path, NULL, &t);
-                if (r < 0)
-                        return r;
-        } else {
-                const char *d;
-
-                r = tmp_dir(&d);
-                if (r < 0)
-                        return r;
-
-                r = tempfn_random_child(d, NULL, &t);
-                if (r < 0)
-                        return r;
-        }
+        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+        assert(path);
 
-        fd = openat(dir_fd, t, O_CLOEXEC|O_NOCTTY|O_RDWR|O_CREAT|O_EXCL, 0600);
+        fd = openat(dir_fd, path, O_CLOEXEC|O_NOCTTY|O_RDWR|O_CREAT|O_EXCL, 0600);
         if (fd < 0)
                 return -errno;
 
@@ -50,15 +36,59 @@ int fopen_temporary_at(int dir_fd, const char *path, FILE **ret_file, char **ret
 
         r = take_fdopen_unlocked(&fd, "w", &f);
         if (r < 0) {
-                (void) unlinkat(dir_fd, t, 0);
+                (void) unlinkat(dir_fd, path, 0);
                 return r;
         }
 
         if (ret_file)
                 *ret_file = TAKE_PTR(f);
 
-        if (ret_temp_path)
-                *ret_temp_path = TAKE_PTR(t);
+        return 0;
+}
+
+int fopen_temporary_at(int dir_fd, const char *path, FILE **ret_file, char **ret_path) {
+        _cleanup_free_ char *t = NULL;
+        int r;
+
+        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+        assert(path);
+
+        r = tempfn_random(path, NULL, &t);
+        if (r < 0)
+                return r;
+
+        r = fopen_temporary_internal(dir_fd, t, ret_file);
+        if (r < 0)
+                return r;
+
+        if (ret_path)
+                *ret_path = TAKE_PTR(t);
+
+        return 0;
+}
+
+int fopen_temporary_child_at(int dir_fd, const char *path, FILE **ret_file, char **ret_path) {
+        _cleanup_free_ char *t = NULL;
+        int r;
+
+        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+
+        if (!path) {
+                r = tmp_dir(&path);
+                if (r < 0)
+                        return r;
+        }
+
+        r = tempfn_random_child(path, NULL, &t);
+        if (r < 0)
+                return r;
+
+        r = fopen_temporary_internal(dir_fd, t, ret_file);
+        if (r < 0)
+                return r;
+
+        if (ret_path)
+                *ret_path = TAKE_PTR(t);
 
         return 0;
 }
index 4af28b9da34be3dd92690b95d4a925e79dbaf5bd..e5b7709e3f9dee234ef47944a359e19e7f85fe3a 100644 (file)
@@ -8,6 +8,12 @@ int fopen_temporary_at(int dir_fd, const char *path, FILE **ret_file, char **ret
 static inline int fopen_temporary(const char *path, FILE **ret_file, char **ret_path) {
         return fopen_temporary_at(AT_FDCWD, path, ret_file, ret_path);
 }
+
+int fopen_temporary_child_at(int dir_fd, const char *path, FILE **ret_file, char **ret_path);
+static inline int fopen_temporary_child(const char *path, FILE **ret_file, char **ret_path) {
+        return fopen_temporary_child_at(AT_FDCWD, path, ret_file, ret_path);
+}
+
 int mkostemp_safe(char *pattern);
 int fmkostemp_safe(char *pattern, const char *mode, FILE**_f);
 
index 79b26765373c6d5d4ef5d879d3e6feb40c6d8d72..2565f54586e960fd1a0fa8fa8cfd4c72d5b9584c 100644 (file)
@@ -484,7 +484,7 @@ static int enumerate_binaries(
         assert(previous);
         assert(is_first);
 
-        r = chase_symlinks_and_opendir(path, esp_path, CHASE_PREFIX_ROOT, &p, &d);
+        r = chase_symlinks_and_opendir(path, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &p, &d);
         if (r == -ENOENT)
                 return 0;
         if (r < 0)
@@ -585,12 +585,16 @@ static int print_efi_option(uint16_t id, int *n_printed, bool in_order) {
         assert(n_printed);
 
         r = efi_get_boot_option(id, &title, &partition, &path, &active);
+        if (r == -ENOENT) {
+                log_debug_errno(r, "Boot option 0x%04X referenced but missing, ignoring: %m", id);
+                return 0;
+        }
         if (r < 0)
-                return log_error_errno(r, "Failed to read boot option %u: %m", id);
+                return log_error_errno(r, "Failed to read boot option 0x%04X: %m", id);
 
         /* print only configured entries with partition information */
         if (!path || sd_id128_is_null(partition)) {
-                log_debug("Ignoring boot entry %u without partition information.", id);
+                log_debug("Ignoring boot entry 0x%04X without partition information.", id);
                 return 0;
         }
 
@@ -913,10 +917,10 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
         if (!p)
                 return log_oom();
 
-        r = chase_symlinks(p, root, CHASE_PREFIX_ROOT, &source_path, NULL);
+        r = chase_symlinks(p, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &source_path, NULL);
         /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
         if (r == -ENOENT && root && arg_install_source == ARG_INSTALL_SOURCE_AUTO)
-                r = chase_symlinks(p, NULL, CHASE_PREFIX_ROOT, &source_path, NULL);
+                r = chase_symlinks(p, NULL, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &source_path, NULL);
         if (r < 0)
                 return log_error_errno(r,
                                        "Failed to resolve path %s%s%s: %m",
@@ -928,7 +932,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
         if (!q)
                 return log_oom();
 
-        r = chase_symlinks(q, esp_path, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &dest_path, NULL);
+        r = chase_symlinks(q, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_NONEXISTENT, &dest_path, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to resolve path %s under directory %s: %m", q, esp_path);
 
@@ -945,7 +949,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
                 v = strjoina("/EFI/BOOT/BOOT", e);
                 ascii_strupper(strrchr(v, '/') + 1);
 
-                r = chase_symlinks(v, esp_path, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &default_dest_path, NULL);
+                r = chase_symlinks(v, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_NONEXISTENT, &default_dest_path, NULL);
                 if (r < 0)
                         return log_error_errno(r, "Failed to resolve path %s under directory %s: %m", v, esp_path);
 
@@ -963,10 +967,10 @@ static int install_binaries(const char *esp_path, const char *arch, bool force)
         _cleanup_free_ char *path = NULL;
         int r;
 
-        r = chase_symlinks_and_opendir(BOOTLIBDIR, root, CHASE_PREFIX_ROOT, &path, &d);
+        r = chase_symlinks_and_opendir(BOOTLIBDIR, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &path, &d);
         /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
         if (r == -ENOENT && root && arg_install_source == ARG_INSTALL_SOURCE_AUTO)
-                r = chase_symlinks_and_opendir(BOOTLIBDIR, NULL, CHASE_PREFIX_ROOT, &path, &d);
+                r = chase_symlinks_and_opendir(BOOTLIBDIR, NULL, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &path, &d);
         if (r < 0)
                 return log_error_errno(r, "Failed to open boot loader directory %s%s: %m", strempty(root), BOOTLIBDIR);
 
@@ -1136,7 +1140,7 @@ static int install_variables(
                 return 0;
         }
 
-        r = chase_symlinks_and_access(path, esp_path, CHASE_PREFIX_ROOT, F_OK, NULL, NULL);
+        r = chase_symlinks_and_access(path, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, NULL, NULL);
         if (r == -ENOENT)
                 return 0;
         if (r < 0)
@@ -1167,7 +1171,7 @@ static int remove_boot_efi(const char *esp_path) {
         _cleanup_free_ char *p = NULL;
         int r, c = 0;
 
-        r = chase_symlinks_and_opendir("/EFI/BOOT", esp_path, CHASE_PREFIX_ROOT, &p, &d);
+        r = chase_symlinks_and_opendir("/EFI/BOOT", esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &p, &d);
         if (r == -ENOENT)
                 return 0;
         if (r < 0)
@@ -1886,8 +1890,6 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                 printf("\n");
 
                 printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
-                have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderRandomSeed)), F_OK) >= 0;
-                printf(" Passed to OS: %s\n", yes_no(have));
                 have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), F_OK) >= 0;
                 printf(" System Token: %s\n", have ? "set" : "not set");
 
@@ -1977,10 +1979,10 @@ static int verb_list(int argc, char *argv[], void *userdata) {
 
 static int install_random_seed(const char *esp) {
         _cleanup_(unlink_and_freep) char *tmp = NULL;
-        _cleanup_free_ void *buffer = NULL;
+        uint8_t buffer[RANDOM_EFI_SEED_SIZE];
         _cleanup_free_ char *path = NULL;
         _cleanup_close_ int fd = -1;
-        size_t sz, token_size;
+        size_t token_size;
         ssize_t n;
         int r;
 
@@ -1990,13 +1992,7 @@ static int install_random_seed(const char *esp) {
         if (!path)
                 return log_oom();
 
-        sz = random_pool_size();
-
-        buffer = malloc(sz);
-        if (!buffer)
-                return log_oom();
-
-        r = crypto_random_bytes(buffer, sz);
+        r = crypto_random_bytes(buffer, sizeof(buffer));
         if (r < 0)
                 return log_error_errno(r, "Failed to acquire random seed: %m");
 
@@ -2017,10 +2013,10 @@ static int install_random_seed(const char *esp) {
                 return log_error_errno(fd, "Failed to open random seed file for writing: %m");
         }
 
-        n = write(fd, buffer, sz);
+        n = write(fd, buffer, sizeof(buffer));
         if (n < 0)
                 return log_error_errno(errno, "Failed to write random seed file: %m");
-        if ((size_t) n != sz)
+        if ((size_t) n != sizeof(buffer))
                 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while writing random seed file.");
 
         if (rename(tmp, path) < 0)
@@ -2028,7 +2024,7 @@ static int install_random_seed(const char *esp) {
 
         tmp = mfree(tmp);
 
-        log_info("Random seed file %s successfully written (%zu bytes).", path, sz);
+        log_info("Random seed file %s successfully written (%zu bytes).", path, sizeof(buffer));
 
         if (!arg_touch_variables)
                 return 0;
@@ -2080,16 +2076,16 @@ static int install_random_seed(const char *esp) {
                 if (r != -ENOENT)
                         return log_error_errno(r, "Failed to test system token validity: %m");
         } else {
-                if (token_size >= sz) {
+                if (token_size >= sizeof(buffer)) {
                         /* Let's avoid writes if we can, and initialize this only once. */
                         log_debug("System token already written, not updating.");
                         return 0;
                 }
 
-                log_debug("Existing system token size (%zu) does not match our expectations (%zu), replacing.", token_size, sz);
+                log_debug("Existing system token size (%zu) does not match our expectations (%zu), replacing.", token_size, sizeof(buffer));
         }
 
-        r = crypto_random_bytes(buffer, sz);
+        r = crypto_random_bytes(buffer, sizeof(buffer));
         if (r < 0)
                 return log_error_errno(r, "Failed to acquire random seed: %m");
 
@@ -2097,7 +2093,7 @@ static int install_random_seed(const char *esp) {
          * and possibly get identification information or too much insight into the kernel's entropy pool
          * state. */
         RUN_WITH_UMASK(0077) {
-                r = efi_set_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), buffer, sz);
+                r = efi_set_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), buffer, sizeof(buffer));
                 if (r < 0) {
                         if (!arg_graceful)
                                 return log_error_errno(r, "Failed to write 'LoaderSystemToken' EFI variable: %m");
@@ -2107,7 +2103,7 @@ static int install_random_seed(const char *esp) {
                         else
                                 log_warning_errno(r, "Unable to write 'LoaderSystemToken' EFI variable, ignoring: %m");
                 } else
-                        log_info("Successfully initialized system token in EFI variable with %zu bytes.", sz);
+                        log_info("Successfully initialized system token in EFI variable with %zu bytes.", sizeof(buffer));
         }
 
         return 0;
index 4150b16ecf9bbd3b1f04d7c867f2205c21377cf4..76023b14cadf4cb9df58eb38750ed72c57d07291 100644 (file)
@@ -16,6 +16,7 @@
 #include "linux.h"
 #include "measure.h"
 #include "pe.h"
+#include "vmm.h"
 #include "random-seed.h"
 #include "secure-boot.h"
 #include "shim.h"
@@ -2646,6 +2647,13 @@ static void config_load_all_entries(
         config_default_entry_select(config);
 }
 
+static EFI_STATUS discover_root_dir(EFI_LOADED_IMAGE_PROTOCOL *loaded_image, EFI_FILE **ret_dir) {
+        if (is_direct_boot(loaded_image->DeviceHandle))
+                return vmm_open(&loaded_image->DeviceHandle, ret_dir);
+        else
+                return open_volume(loaded_image->DeviceHandle, ret_dir);
+}
+
 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
         _cleanup_(file_closep) EFI_FILE *root_dir = NULL;
@@ -2678,11 +2686,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
 
         err = device_path_to_str(loaded_image->FilePath, &loaded_image_path);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Error getting loaded image path: %m");
+                return log_error_status_stall(err, L"Error getting loaded image path: %r", err);
 
         export_variables(loaded_image, loaded_image_path, init_usec);
 
-        err = open_volume(loaded_image->DeviceHandle, &root_dir);
+        err = discover_root_dir(loaded_image, &root_dir);
         if (err != EFI_SUCCESS)
                 return log_error_status_stall(err, L"Unable to open root directory: %r", err);
 
index 39b65e74a6f413d11d21df8c0c06d3006bbe4a99..7f2057f5a15e89a3bacd78a855d12e21228bb8d7 100644 (file)
@@ -51,25 +51,23 @@ static EFI_STATUS load_one_driver(
 }
 
 EFI_STATUS reconnect_all_drivers(void) {
-          _cleanup_free_ EFI_HANDLE *handles = NULL;
-          UINTN n_handles = 0;
-          EFI_STATUS err;
+        _cleanup_free_ EFI_HANDLE *handles = NULL;
+        size_t n_handles = 0;
+        EFI_STATUS err;
 
-          /* Reconnects all handles, so that any loaded drivers can take effect. */
+        /* Reconnects all handles, so that any loaded drivers can take effect. */
 
-          err = BS->LocateHandleBuffer(AllHandles, NULL, NULL, &n_handles, &handles);
-          if (err != EFI_SUCCESS)
-                  return log_error_status_stall(err, L"Failed to get list of handles: %r", err);
+        err = BS->LocateHandleBuffer(AllHandles, NULL, NULL, &n_handles, &handles);
+        if (err != EFI_SUCCESS)
+                return log_error_status_stall(err, L"Failed to get list of handles: %r", err);
 
-          for (UINTN i = 0; i < n_handles; i++) {
-                  err = BS->ConnectController(handles[i], NULL, NULL, true);
-                  if (err == EFI_NOT_FOUND) /* No drivers for this handle */
-                          continue;
-                  if (err != EFI_SUCCESS)
-                          log_error_status_stall(err, L"Failed to reconnect handle %" PRIuN L", ignoring: %r", i, err);
-          }
+        for (size_t i = 0; i < n_handles; i++)
+                /* Some firmware gives us some bogus handles (or they might become bad due to
+                 * reconnecting everything). Security policy may also prevent us from doing so too.
+                 * There is nothing we can realistically do on errors anyways, so just ignore them. */
+                (void) BS->ConnectController(handles[i], NULL, NULL, true);
 
-          return EFI_SUCCESS;
+        return EFI_SUCCESS;
 }
 
 EFI_STATUS load_drivers(
index 1ebd5fd6b7d42b3c0821af465f085706b483f070..d4d76a7c18875a087c4687683b8bd0ead6387737 100644 (file)
@@ -119,6 +119,13 @@ static inline void *mempcpy(void * restrict dest, const void * restrict src, siz
         memcpy(dest, src, n);
         return (uint8_t *) dest + n;
 }
+
+static inline void explicit_bzero_safe(void *bytes, size_t len) {
+        if (!bytes || len == 0)
+                return;
+        memset(bytes, 0, len);
+        __asm__ __volatile__("": :"r"(bytes) :"memory");
+}
 #else
 /* For unit testing. */
 int efi_memcmp(const void *p1, const void *p2, size_t n);
index 395386d3eda70a74e1bb08ac8005dac41f371252..0de43993a4155f521476fd7797f32be6e996f231 100644 (file)
@@ -389,6 +389,7 @@ systemd_boot_sources = files(
         'boot.c',
         'drivers.c',
         'random-seed.c',
+        'vmm.c',
         'shim.c',
         'xbootldr.c',
 )
index aea4f7e532624372976f3568f9b1c4c8c111092f..e6a317860d8bacd819933a19d647a11524749d23 100644 (file)
 
 #define EFI_RNG_GUID &(const EFI_GUID) EFI_RNG_PROTOCOL_GUID
 
+struct linux_efi_random_seed {
+        uint32_t size;
+        uint8_t seed[];
+};
+
+#define LINUX_EFI_RANDOM_SEED_TABLE_GUID \
+        { 0x1ce1e5bc, 0x7ceb, 0x42f2,  { 0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b } }
+
 /* SHA256 gives us 256/8=32 bytes */
 #define HASH_VALUE_SIZE 32
 
-static EFI_STATUS acquire_rng(UINTN size, void **ret) {
-        _cleanup_free_ void *data = NULL;
+/* Linux's RNG is 256 bits, so let's provide this much */
+#define DESIRED_SEED_SIZE 32
+
+/* Some basic domain separation in case somebody uses this data elsewhere */
+#define HASH_LABEL "systemd-boot random seed label v1"
+
+static EFI_STATUS acquire_rng(void *ret, UINTN size) {
         EFI_RNG_PROTOCOL *rng;
         EFI_STATUS err;
 
@@ -32,126 +45,9 @@ static EFI_STATUS acquire_rng(UINTN size, void **ret) {
         if (!rng)
                 return EFI_UNSUPPORTED;
 
-        data = xmalloc(size);
-
-        err = rng->GetRNG(rng, NULL, size, data);
+        err = rng->GetRNG(rng, NULL, size, ret);
         if (err != EFI_SUCCESS)
                 return log_error_status_stall(err, L"Failed to acquire RNG data: %r", err);
-
-        *ret = TAKE_PTR(data);
-        return EFI_SUCCESS;
-}
-
-static void hash_once(
-                const void *old_seed,
-                const void *rng,
-                UINTN size,
-                const void *system_token,
-                UINTN system_token_size,
-                uint64_t uefi_monotonic_counter,
-                UINTN counter,
-                uint8_t ret[static HASH_VALUE_SIZE]) {
-
-        /* This hashes together:
-         *
-         *      1. The contents of the old seed file
-         *      2. Some random data acquired from the UEFI RNG (optional)
-         *      3. Some 'system token' the installer installed as EFI variable (optional)
-         *      4. The UEFI "monotonic counter" that increases with each boot
-         *      5. A supplied counter value
-         *
-         * And writes the result to the specified buffer.
-         */
-
-        struct sha256_ctx hash;
-
-        assert(old_seed);
-        assert(system_token_size == 0 || system_token);
-
-        sha256_init_ctx(&hash);
-        sha256_process_bytes(old_seed, size, &hash);
-        if (rng)
-                sha256_process_bytes(rng, size, &hash);
-        if (system_token_size > 0)
-                sha256_process_bytes(system_token, system_token_size, &hash);
-        sha256_process_bytes(&uefi_monotonic_counter, sizeof(uefi_monotonic_counter), &hash);
-        sha256_process_bytes(&counter, sizeof(counter), &hash);
-        sha256_finish_ctx(&hash, ret);
-}
-
-static EFI_STATUS hash_many(
-                const void *old_seed,
-                const void *rng,
-                UINTN size,
-                const void *system_token,
-                UINTN system_token_size,
-                uint64_t uefi_monotonic_counter,
-                UINTN counter_start,
-                UINTN n,
-                void **ret) {
-
-        _cleanup_free_ void *output = NULL;
-
-        assert(old_seed);
-        assert(system_token_size == 0 || system_token);
-        assert(ret);
-
-        /* Hashes the specified parameters in counter mode, generating n hash values, with the counter in the
-         * range counter_start…counter_start+n-1. */
-
-        output = xmalloc_multiply(HASH_VALUE_SIZE, n);
-
-        for (UINTN i = 0; i < n; i++)
-                hash_once(old_seed, rng, size,
-                          system_token, system_token_size,
-                          uefi_monotonic_counter,
-                          counter_start + i,
-                          (uint8_t*) output + (i * HASH_VALUE_SIZE));
-
-        *ret = TAKE_PTR(output);
-        return EFI_SUCCESS;
-}
-
-static EFI_STATUS mangle_random_seed(
-                const void *old_seed,
-                const void *rng,
-                UINTN size,
-                const void *system_token,
-                UINTN system_token_size,
-                uint64_t uefi_monotonic_counter,
-                void **ret_new_seed,
-                void **ret_for_kernel) {
-
-        _cleanup_free_ void *new_seed = NULL, *for_kernel = NULL;
-        EFI_STATUS err;
-        UINTN n;
-
-        assert(old_seed);
-        assert(system_token_size == 0 || system_token);
-        assert(ret_new_seed);
-        assert(ret_for_kernel);
-
-        /* This takes the old seed file contents, an (optional) random number acquired from the UEFI RNG, an
-         * (optional) system 'token' installed once by the OS installer in an EFI variable, and hashes them
-         * together in counter mode, generating a new seed (to replace the file on disk) and the seed for the
-         * kernel. To keep things simple, the new seed and kernel data have the same size as the old seed and
-         * RNG data. */
-
-        n = (size + HASH_VALUE_SIZE - 1) / HASH_VALUE_SIZE;
-
-        /* Begin hashing in counter mode at counter 0 for the new seed for the disk */
-        err = hash_many(old_seed, rng, size, system_token, system_token_size, uefi_monotonic_counter, 0, n, &new_seed);
-        if (err != EFI_SUCCESS)
-                return err;
-
-        /* Continue counting at 'n' for the seed for the kernel */
-        err = hash_many(old_seed, rng, size, system_token, system_token_size, uefi_monotonic_counter, n, n, &for_kernel);
-        if (err != EFI_SUCCESS)
-                return err;
-
-        *ret_new_seed = TAKE_PTR(new_seed);
-        *ret_for_kernel = TAKE_PTR(for_kernel);
-
         return EFI_SUCCESS;
 }
 
@@ -221,31 +117,84 @@ static void validate_sha256(void) {
 }
 
 EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
-        _cleanup_free_ void *seed = NULL, *new_seed = NULL, *rng = NULL, *for_kernel = NULL, *system_token = NULL;
+        _cleanup_erase_ uint8_t random_bytes[DESIRED_SEED_SIZE], hash_key[HASH_VALUE_SIZE];
+        _cleanup_free_ struct linux_efi_random_seed *new_seed_table = NULL;
+        struct linux_efi_random_seed *previous_seed_table = NULL;
+        _cleanup_free_ void *seed = NULL, *system_token = NULL;
         _cleanup_(file_closep) EFI_FILE *handle = NULL;
-        UINTN size, rsize, wsize, system_token_size = 0;
         _cleanup_free_ EFI_FILE_INFO *info = NULL;
+        _cleanup_erase_ struct sha256_ctx hash;
         uint64_t uefi_monotonic_counter = 0;
+        size_t size, rsize, wsize;
+        bool seeded_by_efi = false;
         EFI_STATUS err;
+        EFI_TIME now;
 
         assert(root_dir);
+        assert_cc(DESIRED_SEED_SIZE == HASH_VALUE_SIZE);
 
         validate_sha256();
 
         if (mode == RANDOM_SEED_OFF)
                 return EFI_NOT_FOUND;
 
-        /* Let's better be safe than sorry, and for now disable this logic in SecureBoot mode, so that we
-         * don't credit a random seed that is not authenticated. */
-        if (secure_boot_enabled())
-                return EFI_NOT_FOUND;
+        /* hash = LABEL || sizeof(input1) || input1 || ... || sizeof(inputN) || inputN */
+        sha256_init_ctx(&hash);
+
+        /* Some basic domain separation in case somebody uses this data elsewhere */
+        sha256_process_bytes(HASH_LABEL, sizeof(HASH_LABEL) - 1, &hash);
+
+        for (size_t i = 0; i < ST->NumberOfTableEntries; ++i)
+                if (memcmp(&(const EFI_GUID)LINUX_EFI_RANDOM_SEED_TABLE_GUID,
+                           &ST->ConfigurationTable[i].VendorGuid, sizeof(EFI_GUID)) == 0) {
+                        previous_seed_table = ST->ConfigurationTable[i].VendorTable;
+                        break;
+                }
+        if (!previous_seed_table) {
+                size = 0;
+                sha256_process_bytes(&size, sizeof(size), &hash);
+        } else {
+                size = previous_seed_table->size;
+                seeded_by_efi = size >= DESIRED_SEED_SIZE;
+                sha256_process_bytes(&size, sizeof(size), &hash);
+                sha256_process_bytes(previous_seed_table->seed, size, &hash);
+
+                /* Zero and free the previous seed table only at the end after we've managed to install a new
+                 * one, so that in case this function fails or aborts, Linux still receives whatever the
+                 * previous bootloader chain set. So, the next line of this block is not an explicit_bzero()
+                 * call. */
+        }
+
+        /* Request some random data from the UEFI RNG. We don't need this to work safely, but it's a good
+         * idea to use it because it helps us for cases where users mistakenly include a random seed in
+         * golden master images that are replicated many times. */
+        err = acquire_rng(random_bytes, sizeof(random_bytes));
+        if (err != EFI_SUCCESS) {
+                size = 0;
+                /* If we can't get any randomness from EFI itself, then we'll only be relying on what's in
+                 * ESP. But ESP is mutable, so if secure boot is enabled, we probably shouldn't trust that
+                 * alone, in which case we bail out early. */
+                if (!seeded_by_efi && secure_boot_enabled())
+                        return EFI_NOT_FOUND;
+        } else {
+                seeded_by_efi = true;
+                size = sizeof(random_bytes);
+        }
+        sha256_process_bytes(&size, sizeof(size), &hash);
+        sha256_process_bytes(random_bytes, size, &hash);
 
         /* Get some system specific seed that the installer might have placed in an EFI variable. We include
          * it in our hash. This is protection against golden master image sloppiness, and it remains on the
          * system, even when disk images are duplicated or swapped out. */
-        err = acquire_system_token(&system_token, &system_token_size);
-        if (mode != RANDOM_SEED_ALWAYS && err != EFI_SUCCESS)
+        size = 0;
+        err = acquire_system_token(&system_token, &size);
+        if (mode != RANDOM_SEED_ALWAYS && (err != EFI_SUCCESS || size < DESIRED_SEED_SIZE) && !seeded_by_efi)
                 return err;
+        sha256_process_bytes(&size, sizeof(size), &hash);
+        if (system_token) {
+                sha256_process_bytes(system_token, size, &hash);
+                explicit_bzero_safe(system_token, size);
+        }
 
         err = root_dir->Open(
                         root_dir,
@@ -261,7 +210,7 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
 
         err = get_file_info_harder(handle, &info, NULL);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to get file info for random seed: %r");
+                return log_error_status_stall(err, L"Failed to get file info for random seed: %r", err);
 
         size = info->FileSize;
         if (size < RANDOM_MAX_SIZE_MIN)
@@ -271,51 +220,114 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
                 return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too large.");
 
         seed = xmalloc(size);
-
         rsize = size;
         err = handle->Read(handle, &rsize, seed);
         if (err != EFI_SUCCESS)
                 return log_error_status_stall(err, L"Failed to read random seed file: %r", err);
-        if (rsize != size)
+        if (rsize != size) {
+                explicit_bzero_safe(seed, rsize);
                 return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short read on random seed file.");
+        }
+
+        sha256_process_bytes(&size, sizeof(size), &hash);
+        sha256_process_bytes(seed, size, &hash);
+        explicit_bzero_safe(seed, size);
 
         err = handle->SetPosition(handle, 0);
         if (err != EFI_SUCCESS)
                 return log_error_status_stall(err, L"Failed to seek to beginning of random seed file: %r", err);
 
-        /* Request some random data from the UEFI RNG. We don't need this to work safely, but it's a good
-         * idea to use it because it helps us for cases where users mistakenly include a random seed in
-         * golden master images that are replicated many times. */
-        (void) acquire_rng(size, &rng); /* It's fine if this fails */
-
         /* Let's also include the UEFI monotonic counter (which is supposedly increasing on every single
          * boot) in the hash, so that even if the changes to the ESP for some reason should not be
          * persistent, the random seed we generate will still be different on every single boot. */
         err = BS->GetNextMonotonicCount(&uefi_monotonic_counter);
-        if (err != EFI_SUCCESS)
+        if (err != EFI_SUCCESS && !seeded_by_efi)
                 return log_error_status_stall(err, L"Failed to acquire UEFI monotonic counter: %r", err);
+        size = sizeof(uefi_monotonic_counter);
+        sha256_process_bytes(&size, sizeof(size), &hash);
+        sha256_process_bytes(&uefi_monotonic_counter, size, &hash);
 
-        /* Calculate new random seed for the disk and what to pass to the kernel */
-        err = mangle_random_seed(seed, rng, size, system_token, system_token_size, uefi_monotonic_counter, &new_seed, &for_kernel);
-        if (err != EFI_SUCCESS)
-                return err;
+        err = RT->GetTime(&now, NULL);
+        size = err == EFI_SUCCESS ? sizeof(now) : 0; /* Known to be flaky, so don't bark on error. */
+        sha256_process_bytes(&size, sizeof(size), &hash);
+        sha256_process_bytes(&now, size, &hash);
+
+        /* hash_key = HASH(hash) */
+        sha256_finish_ctx(&hash, hash_key);
 
+        /* hash = hash_key || 0 */
+        sha256_init_ctx(&hash);
+        sha256_process_bytes(hash_key, sizeof(hash_key), &hash);
+        sha256_process_bytes(&(const uint8_t){ 0 }, sizeof(uint8_t), &hash);
+        /* random_bytes = HASH(hash) */
+        sha256_finish_ctx(&hash, random_bytes);
+
+        size = sizeof(random_bytes);
+        /* If the file size is too large, zero out the remaining bytes on disk. */
+        if (size < info->FileSize) {
+                err = handle->SetPosition(handle, size);
+                if (err != EFI_SUCCESS)
+                        return log_error_status_stall(err, L"Failed to seek to offset of random seed file: %r", err);
+                wsize = info->FileSize - size;
+                err = handle->Write(handle, &wsize, seed /* All zeros now */);
+                if (err != EFI_SUCCESS)
+                        return log_error_status_stall(err, L"Failed to write random seed file: %r", err);
+                if (wsize != info->FileSize - size)
+                        return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short write on random seed file.");
+                err = handle->Flush(handle);
+                if (err != EFI_SUCCESS)
+                        return log_error_status_stall(err, L"Failed to flush random seed file: %r", err);
+                err = handle->SetPosition(handle, 0);
+                if (err != EFI_SUCCESS)
+                        return log_error_status_stall(err, L"Failed to seek to beginning of random seed file: %r", err);
+
+                /* We could truncate the file here with something like:
+                 *
+                 *     info->FileSize = size;
+                 *     err = handle->SetInfo(handle, &GenericFileInfo, info->Size, info);
+                 *     if (err != EFI_SUCCESS)
+                 *             return log_error_status_stall(err, L"Failed to truncate random seed file: %r", err);
+                 *
+                 * But this is considered slightly risky, because EFI filesystem drivers are a little bit
+                 * flimsy. So instead we rely on userspace eventually truncating this when it writes a new
+                 * seed. For now the best we do is zero it. */
+        }
         /* Update the random seed on disk before we use it */
         wsize = size;
-        err = handle->Write(handle, &wsize, new_seed);
+        err = handle->Write(handle, &wsize, random_bytes);
         if (err != EFI_SUCCESS)
                 return log_error_status_stall(err, L"Failed to write random seed file: %r", err);
         if (wsize != size)
                 return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short write on random seed file.");
-
         err = handle->Flush(handle);
         if (err != EFI_SUCCESS)
                 return log_error_status_stall(err, L"Failed to flush random seed file: %r", err);
 
-        /* We are good to go */
-        err = efivar_set_raw(LOADER_GUID, L"LoaderRandomSeed", for_kernel, size, 0);
+        err = BS->AllocatePool(EfiACPIReclaimMemory,
+                               offsetof(struct linux_efi_random_seed, seed) + DESIRED_SEED_SIZE,
+                               (void **) &new_seed_table);
+        if (err != EFI_SUCCESS)
+                return log_error_status_stall(err, L"Failed to allocate EFI table for random seed: %r", err);
+        new_seed_table->size = DESIRED_SEED_SIZE;
+
+        /* hash = hash_key || 1 */
+        sha256_init_ctx(&hash);
+        sha256_process_bytes(hash_key, sizeof(hash_key), &hash);
+        sha256_process_bytes(&(const uint8_t){ 1 }, sizeof(uint8_t), &hash);
+        /* new_seed_table->seed = HASH(hash) */
+        sha256_finish_ctx(&hash, new_seed_table->seed);
+
+        err = BS->InstallConfigurationTable(&(EFI_GUID)LINUX_EFI_RANDOM_SEED_TABLE_GUID, new_seed_table);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to write random seed to EFI variable: %r", err);
+                return log_error_status_stall(err, L"Failed to install EFI table for random seed: %r", err);
+        TAKE_PTR(new_seed_table);
+
+        if (previous_seed_table) {
+                /* Now that we've succeeded in installing the new table, we can safely nuke the old one. */
+                explicit_bzero_safe(previous_seed_table->seed, previous_seed_table->size);
+                explicit_bzero_safe(previous_seed_table, sizeof(*previous_seed_table));
+                free(previous_seed_table);
+        }
 
         return EFI_SUCCESS;
 }
index b33c50f9fc7e27ddf701f3f641d18cd06c4bf432..4c5b6cab13b2c6f3c7db5bb7be770bd38ffa78f9 100644 (file)
 #define UINTN_MAX (~(UINTN)0)
 #define INTN_MAX ((INTN)(UINTN_MAX>>1))
 
+#ifndef __has_attribute
+#define __has_attribute(x) 0
+#endif
+#if __has_attribute(__error__)
+__attribute__((noreturn)) extern void __assert_cl_failure__(void) __attribute__((__error__("compile-time assertion failed")));
+#else
+__attribute__((noreturn)) extern void __assert_cl_failure__(void);
+#endif
+/* assert_cl generates a later-stage compile-time assertion when constant folding occurs. */
+#define assert_cl(condition) ({ if (!(condition)) __assert_cl_failure__(); })
+
 /* gnu-efi format specifiers for integers are fixed to either 64bit with 'l' and 32bit without a size prefix.
  * We rely on %u/%d/%x to format regular ints, so ensure the size is what we expect. At the same time, we also
  * need specifiers for (U)INTN which are native (pointer) sized. */
@@ -43,6 +54,20 @@ static inline void freep(void *p) {
 
 #define _cleanup_free_ _cleanup_(freep)
 
+static __always_inline void erase_obj(void *p) {
+#ifdef __OPTIMIZE__
+        size_t l;
+        assert_cl(p);
+        l = __builtin_object_size(p, 0);
+        assert_cl(l != (size_t) -1);
+        explicit_bzero_safe(p, l);
+#else
+#warning "Object will not be erased with -O0; do not release to production."
+#endif
+}
+
+#define _cleanup_erase_ _cleanup_(erase_obj)
+
 _malloc_ _alloc_(1) _returns_nonnull_ _warn_unused_result_
 static inline void *xmalloc(size_t size) {
         void *p;
diff --git a/src/boot/efi/vmm.c b/src/boot/efi/vmm.c
new file mode 100644 (file)
index 0000000..b1bfd77
--- /dev/null
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <efi.h>
+#include <efilib.h>
+#include <stdbool.h>
+
+#include "drivers.h"
+#include "efi-string.h"
+#include "string-util-fundamental.h"
+#include "util.h"
+
+#define QEMU_KERNEL_LOADER_FS_MEDIA_GUID                                \
+        { 0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7 }}
+
+#define VMM_BOOT_ORDER_GUID \
+        { 0x668f4529, 0x63d0, 0x4bb5, {0xb6, 0x5d, 0x6f, 0xbb, 0x9d, 0x36, 0xa4, 0x4a }}
+
+/* detect direct boot */
+bool is_direct_boot(EFI_HANDLE device) {
+        EFI_STATUS err;
+        VENDOR_DEVICE_PATH *dp;
+
+        err = BS->HandleProtocol(device, &DevicePathProtocol, (void **) &dp);
+        if (err != EFI_SUCCESS)
+                return false;
+
+        /* 'qemu -kernel systemd-bootx64.efi' */
+        if (dp->Header.Type == MEDIA_DEVICE_PATH &&
+            dp->Header.SubType == MEDIA_VENDOR_DP &&
+            memcmp(&dp->Guid, &(EFI_GUID)QEMU_KERNEL_LOADER_FS_MEDIA_GUID, sizeof(EFI_GUID)) == 0)
+                return true;
+
+        /* loaded from firmware volume (sd-boot added to ovmf) */
+        if (dp->Header.Type == MEDIA_DEVICE_PATH &&
+            dp->Header.SubType == MEDIA_PIWG_FW_VOL_DP)
+                return true;
+
+        return false;
+}
+
+static bool device_path_startswith(const EFI_DEVICE_PATH *dp, const EFI_DEVICE_PATH *start) {
+        if (!start)
+                return true;
+        if (!dp)
+                return false;
+        for (;;) {
+                if (IsDevicePathEnd(start))
+                        return true;
+                if (IsDevicePathEnd(dp))
+                        return false;
+                size_t l1 = DevicePathNodeLength(start);
+                size_t l2 = DevicePathNodeLength(dp);
+                if (l1 != l2)
+                        return false;
+                if (memcmp(dp, start, l1) != 0)
+                        return false;
+                start = NextDevicePathNode(start);
+                dp    = NextDevicePathNode(dp);
+        }
+}
+
+/*
+ * Try find ESP when not loaded from ESP
+ *
+ * Inspect all filesystems known to the firmware, try find the ESP.  In case VMMBootOrderNNNN variables are
+ * present they are used to inspect the filesystems in the specified order.  When nothing was found or the
+ * variables are not present the function will do one final search pass over all filesystems.
+ *
+ * Recent OVMF builds store the qemu boot order (as specified using the bootindex property on the qemu
+ * command line) in VMMBootOrderNNNN.  The variables contain a device path.
+ *
+ * Example qemu command line:
+ *     qemu -virtio-scsi-pci,addr=14.0 -device scsi-cd,scsi-id=4,bootindex=1
+ *
+ * Resulting variable:
+ *     VMMBootOrder0000 = PciRoot(0x0)/Pci(0x14,0x0)/Scsi(0x4,0x0)
+ */
+EFI_STATUS vmm_open(EFI_HANDLE *ret_vmm_dev, EFI_FILE **ret_vmm_dir) {
+        _cleanup_free_ EFI_HANDLE *handles = NULL;
+        size_t n_handles;
+        EFI_STATUS err, dp_err;
+
+        assert(ret_vmm_dev);
+        assert(ret_vmm_dir);
+
+        /* find all file system handles */
+        err = BS->LocateHandleBuffer(ByProtocol, &FileSystemProtocol, NULL, &n_handles, &handles);
+        if (err != EFI_SUCCESS)
+                return err;
+
+        for (size_t order = 0;; order++) {
+                _cleanup_free_ EFI_DEVICE_PATH *dp = NULL;
+                char16_t order_str[STRLEN("VMMBootOrder") + 4 + 1];
+
+                SPrint(order_str, sizeof(order_str), u"VMMBootOrder%04x", order);
+                dp_err = efivar_get_raw(&(EFI_GUID)VMM_BOOT_ORDER_GUID, order_str, (char**)&dp, NULL);
+
+                for (size_t i = 0; i < n_handles; i++) {
+                        _cleanup_(file_closep) EFI_FILE *root_dir = NULL, *efi_dir = NULL;
+                        EFI_DEVICE_PATH *fs;
+
+                        err = BS->HandleProtocol(handles[i], &DevicePathProtocol, (void **) &fs);
+                        if (err != EFI_SUCCESS)
+                                return err;
+
+                        /* check against VMMBootOrderNNNN (if set) */
+                        if (dp_err == EFI_SUCCESS && !device_path_startswith(fs, dp))
+                                continue;
+
+                        err = open_volume(handles[i], &root_dir);
+                        if (err != EFI_SUCCESS)
+                                continue;
+
+                        /* simple ESP check */
+                        err = root_dir->Open(root_dir, &efi_dir, (char16_t*) u"\\EFI",
+                                             EFI_FILE_MODE_READ,
+                                             EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY);
+                        if (err != EFI_SUCCESS)
+                                continue;
+
+                        *ret_vmm_dev = handles[i];
+                        *ret_vmm_dir = TAKE_PTR(root_dir);
+                        return EFI_SUCCESS;
+                }
+
+                if (dp_err != EFI_SUCCESS)
+                        return EFI_NOT_FOUND;
+        }
+        assert_not_reached();
+}
diff --git a/src/boot/efi/vmm.h b/src/boot/efi/vmm.h
new file mode 100644 (file)
index 0000000..7bac1a3
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <efi.h>
+#include <efilib.h>
+
+bool is_direct_boot(EFI_HANDLE device);
+EFI_STATUS vmm_open(EFI_HANDLE *ret_qemu_dev, EFI_FILE **ret_qemu_dir);
index b9cd2853b612aadc349f164627bb78ae4f75d6bb..913cf18ee6ef554213f19aa31c73636b7d49600c 100644 (file)
@@ -898,7 +898,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
                         }
 
                         _cleanup_free_ void *sig = malloc(ss);
-                        if (!ss) {
+                        if (!sig) {
                                 r = log_oom();
                                 goto finish;
                         }
index a77a85fb2e87905de026060c164634d2f1a2a05d..9ae17092530d93430e6e2a00d759f0d80f5f7ef8 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "build.h"
 #include "efivars.h"
+#include "env-util.h"
 #include "main-func.h"
 #include "openssl-util.h"
 #include "parse-util.h"
@@ -175,21 +176,33 @@ static int run(int argc, char *argv[]) {
 
         length = strlen(word);
 
+        int b = getenv_bool("SYSTEMD_PCRPHASE_STUB_VERIFY");
+        if (b < 0 && b != -ENXIO)
+                log_warning_errno(b, "Unable to parse $SYSTEMD_PCRPHASE_STUB_VERIFY value, ignoring.");
+
         /* Skip logic if sd-stub is not used, after all PCR 11 might have a very different purpose then. */
         r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubPcrKernelImage), &pcr_string);
         if (r == -ENOENT) {
-                log_info("Kernel stub did not measure kernel image into PCR %u, skipping measurement.", TPM_PCR_INDEX_KERNEL_IMAGE);
-                return EXIT_SUCCESS;
-        }
-        if (r < 0)
+                if (b != 0) {
+                        log_info("Kernel stub did not measure kernel image into PCR %u, skipping measurement.", TPM_PCR_INDEX_KERNEL_IMAGE);
+                        return EXIT_SUCCESS;
+                } else
+                        log_notice("Kernel stub did not measure kernel image into PCR %u, but told to measure anyway, hence proceeding.", TPM_PCR_INDEX_KERNEL_IMAGE);
+        } else if (r < 0)
                 return log_error_errno(r, "Failed to read StubPcrKernelImage EFI variable: %m");
-
-        /* Let's validate that the stub announced PCR 11 as we expected. */
-        r = safe_atou(pcr_string, &pcr_nr);
-        if (r < 0)
-                return log_error_errno(r, "Failed to parse StubPcrKernelImage EFI variable: %s", pcr_string);
-        if (pcr_nr != TPM_PCR_INDEX_KERNEL_IMAGE)
-                return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "Kernel stub measured kernel image into PCR %u, which is different than expected %u.", pcr_nr, TPM_PCR_INDEX_KERNEL_IMAGE);
+        else {
+                /* Let's validate that the stub announced PCR 11 as we expected. */
+                r = safe_atou(pcr_string, &pcr_nr);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse StubPcrKernelImage EFI variable: %s", pcr_string);
+                if (pcr_nr != TPM_PCR_INDEX_KERNEL_IMAGE) {
+                        if (b != 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "Kernel stub measured kernel image into PCR %u, which is different than expected %u.", pcr_nr, TPM_PCR_INDEX_KERNEL_IMAGE);
+                        else
+                                log_notice("Kernel stub measured kernel image into PCR %u, which is different than expected %u, but told to measure anyway, hence proceeding.", pcr_nr, TPM_PCR_INDEX_KERNEL_IMAGE);
+                } else
+                        log_debug("Kernel stub reported same PCR %u as we want to use, proceeding.", TPM_PCR_INDEX_KERNEL_IMAGE);
+        }
 
         r = dlopen_tpm2();
         if (r < 0)
index 3af9e78a1e32f5e9f2987d115da3002cb0b77d92..c79cda5b76d9cacfc06339f827aaa6961b3a13d8 100644 (file)
@@ -515,7 +515,6 @@ int bpf_devices_allow_list_static(
                 "/run/systemd/inaccessible/blk\0" "rwm\0";
         int r = 0, k;
 
-        const char *node, *acc;
         NULSTR_FOREACH_PAIR(node, acc, auto_devices) {
                 k = bpf_devices_allow_list_device(prog, path, node, acc);
                 if (r >= 0 && k < 0)
index 173221b9f178eb1c0e981d5658fe74f422cc9373..de601c48d751ffce39a7ba12a6c5e0254edaf451 100644 (file)
@@ -326,7 +326,6 @@ int lsm_bpf_parse_filesystem(
 
         if (name[0] == '@') {
                 const FilesystemSet *set;
-                const char *i;
 
                 set = filesystem_set_find(name);
                 if (!set) {
index c44966839ccdf89b375b39426c486dc29dbe6f0f..1e9cb758de93a12340336419f5672ba26fb9efa7 100644 (file)
@@ -151,6 +151,7 @@ void cgroup_context_init(CGroupContext *c) {
                 .memory_high = CGROUP_LIMIT_MAX,
                 .memory_max = CGROUP_LIMIT_MAX,
                 .memory_swap_max = CGROUP_LIMIT_MAX,
+                .memory_zswap_max = CGROUP_LIMIT_MAX,
 
                 .memory_limit = CGROUP_LIMIT_MAX,
 
@@ -354,6 +355,9 @@ static int unit_compare_memory_limit(Unit *u, const char *property_name, uint64_
         } else if (streq(property_name, "MemorySwapMax")) {
                 unit_value = c->memory_swap_max;
                 file = "memory.swap.max";
+        } else if (streq(property_name, "MemoryZSwapMax")) {
+                unit_value = c->memory_zswap_max;
+                file = "memory.zswap.max";
         } else
                 return -EINVAL;
 
@@ -396,9 +400,10 @@ static char *format_cgroup_memory_limit_comparison(char *buf, size_t l, Unit *u,
 
         /* memory.swap.max is special in that it relies on CONFIG_MEMCG_SWAP (and the default swapaccount=1).
          * In the absence of reliably being able to detect whether memcg swap support is available or not,
-         * only complain if the error is not ENOENT. */
+         * only complain if the error is not ENOENT. This is similarly the case for memory.zswap.max relying
+         * on CONFIG_ZSWAP. */
         if (r > 0 || IN_SET(r, -ENODATA, -EOWNERDEAD) ||
-            (r == -ENOENT && streq(property_name, "MemorySwapMax")))
+            (r == -ENOENT && STR_IN_SET(property_name, "MemorySwapMax", "MemoryZSwapMax")))
                 buf[0] = 0;
         else if (r < 0) {
                 errno = -r;
@@ -462,6 +467,7 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
                 "%sMemoryHigh: %" PRIu64 "%s\n"
                 "%sMemoryMax: %" PRIu64 "%s\n"
                 "%sMemorySwapMax: %" PRIu64 "%s\n"
+                "%sMemoryZSwapMax: %" PRIu64 "%s\n"
                 "%sMemoryLimit: %" PRIu64 "\n"
                 "%sTasksMax: %" PRIu64 "\n"
                 "%sDevicePolicy: %s\n"
@@ -498,6 +504,7 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
                 prefix, c->memory_high, format_cgroup_memory_limit_comparison(cdc, sizeof(cdc), u, "MemoryHigh"),
                 prefix, c->memory_max, format_cgroup_memory_limit_comparison(cdd, sizeof(cdd), u, "MemoryMax"),
                 prefix, c->memory_swap_max, format_cgroup_memory_limit_comparison(cde, sizeof(cde), u, "MemorySwapMax"),
+                prefix, c->memory_zswap_max, format_cgroup_memory_limit_comparison(cde, sizeof(cde), u, "MemoryZSwapMax"),
                 prefix, c->memory_limit,
                 prefix, tasks_max_resolve(&c->tasks_max),
                 prefix, cgroup_device_policy_to_string(c->device_policy),
@@ -1209,7 +1216,7 @@ static bool unit_has_unified_memory_config(Unit *u) {
 
         return unit_get_ancestor_memory_min(u) > 0 || unit_get_ancestor_memory_low(u) > 0 ||
                c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX ||
-               c->memory_swap_max != CGROUP_LIMIT_MAX;
+               c->memory_swap_max != CGROUP_LIMIT_MAX || c->memory_zswap_max != CGROUP_LIMIT_MAX;
 }
 
 static void cgroup_apply_unified_memory_limit(Unit *u, const char *file, uint64_t v) {
@@ -1569,11 +1576,12 @@ static void cgroup_context_apply(
         if ((apply_mask & CGROUP_MASK_MEMORY) && !is_local_root) {
 
                 if (cg_all_unified() > 0) {
-                        uint64_t max, swap_max = CGROUP_LIMIT_MAX;
+                        uint64_t max, swap_max = CGROUP_LIMIT_MAX, zswap_max = CGROUP_LIMIT_MAX;
 
                         if (unit_has_unified_memory_config(u)) {
                                 max = c->memory_max;
                                 swap_max = c->memory_swap_max;
+                                zswap_max = c->memory_zswap_max;
                         } else {
                                 max = c->memory_limit;
 
@@ -1586,6 +1594,7 @@ static void cgroup_context_apply(
                         cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high);
                         cgroup_apply_unified_memory_limit(u, "memory.max", max);
                         cgroup_apply_unified_memory_limit(u, "memory.swap.max", swap_max);
+                        cgroup_apply_unified_memory_limit(u, "memory.zswap.max", zswap_max);
 
                         (void) set_attribute_and_warn(u, "memory", "memory.oom.group", one_zero(c->memory_oom_group));
 
index 4413eeaaa0afe5dd5dac24540eb35153a7188cbe..09352bafc6353b004e81a16eb5aaadf81001afcd 100644 (file)
@@ -149,6 +149,7 @@ struct CGroupContext {
         uint64_t memory_high;
         uint64_t memory_max;
         uint64_t memory_swap_max;
+        uint64_t memory_zswap_max;
 
         bool default_memory_min_set:1;
         bool default_memory_low_set:1;
index 561b7fc19c69452ad4644de6051c86343789865d..6983f2e2b7f83a84b7a73defef1e88e1cdc9b130 100644 (file)
@@ -49,7 +49,7 @@ _noreturn_ static void crash(int sig, siginfo_t *siginfo, void *context) {
 
         if (getpid_cached() != 1)
                 /* Pass this on immediately, if this is not PID 1 */
-                (void) raise(sig);
+                propagate_signal(sig, siginfo);
         else if (!arg_dump_core)
                 log_emergency("Caught <%s>, not dumping core.", signal_to_string(sig));
         else {
@@ -79,9 +79,7 @@ _noreturn_ static void crash(int sig, siginfo_t *siginfo, void *context) {
                         (void) chdir("/");
 
                         /* Raise the signal again */
-                        pid = raw_getpid();
-                        (void) kill(pid, sig); /* raise() would kill the parent */
-
+                        propagate_signal(sig, siginfo);
                         assert_not_reached();
                         _exit(EXIT_EXCEPTION);
                 } else {
index cbadb5bc447d7fa420f4f73ae0683dd5b608909d..b5484eda78ed17cc1a6679deecceaffd4fd14ee2 100644 (file)
@@ -468,6 +468,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
         SD_BUS_PROPERTY("MemoryHigh", "t", NULL, offsetof(CGroupContext, memory_high), 0),
         SD_BUS_PROPERTY("MemoryMax", "t", NULL, offsetof(CGroupContext, memory_max), 0),
         SD_BUS_PROPERTY("MemorySwapMax", "t", NULL, offsetof(CGroupContext, memory_swap_max), 0),
+        SD_BUS_PROPERTY("MemoryZSwapMax", "t", NULL, offsetof(CGroupContext, memory_zswap_max), 0),
         SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
         SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
         SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
@@ -887,6 +888,7 @@ BUS_DEFINE_SET_CGROUP_WEIGHT(blockio_weight, CGROUP_MASK_BLKIO, CGROUP_BLKIO_WEI
 BUS_DEFINE_SET_CGROUP_LIMIT(memory, CGROUP_MASK_MEMORY, physical_memory_scale, 1);
 BUS_DEFINE_SET_CGROUP_LIMIT(memory_protection, CGROUP_MASK_MEMORY, physical_memory_scale, 0);
 BUS_DEFINE_SET_CGROUP_LIMIT(swap, CGROUP_MASK_MEMORY, physical_memory_scale, 0);
+BUS_DEFINE_SET_CGROUP_LIMIT(zswap, CGROUP_MASK_MEMORY, physical_memory_scale, 0);
 REENABLE_WARNING;
 
 static int bus_cgroup_set_cpu_weight(
@@ -1075,6 +1077,9 @@ int bus_cgroup_set_property(
         if (streq(name, "MemorySwapMax"))
                 return bus_cgroup_set_swap(u, name, &c->memory_swap_max, message, flags, error);
 
+        if (streq(name, "MemoryZSwapMax"))
+                return bus_cgroup_set_zswap(u, name, &c->memory_zswap_max, message, flags, error);
+
         if (streq(name, "MemoryMax"))
                 return bus_cgroup_set_memory(u, name, &c->memory_max, message, flags, error);
 
@@ -1115,6 +1120,9 @@ int bus_cgroup_set_property(
         if (streq(name, "MemorySwapMaxScale"))
                 return bus_cgroup_set_swap_scale(u, name, &c->memory_swap_max, message, flags, error);
 
+        if (streq(name, "MemoryZSwapMaxScale"))
+                return bus_cgroup_set_zswap_scale(u, name, &c->memory_zswap_max, message, flags, error);
+
         if (streq(name, "MemoryMaxScale"))
                 return bus_cgroup_set_memory_scale(u, name, &c->memory_max, message, flags, error);
 
index 4086b12739db86620f18ddcbf46d16e2519e3477..61516775fc705f5681ab6e6a4c8150c58fecab07 100644 (file)
 #include "random-util.h"
 #include "strv.h"
 
-/* If a random seed was passed by the boot loader in the LoaderRandomSeed EFI variable, let's credit it to
- * the kernel's random pool, but only once per boot. If this is run very early during initialization we can
- * instantly boot up with a filled random pool.
- *
- * This makes no judgement on the entropy passed, it's the job of the boot loader to only pass us a seed that
- * is suitably validated. */
-
-static void lock_down_efi_variables(void) {
+void lock_down_efi_variables(void) {
+        _cleanup_close_ int fd = -1;
         int r;
 
+        fd = open(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), O_RDONLY|O_CLOEXEC);
+        if (fd < 0) {
+                if (errno != ENOENT)
+                        log_warning_errno(errno, "Unable to open LoaderSystemToken EFI variable, ignoring: %m");
+                return;
+        }
+
         /* Paranoia: let's restrict access modes of these a bit, so that unprivileged users can't use them to
          * identify the system or gain too much insight into what we might have credited to the entropy
          * pool. */
-        FOREACH_STRING(path,
-                       EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderRandomSeed)),
-                       EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken))) {
-
-                r = chattr_path(path, 0, FS_IMMUTABLE_FL, NULL);
-                if (r == -ENOENT)
-                        continue;
-                if (r < 0)
-                        log_warning_errno(r, "Failed to drop FS_IMMUTABLE_FL from %s, ignoring: %m", path);
-
-                if (chmod(path, 0600) < 0)
-                        log_warning_errno(errno, "Failed to reduce access mode of %s, ignoring: %m", path);
-        }
-}
-
-int efi_take_random_seed(void) {
-        _cleanup_free_ void *value = NULL;
-        size_t size;
-        int r;
-
-        /* Paranoia comes first. */
-        lock_down_efi_variables();
-
-        if (access("/run/systemd/efi-random-seed-taken", F_OK) < 0) {
-                if (errno != ENOENT) {
-                        log_warning_errno(errno, "Failed to determine whether we already used the random seed token, not using it.");
-                        return 0;
-                }
-
-                /* ENOENT means we haven't used it yet. */
-        } else {
-                log_debug("EFI random seed already used, not using again.");
-                return 0;
-        }
-
-        r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderRandomSeed), NULL, &value, &size);
-        if (r == -EOPNOTSUPP) {
-                log_debug_errno(r, "System lacks EFI support, not initializing random seed from EFI variable.");
-                return 0;
-        }
-        if (r == -ENOENT) {
-                log_debug_errno(r, "Boot loader did not pass LoaderRandomSeed EFI variable, not crediting any entropy.");
-                return 0;
-        }
+        r = chattr_fd(fd, 0, FS_IMMUTABLE_FL, NULL);
         if (r < 0)
-                return log_warning_errno(r, "Failed to read LoaderRandomSeed EFI variable, ignoring: %m");
-
-        if (size == 0)
-                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Random seed passed from boot loader has zero size? Ignoring.");
-
-        /* Before we use the seed, let's mark it as used, so that we never credit it twice. Also, it's a nice
-         * way to let users known that we successfully acquired entropy from the boot loader. */
-        r = touch("/run/systemd/efi-random-seed-taken");
-        if (r < 0)
-                return log_warning_errno(r, "Unable to mark EFI random seed as used, not using it: %m");
-
-        r = random_write_entropy(-1, value, size, true);
-        if (r < 0)
-                return log_warning_errno(errno, "Failed to credit entropy, ignoring: %m");
-
-        log_info("Successfully credited entropy passed from boot loader.");
-        return 1;
+                log_warning_errno(r, "Failed to drop FS_IMMUTABLE_FL from LoaderSystemToken EFI variable, ignoring: %m");
+        if (fchmod(fd, 0600) < 0)
+                log_warning_errno(errno, "Failed to reduce access mode of LoaderSystemToken EFI variable, ignoring: %m");
 }
index 7d20fff57da317d4d950cffa2f0927c352fe0340..87166c9e3f35e244e6c1464fe24834b0aae3548f 100644 (file)
@@ -1,4 +1,4 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
-int efi_take_random_seed(void);
+void lock_down_efi_variables(void);
index 966631d44eca23207cb89d98a24c50dcc880c085..dcbc28205fef1317ae40992c57a921e6a1bf857c 100644 (file)
@@ -5,6 +5,7 @@
 #include "alloc-util.h"
 #include "bus-util.h"
 #include "capability-util.h"
+#include "efi-api.h"
 #include "fileio.h"
 #include "kmod-setup.h"
 #include "macro.h"
@@ -99,27 +100,32 @@ int kmod_setup(void) {
         } kmod_table[] = {
                 /* This one we need to load explicitly, since auto-loading on use doesn't work
                  * before udev created the ghost device nodes, and we need it earlier than that. */
-                { "autofs4",   "/sys/class/misc/autofs",    true,   false,   NULL      },
+                { "autofs4",     "/sys/class/misc/autofs",    true,  false, NULL           },
 
                 /* This one we need to load explicitly, since auto-loading of IPv6 is not done when
                  * we try to configure ::1 on the loopback device. */
-                { "ipv6",      "/sys/module/ipv6",          false,  true,    NULL      },
+                { "ipv6",        "/sys/module/ipv6",          false, true,  NULL           },
 
                 /* This should never be a module */
-                { "unix",      "/proc/net/unix",            true,   true,    NULL      },
+                { "unix",        "/proc/net/unix",            true,  true,  NULL           },
 
 #if HAVE_LIBIPTC
                 /* netfilter is needed by networkd, nspawn among others, and cannot be autoloaded */
-                { "ip_tables", "/proc/net/ip_tables_names", false,  false,   NULL      },
+                { "ip_tables",   "/proc/net/ip_tables_names", false, false, NULL           },
 #endif
                 /* virtio_rng would be loaded by udev later, but real entropy might be needed very early */
-                { "virtio_rng", NULL,                       false,  false,   has_virtio_rng },
+                { "virtio_rng",  NULL,                        false, false, has_virtio_rng },
 
                 /* qemu_fw_cfg would be loaded by udev later, but we want to import credentials from it super early */
-                { "qemu_fw_cfg", "/sys/firmware/qemu_fw_cfg", false, false,  in_qemu   },
+                { "qemu_fw_cfg", "/sys/firmware/qemu_fw_cfg", false, false, in_qemu        },
 
                 /* dmi-sysfs is needed to import credentials from it super early */
-                { "dmi-sysfs", "/sys/firmware/dmi/entries", false, false,  NULL   },
+                { "dmi-sysfs",   "/sys/firmware/dmi/entries", false, false, NULL           },
+
+#if HAVE_TPM2
+                /* Make sure the tpm subsystem is available which ConditionSecurity=tpm2 depends on. */
+                { "tpm",         "/sys/class/tpmrm",          false, false, efi_has_tpm2   },
+#endif
         };
         _cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
         unsigned i;
index 7675b7bb2e74619f59f0db8b52beadc1a436d097..bba6666a5281d45a55d3b451ae9079285c5bd1e6 100644 (file)
 {{type}}.MemoryHigh,                       config_parse_memory_limit,                   0,                                  offsetof({{type}}, cgroup_context)
 {{type}}.MemoryMax,                        config_parse_memory_limit,                   0,                                  offsetof({{type}}, cgroup_context)
 {{type}}.MemorySwapMax,                    config_parse_memory_limit,                   0,                                  offsetof({{type}}, cgroup_context)
+{{type}}.MemoryZSwapMax,                   config_parse_memory_limit,                   0,                                  offsetof({{type}}, cgroup_context)
 {{type}}.MemoryLimit,                      config_parse_memory_limit,                   0,                                  offsetof({{type}}, cgroup_context)
 {{type}}.DeviceAllow,                      config_parse_device_allow,                   0,                                  offsetof({{type}}, cgroup_context)
 {{type}}.DevicePolicy,                     config_parse_device_policy,                  0,                                  offsetof({{type}}, cgroup_context.device_policy)
index 1a5895346d96633561d2a624068b91871e193386..734a5941cc134d322b1b149f44f63290f55c80c5 100644 (file)
@@ -3826,7 +3826,7 @@ int config_parse_memory_limit(
                         bytes = physical_memory_scale(r, 10000U);
 
                 if (bytes >= UINT64_MAX ||
-                    (bytes <= 0 && !STR_IN_SET(lvalue, "MemorySwapMax", "MemoryLow", "MemoryMin", "DefaultMemoryLow", "DefaultMemoryMin"))) {
+                    (bytes <= 0 && !STR_IN_SET(lvalue, "MemorySwapMax", "MemoryZSwapMax", "MemoryLow", "MemoryMin", "DefaultMemoryLow", "DefaultMemoryMin"))) {
                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Memory limit '%s' out of range, ignoring.", rvalue);
                         return 0;
                 }
@@ -3850,6 +3850,8 @@ int config_parse_memory_limit(
                 c->memory_max = bytes;
         else if (streq(lvalue, "MemorySwapMax"))
                 c->memory_swap_max = bytes;
+        else if (streq(lvalue, "MemoryZSwapMax"))
+                c->memory_zswap_max = bytes;
         else if (streq(lvalue, "MemoryLimit")) {
                 log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Unit uses MemoryLimit=; please use MemoryMax= instead. Support for MemoryLimit= will be removed soon.");
@@ -6226,7 +6228,6 @@ void unit_dump_config_items(FILE *f) {
         };
 
         const char *prev = NULL;
-        const char *i;
 
         assert(f);
 
index cc725e6c42af33d20ceed618413d1303fedac07a..119c518664ff78877bebd87a4a055ae11e2d7971 100644 (file)
@@ -2831,8 +2831,8 @@ int main(int argc, char *argv[]) {
                         goto finish;
                 }
 
-                /* The efivarfs is now mounted, let's read the random seed off it */
-                (void) efi_take_random_seed();
+                /* The efivarfs is now mounted, let's lock down the system token. */
+                lock_down_efi_variables();
 
                 /* Cache command-line options passed from EFI variables */
                 if (!skip_setup)
index 852be3bdde82880e43a931841865ff06e7356491..7752e48fb0bfbb8f0e8cc0a4033f60722e556f58 100644 (file)
@@ -916,7 +916,7 @@ static int mount_private_dev(MountEntry *m) {
                 "/dev/tty\0";
 
         char temporary_mount[] = "/tmp/namespace-dev-XXXXXX";
-        const char *d, *dev = NULL, *devpts = NULL, *devshm = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL;
+        const char *dev = NULL, *devpts = NULL, *devshm = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL;
         bool can_mknod = true;
         int r;
 
index 7aa36b4b032f54b1139719db78d0b8a68e771eb3..90b5ab21a991d5df63f691eca77a85aa189c56fc 100644 (file)
@@ -494,7 +494,7 @@ static char* disk_description(const char *path) {
                 "ID_MODEL\0";
 
         _cleanup_(sd_device_unrefp) sd_device *device = NULL;
-        const char *i, *name;
+        const char *name;
         struct stat st;
 
         assert(path);
index da648bc837dbfb40ee21cf301e9f3d208e12a5c5..db6e957fc9d51ee9a083a641629033a29e0197ed 100644 (file)
@@ -371,7 +371,6 @@ static int enumerate_dir(
 static int should_skip_path(const char *prefix, const char *suffix) {
 #if HAVE_SPLIT_USR
         _cleanup_free_ char *target = NULL, *dirname = NULL;
-        const char *p;
 
         dirname = path_join(prefix, suffix);
         if (!dirname)
@@ -400,7 +399,6 @@ static int should_skip_path(const char *prefix, const char *suffix) {
 }
 
 static int process_suffix(const char *suffix, const char *onlyprefix) {
-        const char *p;
         char *f, *key;
         OrderedHashmap *top, *bottom, *drops, *h;
         int r = 0, k, n_found = 0;
@@ -477,7 +475,6 @@ finish:
 }
 
 static int process_suffixes(const char *onlyprefix) {
-        const char *n;
         int n_found = 0, r;
 
         NULSTR_FOREACH(n, suffixes) {
@@ -492,8 +489,6 @@ static int process_suffixes(const char *onlyprefix) {
 }
 
 static int process_suffix_chop(const char *arg) {
-        const char *p;
-
         assert(arg);
 
         if (!path_is_absolute(arg))
index 81764690e9975c53fe3f476e8f4c2a5c61bc8ccd..4ff86ba1dedc0aadaff0493f3a252f687dfac531 100644 (file)
@@ -568,7 +568,7 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
                 pager_open(arg_pager_flags);
 
         if (arg_json_format_flags & JSON_FORMAT_OFF)
-                printf("      Name: %s\n", bn);
+                printf("      Name: %s%s%s\n", ansi_highlight(), bn, ansi_normal());
 
         if (ioctl(d->fd, BLKGETSIZE64, &size) < 0)
                 log_debug_errno(errno, "Failed to query size of loopback device: %m");
@@ -594,6 +594,9 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
         else if (arg_json_format_flags & JSON_FORMAT_OFF) {
                 _cleanup_strv_free_ char **sysext_scopes = NULL;
 
+                if (!sd_id128_is_null(m->image_uuid))
+                        printf("Image UUID: %s\n", SD_ID128_TO_UUID_STRING(m->image_uuid));
+
                 if (m->hostname)
                         printf("  Hostname: %s\n", m->hostname);
 
@@ -673,6 +676,7 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
 
                 r = json_build(&v, JSON_BUILD_OBJECT(
                                                JSON_BUILD_PAIR("name", JSON_BUILD_STRING(bn)),
+                                               JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->image_uuid), "imageUuid", JSON_BUILD_UUID(m->image_uuid)),
                                                JSON_BUILD_PAIR("size", JSON_BUILD_INTEGER(size)),
                                                JSON_BUILD_PAIR_CONDITION(m->hostname, "hostname", JSON_BUILD_STRING(m->hostname)),
                                                JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->machine_id), "machineId", JSON_BUILD_ID128(m->machine_id)),
@@ -1122,9 +1126,9 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) {
                                 if (errno != ENOENT)
                                         return log_error_errno(errno, "Failed to open destination '%s': %m", arg_target);
 
-                                r = copy_tree_at(source_fd, ".", dfd, bn, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS);
+                                r = copy_tree_at(source_fd, ".", dfd, bn, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS, NULL);
                         } else
-                                r = copy_tree_at(source_fd, ".", target_fd, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS);
+                                r = copy_tree_at(source_fd, ".", target_fd, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS, NULL);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to copy '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image);
 
index 712ab3ffa9ea730b0a78aacdd7dc7bede8806599..10956cc5482b4ecd4d5251db50347039e09b4ab0 100644 (file)
@@ -55,7 +55,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
 
         size_t sw_alloc = MAX(h->sw_alloc, 1u);
         buf2 = malloc(sw_alloc);
-        if (!buf) {
+        if (!buf2) {
                 log_oom();
                 return 0;
         }
index e79def3daec75a3c5215eb9c9f45b3b815fa813d..8ad1da6303d55acab38479c390b49223d81cacfa 100644 (file)
@@ -64,7 +64,7 @@ int home_setup_cifs(
                 pid_t mount_pid;
                 int exit_status;
 
-                r = fopen_temporary(NULL, &f, &p);
+                r = fopen_temporary_child(NULL, &f, &p);
                 if (r < 0)
                         return log_error_errno(r, "Failed to create temporary credentials file: %m");
 
index 5106961f38bdef6ec55b3967699f166c9b99dca7..f29c55c8952b05dc5859efbc777f7f0f3f13e43a 100644 (file)
@@ -216,7 +216,6 @@ static int fscrypt_setup(
                 size_t *ret_volume_key_size) {
 
         _cleanup_free_ char *xattr_buf = NULL;
-        const char *xa;
         int r;
 
         assert(setup);
@@ -646,7 +645,6 @@ int home_passwd_fscrypt(
         _cleanup_free_ char *xattr_buf = NULL;
         size_t volume_key_size = 0;
         uint32_t slot = 0;
-        const char *xa;
         int r;
 
         assert(h);
index c41866cd41b182959d9ffb141b630635471ba9c0..bc38437f2cf6cee030bfd6b57abd7e74142dd3e9 100644 (file)
@@ -1036,7 +1036,7 @@ static int copy_skel(int root_fd, const char *skel) {
 
         assert(root_fd >= 0);
 
-        r = copy_tree_at(AT_FDCWD, skel, root_fd, ".", UID_INVALID, GID_INVALID, COPY_MERGE|COPY_REPLACE);
+        r = copy_tree_at(AT_FDCWD, skel, root_fd, ".", UID_INVALID, GID_INVALID, COPY_MERGE|COPY_REPLACE, NULL);
         if (r == -ENOENT) {
                 log_info("Skeleton directory %s missing, ignoring.", skel);
                 return 0;
index 826826d70b9437989034f2170bfd6d5d7b58d208..52606d2f4046094ba374147c14cc4be4c3283067 100644 (file)
@@ -82,20 +82,17 @@ static int print_status_info(StatusInfo *i) {
 
         assert(i);
 
-        table = table_new("key", "value");
+        table = table_new_vertical();
         if (!table)
                 return log_oom();
 
         assert_se(cell = table_get_cell(table, 0, 0));
         (void) table_set_ellipsize_percent(table, cell, 100);
-        (void) table_set_align_percent(table, cell, 100);
-
-        table_set_header(table, false);
 
         table_set_ersatz_string(table, TABLE_ERSATZ_UNSET);
 
         r = table_add_many(table,
-                           TABLE_STRING, "Static hostname:",
+                           TABLE_FIELD, "Static hostname",
                            TABLE_STRING, i->static_hostname);
         if (r < 0)
                 return table_log_add_error(r);
@@ -103,7 +100,7 @@ static int print_status_info(StatusInfo *i) {
         if (!isempty(i->pretty_hostname) &&
             !streq_ptr(i->pretty_hostname, i->static_hostname)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Pretty hostname:",
+                                   TABLE_FIELD, "Pretty hostname",
                                    TABLE_STRING, i->pretty_hostname);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -112,7 +109,7 @@ static int print_status_info(StatusInfo *i) {
         if (!isempty(i->hostname) &&
             !streq_ptr(i->hostname, i->static_hostname)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Transient hostname:",
+                                   TABLE_FIELD, "Transient hostname",
                                    TABLE_STRING, i->hostname);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -120,7 +117,7 @@ static int print_status_info(StatusInfo *i) {
 
         if (!isempty(i->icon_name)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Icon name:",
+                                   TABLE_FIELD, "Icon name",
                                    TABLE_STRING, i->icon_name);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -134,7 +131,7 @@ static int print_status_info(StatusInfo *i) {
                         v = strjoina(i->chassis, " ", v);
 
                 r = table_add_many(table,
-                                   TABLE_STRING, "Chassis:",
+                                   TABLE_FIELD, "Chassis",
                                    TABLE_STRING, v ?: i->chassis);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -142,7 +139,7 @@ static int print_status_info(StatusInfo *i) {
 
         if (!isempty(i->deployment)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Deployment:",
+                                   TABLE_FIELD, "Deployment",
                                    TABLE_STRING, i->deployment);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -150,7 +147,7 @@ static int print_status_info(StatusInfo *i) {
 
         if (!isempty(i->location)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Location:",
+                                   TABLE_FIELD, "Location",
                                    TABLE_STRING, i->location);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -159,7 +156,7 @@ static int print_status_info(StatusInfo *i) {
         r = sd_id128_get_machine(&mid);
         if (r >= 0) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Machine ID:",
+                                   TABLE_FIELD, "Machine ID",
                                    TABLE_ID128, mid);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -168,7 +165,7 @@ static int print_status_info(StatusInfo *i) {
         r = sd_id128_get_boot(&bid);
         if (r >= 0) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Boot ID:",
+                                   TABLE_FIELD, "Boot ID",
                                    TABLE_ID128, bid);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -176,7 +173,7 @@ static int print_status_info(StatusInfo *i) {
 
         if (!isempty(i->virtualization)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Virtualization:",
+                                   TABLE_FIELD, "Virtualization",
                                    TABLE_STRING, i->virtualization);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -184,7 +181,7 @@ static int print_status_info(StatusInfo *i) {
 
         if (!isempty(i->os_pretty_name)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Operating System:",
+                                   TABLE_FIELD, "Operating System",
                                    TABLE_STRING, i->os_pretty_name,
                                    TABLE_SET_URL, i->home_url);
                 if (r < 0)
@@ -193,7 +190,7 @@ static int print_status_info(StatusInfo *i) {
 
         if (!isempty(i->os_cpe_name)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "CPE OS Name:",
+                                   TABLE_FIELD, "CPE OS Name",
                                    TABLE_STRING, i->os_cpe_name);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -204,7 +201,7 @@ static int print_status_info(StatusInfo *i) {
 
                 v = strjoina(i->kernel_name, " ", i->kernel_release);
                 r = table_add_many(table,
-                                   TABLE_STRING, "Kernel:",
+                                   TABLE_FIELD, "Kernel",
                                    TABLE_STRING, v);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -212,7 +209,7 @@ static int print_status_info(StatusInfo *i) {
 
         if (!isempty(i->architecture)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Architecture:",
+                                   TABLE_FIELD, "Architecture",
                                    TABLE_STRING, i->architecture);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -220,7 +217,7 @@ static int print_status_info(StatusInfo *i) {
 
         if (!isempty(i->hardware_vendor)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Hardware Vendor:",
+                                   TABLE_FIELD, "Hardware Vendor",
                                    TABLE_STRING, i->hardware_vendor);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -228,7 +225,7 @@ static int print_status_info(StatusInfo *i) {
 
         if (!isempty(i->hardware_model)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Hardware Model:",
+                                   TABLE_FIELD, "Hardware Model",
                                    TABLE_STRING, i->hardware_model);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -236,7 +233,7 @@ static int print_status_info(StatusInfo *i) {
 
         if (!isempty(i->firmware_version)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Firmware Version:",
+                                   TABLE_FIELD, "Firmware Version",
                                    TABLE_STRING, i->firmware_version);
                 if (r < 0)
                         return table_log_add_error(r);
index af88e315bbcfca56442043e6fa96ceab17572fe2..53a24348d5c0b68dfb1a43836c4de99605f3d214 100644 (file)
@@ -123,10 +123,13 @@ static int verb_show(int argc, char **argv, void *userdata) {
                         if (have_uuid)
                                 id = gpt_partition_type_uuid_to_string(uuid) ?: "XYZ";
                         else {
-                                r = gpt_partition_type_uuid_from_string(*p, &uuid);
+                                GptPartitionType type;
+
+                                r = gpt_partition_type_from_string(*p, &type);
                                 if (r < 0)
                                         return log_error_errno(r, "Unknown identifier \"%s\".", *p);
 
+                                uuid = type.uuid;
                                 id = *p;
                         }
 
index cf18461db30e070c8b58122eae5c8a0c6337705f..d3a1179ebc7616eb803cdd05b3f72ad785d4246f 100644 (file)
@@ -245,7 +245,7 @@ static int tar_pull_make_local_copy(TarPull *i) {
                                 BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
                                 BTRFS_SNAPSHOT_RECURSIVE);
         else
-                r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS);
+                r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to create local image: %m");
 
index 7e7d1b56b1b3127641c077e80779f613b4577e5a..df183354694117d1bf4a533539424469bed5093c 100644 (file)
@@ -64,11 +64,11 @@ void microhttpd_logger(void *arg, const char *fmt, va_list ap) _printf_(2, 0);
 
 int mhd_respondf(struct MHD_Connection *connection,
                  int error,
-                 unsigned code,
+                 enum MHD_RequestTerminationCode code,
                  const char *format, ...) _printf_(4,5);
 
 int mhd_respond(struct MHD_Connection *connection,
-                unsigned code,
+                enum MHD_RequestTerminationCode code,
                 const char *message);
 
 int mhd_respond_oom(struct MHD_Connection *connection);
index eb8106cbb41fadb28a11ef69d1de862ac1d5a0d9..ba5636af141d38ccda6453e86c5391e033f87d5c 100644 (file)
@@ -1512,7 +1512,6 @@ static int get_possible_units(
                 Set **units) {
 
         _cleanup_set_free_free_ Set *found = NULL;
-        const char *field;
         int r;
 
         found = set_new(&string_hash_ops);
index 3e87a93a9ec161a62317ceaf553c508072d5e2e3..d301d2896621d981231bcb1973cb630be302fc20 100644 (file)
@@ -441,7 +441,7 @@ void server_process_audit_message(
         }
 
         if (!NLMSG_OK(nl, buffer_size)) {
-                log_error("Audit netlink message truncated.");
+                log_ratelimit_error(JOURNALD_LOG_RATELIMIT, "Audit netlink message truncated.");
                 return;
         }
 
index 27608ff089888357c03e118f6463efee158fce6f..6d58422dddac0debc0f010dc6185a7248f36e287 100644 (file)
@@ -771,7 +771,8 @@ void client_context_acquire_default(Server *s) {
 
                 r = client_context_acquire(s, ucred.pid, &ucred, NULL, 0, NULL, &s->my_context);
                 if (r < 0)
-                        log_warning_errno(r, "Failed to acquire our own context, ignoring: %m");
+                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                    "Failed to acquire our own context, ignoring: %m");
         }
 
         if (!s->namespace && !s->pid1_context) {
@@ -780,7 +781,8 @@ void client_context_acquire_default(Server *s) {
 
                 r = client_context_acquire(s, 1, NULL, NULL, 0, NULL, &s->pid1_context);
                 if (r < 0)
-                        log_warning_errno(r, "Failed to acquire PID1's context, ignoring: %m");
+                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                    "Failed to acquire PID1's context, ignoring: %m");
 
         }
 }
index 8ae7a23d5662dad05cdc262b0b7545a18502beea..6c1e7892d1cb2b4f3810dd86bd80b9da1bcc85a5 100644 (file)
@@ -329,7 +329,7 @@ static int server_read_dev_kmsg(Server *s) {
                 if (ERRNO_IS_TRANSIENT(errno) || errno == EPIPE)
                         return 0;
 
-                return log_error_errno(errno, "Failed to read from /dev/kmsg: %m");
+                return log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT, "Failed to read from /dev/kmsg: %m");
         }
 
         dev_kmsg_record(s, buffer, l);
@@ -368,7 +368,8 @@ static int dispatch_dev_kmsg(sd_event_source *es, int fd, uint32_t revents, void
         assert(fd == s->dev_kmsg_fd);
 
         if (revents & EPOLLERR)
-                log_warning("/dev/kmsg buffer overrun, some messages lost.");
+                log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+                                      "/dev/kmsg buffer overrun, some messages lost.");
 
         if (!(revents & EPOLLIN))
                 log_error("Got invalid event from epoll for /dev/kmsg: %"PRIx32, revents);
index 032578822d13cb9c799fd875ec82cddc1d0b7a1e..21e20db2d44f033690dcff7c170a40d12ba96150 100644 (file)
@@ -309,7 +309,9 @@ void server_process_native_message(
         if (ucred && pid_is_valid(ucred->pid)) {
                 r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
                 if (r < 0)
-                        log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid);
+                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                    "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m",
+                                                    ucred->pid);
         }
 
         do {
@@ -348,29 +350,34 @@ void server_process_native_file(
 
                 r = fd_get_path(fd, &k);
                 if (r < 0) {
-                        log_error_errno(r, "readlink(/proc/self/fd/%i) failed: %m", fd);
+                        log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                  "readlink(/proc/self/fd/%i) failed: %m", fd);
                         return;
                 }
 
                 e = PATH_STARTSWITH_SET(k, "/dev/shm/", "/tmp/", "/var/tmp/");
                 if (!e) {
-                        log_error("Received file outside of allowed directories. Refusing.");
+                        log_ratelimit_error(JOURNALD_LOG_RATELIMIT,
+                                            "Received file outside of allowed directories. Refusing.");
                         return;
                 }
 
                 if (!filename_is_valid(e)) {
-                        log_error("Received file in subdirectory of allowed directories. Refusing.");
+                        log_ratelimit_error(JOURNALD_LOG_RATELIMIT,
+                                            "Received file in subdirectory of allowed directories. Refusing.");
                         return;
                 }
         }
 
         if (fstat(fd, &st) < 0) {
-                log_error_errno(errno, "Failed to stat passed file, ignoring: %m");
+                log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+                                          "Failed to stat passed file, ignoring: %m");
                 return;
         }
 
         if (!S_ISREG(st.st_mode)) {
-                log_error("File passed is not regular. Ignoring.");
+                log_ratelimit_error(JOURNALD_LOG_RATELIMIT,
+                                    "File passed is not regular. Ignoring.");
                 return;
         }
 
@@ -380,7 +387,9 @@ void server_process_native_file(
         /* When !sealed, set a lower memory limit. We have to read the file,
          * effectively doubling memory use. */
         if (st.st_size > ENTRY_SIZE_MAX / (sealed ? 1 : 2)) {
-                log_error("File passed too large (%"PRIu64" bytes). Ignoring.", (uint64_t) st.st_size);
+                log_ratelimit_error(JOURNALD_LOG_RATELIMIT,
+                                    "File passed too large (%"PRIu64" bytes). Ignoring.",
+                                    (uint64_t) st.st_size);
                 return;
         }
 
@@ -393,7 +402,8 @@ void server_process_native_file(
                 ps = PAGE_ALIGN(st.st_size);
                 p = mmap(NULL, ps, PROT_READ, MAP_PRIVATE, fd, 0);
                 if (p == MAP_FAILED) {
-                        log_error_errno(errno, "Failed to map memfd, ignoring: %m");
+                        log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+                                                  "Failed to map memfd, ignoring: %m");
                         return;
                 }
 
@@ -405,7 +415,8 @@ void server_process_native_file(
                 ssize_t n;
 
                 if (fstatvfs(fd, &vfs) < 0) {
-                        log_error_errno(errno, "Failed to stat file system of passed file, not processing it: %m");
+                        log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+                                                  "Failed to stat file system of passed file, not processing it: %m");
                         return;
                 }
 
@@ -415,7 +426,8 @@ void server_process_native_file(
                  * https://github.com/systemd/systemd/issues/1822
                  */
                 if (vfs.f_flag & ST_MANDLOCK) {
-                        log_error("Received file descriptor from file system with mandatory locking enabled, not processing it.");
+                        log_ratelimit_error(JOURNALD_LOG_RATELIMIT,
+                                            "Received file descriptor from file system with mandatory locking enabled, not processing it.");
                         return;
                 }
 
@@ -428,7 +440,8 @@ void server_process_native_file(
                  * and so is SMB. */
                 r = fd_nonblock(fd, true);
                 if (r < 0) {
-                        log_error_errno(r, "Failed to make fd non-blocking, not processing it: %m");
+                        log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                  "Failed to make fd non-blocking, not processing it: %m");
                         return;
                 }
 
@@ -444,7 +457,8 @@ void server_process_native_file(
 
                 n = pread(fd, p, st.st_size, 0);
                 if (n < 0)
-                        log_error_errno(errno, "Failed to read file, ignoring: %m");
+                        log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+                                                  "Failed to read file, ignoring: %m");
                 else if (n > 0)
                         server_process_native_message(s, p, n, ucred, tv, label, label_len);
         }
index c02d73bdc2d2de769ce534557c047cbe60f042e8..e86aa481d93e5c01628d304eda51b37ccf9e72dd 100644 (file)
@@ -83,6 +83,8 @@
 
 #define IDLE_TIMEOUT_USEC (30*USEC_PER_SEC)
 
+#define FAILED_TO_WRITE_ENTRY_RATELIMIT ((RateLimit) { .interval = 1 * USEC_PER_SEC, .burst = 1 })
+
 static int determine_path_usage(
                 Server *s,
                 const char *path,
@@ -99,11 +101,12 @@ static int determine_path_usage(
 
         d = opendir(path);
         if (!d)
-                return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
-                                      errno, "Failed to open %s: %m", path);
+                return log_ratelimit_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
+                                                errno, JOURNALD_LOG_RATELIMIT, "Failed to open %s: %m", path);
 
         if (fstatvfs(dirfd(d), &ss) < 0)
-                return log_error_errno(errno, "Failed to fstatvfs(%s): %m", path);
+                return log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+                                                "Failed to fstatvfs(%s): %m", path);
 
         *ret_free = ss.f_bsize * ss.f_bavail;
         *ret_used = 0;
@@ -253,7 +256,8 @@ static void server_add_acls(ManagedJournalFile *f, uid_t uid) {
 
         r = fd_add_uid_acl_permission(f->file->fd, uid, ACL_READ);
         if (r < 0)
-                log_warning_errno(r, "Failed to set ACL on %s, ignoring: %m", f->file->path);
+                log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                            "Failed to set ACL on %s, ignoring: %m", f->file->path);
 #endif
 }
 
@@ -353,7 +357,8 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_
                         patch_min_use(&s->system_storage);
                 } else {
                         if (!IN_SET(r, -ENOENT, -EROFS))
-                                log_warning_errno(r, "Failed to open system journal: %m");
+                                log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                            "Failed to open system journal: %m");
 
                         r = 0;
                 }
@@ -382,7 +387,8 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_
                         r = open_journal(s, false, fn, O_RDWR, false, &s->runtime_storage.metrics, &s->runtime_journal);
                         if (r < 0) {
                                 if (r != -ENOENT)
-                                        log_warning_errno(r, "Failed to open runtime journal: %m");
+                                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                                    "Failed to open runtime journal: %m");
 
                                 r = 0;
                         }
@@ -396,7 +402,8 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_
 
                         r = open_journal(s, true, fn, O_RDWR|O_CREAT, false, &s->runtime_storage.metrics, &s->runtime_journal);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to open runtime journal: %m");
+                                return log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                                   "Failed to open runtime journal: %m");
                 }
 
                 if (s->runtime_journal) {
@@ -493,9 +500,11 @@ static int do_rotate(
         r = managed_journal_file_rotate(f, s->mmap, file_flags, s->compress.threshold_bytes, s->deferred_closes);
         if (r < 0) {
                 if (*f)
-                        return log_error_errno(r, "Failed to rotate %s: %m", (*f)->file->path);
+                        return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                         "Failed to rotate %s: %m", (*f)->file->path);
                 else
-                        return log_error_errno(r, "Failed to create new %s journal: %m", name);
+                        return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                         "Failed to create new %s journal: %m", name);
         }
 
         server_add_acls(*f, uid);
@@ -545,7 +554,8 @@ static int vacuum_offline_user_journals(Server *s) {
                 if (errno == ENOENT)
                         return 0;
 
-                return log_error_errno(errno, "Failed to open %s: %m", s->system_storage.path);
+                return log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+                                                 "Failed to open %s: %m", s->system_storage.path);
         }
 
         for (;;) {
@@ -560,7 +570,9 @@ static int vacuum_offline_user_journals(Server *s) {
                 de = readdir_no_dot(d);
                 if (!de) {
                         if (errno != 0)
-                                log_warning_errno(errno, "Failed to enumerate %s, ignoring: %m", s->system_storage.path);
+                                log_ratelimit_warning_errno(errno, JOURNALD_LOG_RATELIMIT,
+                                                            "Failed to enumerate %s, ignoring: %m",
+                                                            s->system_storage.path);
 
                         break;
                 }
@@ -592,8 +604,9 @@ static int vacuum_offline_user_journals(Server *s) {
 
                 fd = openat(dirfd(d), de->d_name, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
                 if (fd < 0) {
-                        log_full_errno(IN_SET(errno, ELOOP, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno,
-                                       "Failed to open journal file '%s' for rotation: %m", full);
+                        log_ratelimit_full_errno(IN_SET(errno, ELOOP, ENOENT) ? LOG_DEBUG : LOG_WARNING,
+                                                 errno, JOURNALD_LOG_RATELIMIT,
+                                                 "Failed to open journal file '%s' for rotation: %m", full);
                         continue;
                 }
 
@@ -615,11 +628,15 @@ static int vacuum_offline_user_journals(Server *s) {
                                 NULL,
                                 &f);
                 if (r < 0) {
-                        log_warning_errno(r, "Failed to read journal file %s for rotation, trying to move it out of the way: %m", full);
+                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                    "Failed to read journal file %s for rotation, trying to move it out of the way: %m",
+                                                    full);
 
                         r = journal_file_dispose(dirfd(d), de->d_name);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to move %s out of the way, ignoring: %m", full);
+                                log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                            "Failed to move %s out of the way, ignoring: %m",
+                                                            full);
                         else
                                 log_debug("Successfully moved %s out of the way.", full);
 
@@ -675,19 +692,22 @@ void server_sync(Server *s) {
         if (s->system_journal) {
                 r = managed_journal_file_set_offline(s->system_journal, false);
                 if (r < 0)
-                        log_warning_errno(r, "Failed to sync system journal, ignoring: %m");
+                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                    "Failed to sync system journal, ignoring: %m");
         }
 
         ORDERED_HASHMAP_FOREACH(f, s->user_journals) {
                 r = managed_journal_file_set_offline(f, false);
                 if (r < 0)
-                        log_warning_errno(r, "Failed to sync user journal, ignoring: %m");
+                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                    "Failed to sync user journal, ignoring: %m");
         }
 
         if (s->sync_event_source) {
                 r = sd_event_source_set_enabled(s->sync_event_source, SD_EVENT_OFF);
                 if (r < 0)
-                        log_error_errno(r, "Failed to disable sync timer source: %m");
+                        log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                  "Failed to disable sync timer source: %m");
         }
 
         s->sync_scheduled = false;
@@ -709,7 +729,8 @@ static void do_vacuum(Server *s, JournalStorage *storage, bool verbose) {
                                      storage->metrics.n_max_files, s->max_retention_usec,
                                      &s->oldest_file_usec, verbose);
         if (r < 0 && r != -ENOENT)
-                log_warning_errno(r, "Failed to vacuum %s, ignoring: %m", storage->path);
+                log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                            "Failed to vacuum %s, ignoring: %m", storage->path);
 
         cache_space_invalidate(&storage->space);
 }
@@ -781,37 +802,39 @@ static bool shall_try_append_again(JournalFile *f, int r) {
                 return true;
 
         case -EIO:             /* I/O error of some kind (mmap) */
-                log_warning("%s: IO error, rotating.", f->path);
+                log_ratelimit_warning(JOURNALD_LOG_RATELIMIT, "%s: IO error, rotating.", f->path);
                 return true;
 
         case -EHOSTDOWN:       /* Other machine                 */
-                log_info("%s: Journal file from other machine, rotating.", f->path);
+                log_ratelimit_info(JOURNALD_LOG_RATELIMIT, "%s: Journal file from other machine, rotating.", f->path);
                 return true;
 
         case -EBUSY:           /* Unclean shutdown              */
-                log_info("%s: Unclean shutdown, rotating.", f->path);
+                log_ratelimit_info(JOURNALD_LOG_RATELIMIT, "%s: Unclean shutdown, rotating.", f->path);
                 return true;
 
         case -EPROTONOSUPPORT: /* Unsupported feature           */
-                log_info("%s: Unsupported feature, rotating.", f->path);
+                log_ratelimit_info(JOURNALD_LOG_RATELIMIT, "%s: Unsupported feature, rotating.", f->path);
                 return true;
 
         case -EBADMSG:         /* Corrupted                     */
         case -ENODATA:         /* Truncated                     */
         case -ESHUTDOWN:       /* Already archived              */
-                log_warning("%s: Journal file corrupted, rotating.", f->path);
+                log_ratelimit_warning(JOURNALD_LOG_RATELIMIT, "%s: Journal file corrupted, rotating.", f->path);
                 return true;
 
         case -EIDRM:           /* Journal file has been deleted */
-                log_warning("%s: Journal file has been deleted, rotating.", f->path);
+                log_ratelimit_warning(JOURNALD_LOG_RATELIMIT, "%s: Journal file has been deleted, rotating.", f->path);
                 return true;
 
         case -ETXTBSY:         /* Journal file is from the future */
-                log_warning("%s: Journal file is from the future, rotating.", f->path);
+                log_ratelimit_warning(JOURNALD_LOG_RATELIMIT, "%s: Journal file is from the future, rotating.", f->path);
                 return true;
 
         case -EAFNOSUPPORT:
-                log_warning("%s: underlying file system does not support memory mapping or another required file system feature.", f->path);
+                log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+                                      "%s: underlying file system does not support memory mapping or another required file system feature.",
+                                      f->path);
                 return false;
 
         default:
@@ -841,7 +864,7 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, size_t n
                  * to ensure that the entries in the journal files are strictly ordered by time, in order to ensure
                  * bisection works correctly. */
 
-                log_info("Time jumped backwards, rotating.");
+                log_ratelimit_info(JOURNALD_LOG_RATELIMIT, "Time jumped backwards, rotating.");
                 rotate = true;
         } else {
 
@@ -850,7 +873,9 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, size_t n
                         return;
 
                 if (journal_file_rotate_suggested(f->file, s->max_file_usec, LOG_INFO)) {
-                        log_info("%s: Journal header limits reached or header out-of-date, rotating.", f->file->path);
+                        log_ratelimit_info(JOURNALD_LOG_RATELIMIT,
+                                           "%s: Journal header limits reached or header out-of-date, rotating.",
+                                           f->file->path);
                         rotate = true;
                 }
         }
@@ -874,14 +899,18 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, size_t n
         }
 
         if (vacuumed || !shall_try_append_again(f->file, r)) {
-                log_ratelimit_full_errno(LOG_ERR, r, "Failed to write entry (%zu items, %zu bytes), ignoring: %m", n, IOVEC_TOTAL_SIZE(iovec, n));
+                log_ratelimit_error_errno(r, FAILED_TO_WRITE_ENTRY_RATELIMIT,
+                                          "Failed to write entry (%zu items, %zu bytes), ignoring: %m",
+                                          n, IOVEC_TOTAL_SIZE(iovec, n));
                 return;
         }
 
         if (r == -E2BIG)
                 log_debug("Journal file %s is full, rotating to a new file", f->file->path);
         else
-                log_ratelimit_full_errno(LOG_INFO, r, "Failed to write entry to %s (%zu items, %zu bytes), rotating before retrying: %m", f->file->path, n, IOVEC_TOTAL_SIZE(iovec, n));
+                log_ratelimit_info_errno(r, FAILED_TO_WRITE_ENTRY_RATELIMIT,
+                                         "Failed to write entry to %s (%zu items, %zu bytes), rotating before retrying: %m",
+                                         f->file->path, n, IOVEC_TOTAL_SIZE(iovec, n));
 
         server_rotate(s);
         server_vacuum(s, false);
@@ -890,10 +919,12 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, size_t n
         if (!f)
                 return;
 
-        log_debug("Retrying write.");
+        log_debug_errno(r, "Retrying write.");
         r = journal_file_append_entry(f->file, &ts, NULL, iovec, n, &s->seqnum, NULL, NULL);
         if (r < 0)
-                log_ratelimit_full_errno(LOG_ERR, r, "Failed to write entry to %s (%zu items, %zu bytes) despite vacuuming, ignoring: %m", f->file->path, n, IOVEC_TOTAL_SIZE(iovec, n));
+                log_ratelimit_error_errno(r, FAILED_TO_WRITE_ENTRY_RATELIMIT,
+                                          "Failed to write entry to %s (%zu items, %zu bytes) despite vacuuming, ignoring: %m",
+                                          f->file->path, n, IOVEC_TOTAL_SIZE(iovec, n));
         else
                 server_schedule_sync(s, priority);
 }
@@ -1181,7 +1212,8 @@ int server_flush_to_var(Server *s, bool require_flag_file) {
 
         r = sd_journal_open(&j, SD_JOURNAL_RUNTIME_ONLY);
         if (r < 0)
-                return log_error_errno(r, "Failed to read runtime journal: %m");
+                return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                 "Failed to read runtime journal: %m");
 
         sd_journal_set_data_threshold(j, 0);
 
@@ -1196,7 +1228,7 @@ int server_flush_to_var(Server *s, bool require_flag_file) {
 
                 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
                 if (r < 0) {
-                        log_error_errno(r, "Can't read entry: %m");
+                        log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Can't read entry: %m");
                         goto finish;
                 }
 
@@ -1205,17 +1237,18 @@ int server_flush_to_var(Server *s, bool require_flag_file) {
                         continue;
 
                 if (!shall_try_append_again(s->system_journal->file, r)) {
-                        log_error_errno(r, "Can't write entry: %m");
+                        log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Can't write entry: %m");
                         goto finish;
                 }
 
-                log_info("Rotating system journal.");
+                log_ratelimit_info(JOURNALD_LOG_RATELIMIT, "Rotating system journal.");
 
                 server_rotate(s);
                 server_vacuum(s, false);
 
                 if (!s->system_journal) {
-                        log_notice("Didn't flush runtime journal since rotation of system journal wasn't successful.");
+                        log_ratelimit_notice(JOURNALD_LOG_RATELIMIT,
+                                             "Didn't flush runtime journal since rotation of system journal wasn't successful.");
                         r = -EIO;
                         goto finish;
                 }
@@ -1223,7 +1256,7 @@ int server_flush_to_var(Server *s, bool require_flag_file) {
                 log_debug("Retrying write.");
                 r = journal_file_copy_entry(f, s->system_journal->file, o, f->current_offset);
                 if (r < 0) {
-                        log_error_errno(r, "Can't write entry: %m");
+                        log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Can't write entry: %m");
                         goto finish;
                 }
         }
@@ -1251,7 +1284,8 @@ finish:
         fn = strjoina(s->runtime_directory, "/flushed");
         k = touch(fn);
         if (k < 0)
-                log_warning_errno(k, "Failed to touch %s, ignoring: %m", fn);
+                log_ratelimit_warning_errno(k, JOURNALD_LOG_RATELIMIT,
+                                            "Failed to touch %s, ignoring: %m", fn);
 
         server_refresh_idle_timer(s);
         return r;
@@ -1280,7 +1314,8 @@ static int server_relinquish_var(Server *s) {
 
         fn = strjoina(s->runtime_directory, "/flushed");
         if (unlink(fn) < 0 && errno != ENOENT)
-                log_warning_errno(errno, "Failed to unlink %s, ignoring: %m", fn);
+                log_ratelimit_warning_errno(errno, JOURNALD_LOG_RATELIMIT,
+                                            "Failed to unlink %s, ignoring: %m", fn);
 
         server_refresh_idle_timer(s);
         return 0;
@@ -1352,10 +1387,11 @@ int server_process_datagram(
                 if (ERRNO_IS_TRANSIENT(n))
                         return 0;
                 if (n == -EXFULL) {
-                        log_warning("Got message with truncated control data (too many fds sent?), ignoring.");
+                        log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+                                              "Got message with truncated control data (too many fds sent?), ignoring.");
                         return 0;
                 }
-                return log_error_errno(n, "recvmsg() failed: %m");
+                return log_ratelimit_error_errno(n, JOURNALD_LOG_RATELIMIT, "recvmsg() failed: %m");
         }
 
         CMSG_FOREACH(cmsg, &msghdr)
@@ -1388,7 +1424,8 @@ int server_process_datagram(
                 if (n > 0 && n_fds == 0)
                         server_process_syslog_message(s, s->buffer, n, ucred, tv, label, label_len);
                 else if (n_fds > 0)
-                        log_warning("Got file descriptors via syslog socket. Ignoring.");
+                        log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+                                              "Got file descriptors via syslog socket. Ignoring.");
 
         } else if (fd == s->native_fd) {
                 if (n > 0 && n_fds == 0)
@@ -1396,7 +1433,8 @@ int server_process_datagram(
                 else if (n == 0 && n_fds == 1)
                         server_process_native_file(s, fds[0], ucred, tv, label, label_len);
                 else if (n_fds > 0)
-                        log_warning("Got too many file descriptors via native socket. Ignoring.");
+                        log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+                                              "Got too many file descriptors via native socket. Ignoring.");
 
         } else {
                 assert(fd == s->audit_fd);
@@ -1404,7 +1442,8 @@ int server_process_datagram(
                 if (n > 0 && n_fds == 0)
                         server_process_audit_message(s, s->buffer, n, ucred, &sa, msghdr.msg_namelen);
                 else if (n_fds > 0)
-                        log_warning("Got file descriptors via audit socket. Ignoring.");
+                        log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+                                              "Got file descriptors via audit socket. Ignoring.");
         }
 
         close_many(fds, n_fds);
@@ -1457,7 +1496,8 @@ static void server_full_rotate(Server *s) {
         fn = strjoina(s->runtime_directory, "/rotated");
         r = write_timestamp_file_atomic(fn, now(CLOCK_MONOTONIC));
         if (r < 0)
-                log_warning_errno(r, "Failed to write %s, ignoring: %m", fn);
+                log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                            "Failed to write %s, ignoring: %m", fn);
 }
 
 static int dispatch_sigusr2(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) {
@@ -1560,7 +1600,8 @@ static void server_full_sync(Server *s) {
         fn = strjoina(s->runtime_directory, "/synced");
         r = write_timestamp_file_atomic(fn, now(CLOCK_MONOTONIC));
         if (r < 0)
-                log_warning_errno(r, "Failed to write %s, ignoring: %m", fn);
+                log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                            "Failed to write %s, ignoring: %m", fn);
 
         return;
 }
index ee8f37419088c2e7e9c99e29ba078ec28b4923d5..84efb04265873ea1f4b5a6f750c00f98a78b1c4a 100644 (file)
@@ -20,6 +20,8 @@ typedef struct Server Server;
 #include "time-util.h"
 #include "varlink.h"
 
+#define JOURNALD_LOG_RATELIMIT ((RateLimit) { .interval = 60 * USEC_PER_SEC, .burst = 3 })
+
 typedef enum Storage {
         STORAGE_AUTO,
         STORAGE_VOLATILE,
index 8bdcd8c2ae93a0738d1a0221395e3ebf342d1842..abfd04683713f42c91ca08665119e74e0acedadf 100644 (file)
@@ -160,7 +160,8 @@ static int stdout_stream_save(StdoutStream *s) {
 
                 r = fstat(s->fd, &st);
                 if (r < 0)
-                        return log_warning_errno(errno, "Failed to stat connected stream: %m");
+                        return log_ratelimit_warning_errno(errno, JOURNALD_LOG_RATELIMIT,
+                                                           "Failed to stat connected stream: %m");
 
                 /* We use device and inode numbers as identifier for the stream */
                 r = asprintf(&s->state_file, "%s/streams/%lu:%lu", s->server->runtime_directory, (unsigned long) st.st_dev, (unsigned long) st.st_ino);
@@ -231,7 +232,7 @@ static int stdout_stream_save(StdoutStream *s) {
                 if (s->server->notify_event_source) {
                         r = sd_event_source_set_enabled(s->server->notify_event_source, SD_EVENT_ON);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to enable notify event source: %m");
+                                log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to enable notify event source: %m");
                 }
         }
 
@@ -239,7 +240,8 @@ static int stdout_stream_save(StdoutStream *s) {
 
 fail:
         (void) unlink(s->state_file);
-        return log_error_errno(r, "Failed to save stream data %s: %m", s->state_file);
+        return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+                                         "Failed to save stream data %s: %m", s->state_file);
 }
 
 static int stdout_stream_log(
@@ -266,7 +268,8 @@ static int stdout_stream_log(
         else if (pid_is_valid(s->ucred.pid)) {
                 r = client_context_acquire(s->server, s->ucred.pid, &s->ucred, s->label, strlen_ptr(s->label), s->unit_id, &s->context);
                 if (r < 0)
-                        log_warning_errno(r, "Failed to acquire client context, ignoring: %m");
+                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                    "Failed to acquire client context, ignoring: %m");
         }
 
         priority = s->priority;
@@ -363,8 +366,8 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
 
         /* line breaks by NUL, line max length or EOF are not permissible during the negotiation part of the protocol */
         if (line_break != LINE_BREAK_NEWLINE && s->state != STDOUT_STREAM_RUNNING)
-                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                         "Control protocol line not properly terminated.");
+                return log_ratelimit_warning_errno(SYNTHETIC_ERRNO(EINVAL), JOURNALD_LOG_RATELIMIT,
+                                                   "Control protocol line not properly terminated.");
 
         switch (s->state) {
 
@@ -395,7 +398,8 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
 
                 priority = syslog_parse_priority_and_facility(p);
                 if (priority < 0)
-                        return log_warning_errno(priority, "Failed to parse log priority line: %m");
+                        return log_ratelimit_warning_errno(priority, JOURNALD_LOG_RATELIMIT,
+                                                           "Failed to parse log priority line: %m");
 
                 s->priority = priority;
                 s->state = STDOUT_STREAM_LEVEL_PREFIX;
@@ -405,7 +409,8 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
         case STDOUT_STREAM_LEVEL_PREFIX:
                 r = parse_boolean(p);
                 if (r < 0)
-                        return log_warning_errno(r, "Failed to parse level prefix line: %m");
+                        return log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                           "Failed to parse level prefix line: %m");
 
                 s->level_prefix = r;
                 s->state = STDOUT_STREAM_FORWARD_TO_SYSLOG;
@@ -414,7 +419,8 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
         case STDOUT_STREAM_FORWARD_TO_SYSLOG:
                 r = parse_boolean(p);
                 if (r < 0)
-                        return log_warning_errno(r, "Failed to parse forward to syslog line: %m");
+                        return log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                           "Failed to parse forward to syslog line: %m");
 
                 s->forward_to_syslog = r;
                 s->state = STDOUT_STREAM_FORWARD_TO_KMSG;
@@ -423,7 +429,8 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
         case STDOUT_STREAM_FORWARD_TO_KMSG:
                 r = parse_boolean(p);
                 if (r < 0)
-                        return log_warning_errno(r, "Failed to parse copy to kmsg line: %m");
+                        return log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                           "Failed to parse copy to kmsg line: %m");
 
                 s->forward_to_kmsg = r;
                 s->state = STDOUT_STREAM_FORWARD_TO_CONSOLE;
@@ -432,7 +439,8 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
         case STDOUT_STREAM_FORWARD_TO_CONSOLE:
                 r = parse_boolean(p);
                 if (r < 0)
-                        return log_warning_errno(r, "Failed to parse copy to console line.");
+                        return log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                           "Failed to parse copy to console line.");
 
                 s->forward_to_console = r;
                 s->state = STDOUT_STREAM_RUNNING;
@@ -589,7 +597,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents,
                 if (ERRNO_IS_TRANSIENT(errno))
                         return 0;
 
-                log_warning_errno(errno, "Failed to read from stream: %m");
+                log_ratelimit_warning_errno(errno, JOURNALD_LOG_RATELIMIT, "Failed to read from stream: %m");
                 goto terminate;
         }
         cmsg_close_all(&msghdr);
@@ -648,7 +656,7 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
 
         r = sd_id128_randomize(&id);
         if (r < 0)
-                return log_error_errno(r, "Failed to generate stream ID: %m");
+                return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to generate stream ID: %m");
 
         stream = new(StdoutStream, 1);
         if (!stream)
@@ -664,7 +672,7 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
 
         r = getpeercred(fd, &stream->ucred);
         if (r < 0)
-                return log_error_errno(r, "Failed to determine peer credentials: %m");
+                return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to determine peer credentials: %m");
 
         r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
         if (r < 0)
@@ -673,18 +681,18 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
         if (mac_selinux_use()) {
                 r = getpeersec(fd, &stream->label);
                 if (r < 0 && r != -EOPNOTSUPP)
-                        (void) log_warning_errno(r, "Failed to determine peer security context: %m");
+                        (void) log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to determine peer security context: %m");
         }
 
         (void) shutdown(fd, SHUT_WR);
 
         r = sd_event_add_io(s->event, &stream->event_source, fd, EPOLLIN, stdout_stream_process, stream);
         if (r < 0)
-                return log_error_errno(r, "Failed to add stream to event loop: %m");
+                return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to add stream to event loop: %m");
 
         r = sd_event_source_set_priority(stream->event_source, SD_EVENT_PRIORITY_NORMAL+5);
         if (r < 0)
-                return log_error_errno(r, "Failed to adjust stdout event source priority: %m");
+                return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to adjust stdout event source priority: %m");
 
         stream->fd = fd;
 
@@ -716,7 +724,7 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
                 if (ERRNO_IS_ACCEPT_AGAIN(errno))
                         return 0;
 
-                return log_error_errno(errno, "Failed to accept stdout connection: %m");
+                return log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT, "Failed to accept stdout connection: %m");
         }
 
         if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
index ce023786755ee2005e587358ad422c476a8afeb1..6394adfdfdf2291d30b214c92b99f4770b82ff5e 100644 (file)
@@ -334,7 +334,9 @@ void server_process_syslog_message(
         if (ucred && pid_is_valid(ucred->pid)) {
                 r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
                 if (r < 0)
-                        log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid);
+                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                                    "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m",
+                                                    ucred->pid);
         }
 
         /* We are creating a copy of the message because we want to forward the original message
index c5288000af81c9ae0d90c6e55cf6e137f5bc1758..7939574c372d82350226b6a64e248a34b5097d04 100644 (file)
@@ -157,7 +157,6 @@ static void test_skip_one(void (*setup)(void)) {
          */
         assert_ret(sd_journal_open_directory(&j, t, 0));
         assert_ret(sd_journal_seek_head(j));
-        assert_ret(sd_journal_previous(j) == 0);
         assert_ret(sd_journal_next(j));
         test_check_numbers_down(j, 4);
         sd_journal_close(j);
@@ -166,7 +165,6 @@ static void test_skip_one(void (*setup)(void)) {
          */
         assert_ret(sd_journal_open_directory(&j, t, 0));
         assert_ret(sd_journal_seek_tail(j));
-        assert_ret(sd_journal_next(j) == 0);
         assert_ret(sd_journal_previous(j));
         test_check_numbers_up(j, 4);
         sd_journal_close(j);
@@ -175,7 +173,6 @@ static void test_skip_one(void (*setup)(void)) {
          */
         assert_ret(sd_journal_open_directory(&j, t, 0));
         assert_ret(sd_journal_seek_tail(j));
-        assert_ret(sd_journal_next(j) == 0);
         assert_ret(r = sd_journal_previous_skip(j, 4));
         assert_se(r == 4);
         test_check_numbers_down(j, 4);
@@ -185,7 +182,6 @@ static void test_skip_one(void (*setup)(void)) {
          */
         assert_ret(sd_journal_open_directory(&j, t, 0));
         assert_ret(sd_journal_seek_head(j));
-        assert_ret(sd_journal_previous(j) == 0);
         assert_ret(r = sd_journal_next_skip(j, 4));
         assert_se(r == 4);
         test_check_numbers_up(j, 4);
index 22eb4d2be144c30d73d086b2eb65efe68fe44452..fa2c0d5276aca016ea9478a7a6ed7481bbb209e9 100755 (executable)
@@ -158,8 +158,9 @@ if [ -z "$MACHINE_ID" ] && [ -f /etc/machine-info ]; then
     [ -n "$MACHINE_ID" ] && \
         log_verbose "machine-id $MACHINE_ID acquired from /etc/machine-info"
 fi
-if [ -z "$MACHINE_ID" ] && [ -f /etc/machine-id ]; then
+if [ -z "$MACHINE_ID" ] && [ -s /etc/machine-id ]; then
     read -r MACHINE_ID </etc/machine-id
+    [ "$MACHINE_ID" = "uninitialized" ] && unset MACHINE_ID
     [ -n "$MACHINE_ID" ] && \
         log_verbose "machine-id $MACHINE_ID acquired from /etc/machine-id"
 fi
index 45e7473c29f2eaea39d03f782d80bfb543a058ff..6e53e942dff232b95939da8462a47f9b96a41786 100644 (file)
@@ -15,6 +15,7 @@
 #include "fileio.h"
 #include "format-util.h"
 #include "hexdecoct.h"
+#include "nulstr-util.h"
 #include "parse-util.h"
 #include "process-util.h"
 #include "string-util.h"
index 7e72245e8dca2c48d706b22440a44f5348321f2a..ba5ef7de006f61740a1c375e8cd4c7081095501a 100644 (file)
@@ -3529,7 +3529,7 @@ static int bus_add_match_full(
                                                                  s);
 
                                 if (r < 0)
-                                        return r;
+                                        goto finish;
 
                                 /* Make the slot of the match call floating now. We need the reference, but we don't
                                  * want that this match pins the bus object, hence we first create it non-floating, but
index bc7a838608996b81df0497e15d9a6c0006d99813..7cda48c4cafb8ef0bcaf9e655d4344782eb86213 100644 (file)
@@ -559,7 +559,6 @@ static int device_update_properties_bufs(sd_device *device) {
                 return -ENOMEM;
 
         size_t i = 0;
-        char *p;
         NULSTR_FOREACH(p, buf_nulstr)
                 buf_strv[i++] = p;
         assert(i == num);
index 4ab8b3894ae3737498c2557bcc924d92ed490254..32ee6707013f4c691ebd01015b7fc320f2871efa 100644 (file)
@@ -534,7 +534,7 @@ TEST(sd_device_new_from_nulstr) {
 
         _cleanup_(sd_device_unrefp) sd_device *device = NULL, *from_nulstr = NULL;
         _cleanup_free_ char *nulstr_copy = NULL;
-        const char *devlink, *nulstr;
+        const char *nulstr;
         size_t len;
 
         assert_se(sd_device_new_from_syspath(&device, "/sys/class/net/lo") >= 0);
index 51be54b1e14ceff3e7166bd57438d3ad0b09f527..f163314f7c3ac1153b534eb601fc94f29eaf96db 100644 (file)
@@ -300,13 +300,15 @@ static int hwdb_new(const char *path, sd_hwdb **ret) {
                 if (!hwdb->f)
                         return log_debug_errno(errno, "Failed to open %s: %m", path);
         } else {
-                NULSTR_FOREACH(path, hwdb_bin_paths) {
-                        log_debug("Trying to open \"%s\"...", path);
-                        hwdb->f = fopen(path, "re");
-                        if (hwdb->f)
+                NULSTR_FOREACH(p, hwdb_bin_paths) {
+                        log_debug("Trying to open \"%s\"...", p);
+                        hwdb->f = fopen(p, "re");
+                        if (hwdb->f) {
+                                path = p;
                                 break;
+                        }
                         if (errno != ENOENT)
-                                return log_debug_errno(errno, "Failed to open %s: %m", path);
+                                return log_debug_errno(errno, "Failed to open %s: %m", p);
                 }
 
                 if (!hwdb->f)
index 3f0dcaebf17057e78754c2add9b5f99220c4d995..23eed6f8dda3278372cc06bc33f4ba71c920af7f 100644 (file)
@@ -4161,6 +4161,10 @@ int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, u
         return 1;
 }
 
+/* Ideally this would be a function parameter but initializers for static fields have to be compile
+ * time constants so we hardcode the interval instead. */
+#define LOG_RATELIMIT ((RateLimit) { .interval = 60 * USEC_PER_SEC, .burst = 3 })
+
 bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec, int log_level) {
         assert(f);
         assert(f->header);
@@ -4178,25 +4182,27 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec, int log
 
         if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
                 if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) {
-                        log_full(log_level,
-                                 "Data hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items, %llu file size, %"PRIu64" bytes per hash table item), suggesting rotation.",
-                                 f->path,
-                                 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))),
-                                 le64toh(f->header->n_data),
-                                 le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
-                                 (unsigned long long) f->last_stat.st_size,
-                                 f->last_stat.st_size / le64toh(f->header->n_data));
+                        log_ratelimit_full(
+                                log_level, LOG_RATELIMIT,
+                                "Data hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items, %llu file size, %"PRIu64" bytes per hash table item), suggesting rotation.",
+                                f->path,
+                                100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))),
+                                le64toh(f->header->n_data),
+                                le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
+                                (unsigned long long) f->last_stat.st_size,
+                                f->last_stat.st_size / le64toh(f->header->n_data));
                         return true;
                 }
 
         if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
                 if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) {
-                        log_full(log_level,
-                                 "Field hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items), suggesting rotation.",
-                                 f->path,
-                                 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))),
-                                 le64toh(f->header->n_fields),
-                                 le64toh(f->header->field_hash_table_size) / sizeof(HashItem));
+                        log_ratelimit_full(
+                                log_level, LOG_RATELIMIT,
+                                "Field hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items), suggesting rotation.",
+                                f->path,
+                                100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))),
+                                le64toh(f->header->n_fields),
+                                le64toh(f->header->field_hash_table_size) / sizeof(HashItem));
                         return true;
                 }
 
@@ -4204,17 +4210,19 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec, int log
          * longest chain is longer than some threshold, let's suggest rotation. */
         if (JOURNAL_HEADER_CONTAINS(f->header, data_hash_chain_depth) &&
             le64toh(f->header->data_hash_chain_depth) > HASH_CHAIN_DEPTH_MAX) {
-                log_full(log_level,
-                         "Data hash table of %s has deepest hash chain of length %" PRIu64 ", suggesting rotation.",
-                         f->path, le64toh(f->header->data_hash_chain_depth));
+                log_ratelimit_full(
+                        log_level, LOG_RATELIMIT,
+                        "Data hash table of %s has deepest hash chain of length %" PRIu64 ", suggesting rotation.",
+                        f->path, le64toh(f->header->data_hash_chain_depth));
                 return true;
         }
 
         if (JOURNAL_HEADER_CONTAINS(f->header, field_hash_chain_depth) &&
             le64toh(f->header->field_hash_chain_depth) > HASH_CHAIN_DEPTH_MAX) {
-                log_full(log_level,
-                         "Field hash table of %s has deepest hash chain of length at %" PRIu64 ", suggesting rotation.",
-                         f->path, le64toh(f->header->field_hash_chain_depth));
+                log_ratelimit_full(
+                        log_level, LOG_RATELIMIT,
+                        "Field hash table of %s has deepest hash chain of length at %" PRIu64 ", suggesting rotation.",
+                        f->path, le64toh(f->header->field_hash_chain_depth));
                 return true;
         }
 
@@ -4223,9 +4231,10 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec, int log
             JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
             le64toh(f->header->n_data) > 0 &&
             le64toh(f->header->n_fields) == 0) {
-                log_full(log_level,
-                         "Data objects of %s are not indexed by field objects, suggesting rotation.",
-                         f->path);
+                log_ratelimit_full(
+                        log_level, LOG_RATELIMIT,
+                        "Data objects of %s are not indexed by field objects, suggesting rotation.",
+                        f->path);
                 return true;
         }
 
@@ -4236,9 +4245,10 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec, int log
                 t = now(CLOCK_REALTIME);
 
                 if (h > 0 && t > h + max_file_usec) {
-                        log_full(log_level,
-                                 "Oldest entry in %s is older than the configured file retention duration (%s), suggesting rotation.",
-                                 f->path, FORMAT_TIMESPAN(max_file_usec, USEC_PER_SEC));
+                        log_ratelimit_full(
+                                log_level, LOG_RATELIMIT,
+                                "Oldest entry in %s is older than the configured file retention duration (%s), suggesting rotation.",
+                                f->path, FORMAT_TIMESPAN(max_file_usec, USEC_PER_SEC));
                         return true;
                 }
         }
index 53c0b2a01e93f6b2baf86afca650f6c4d6825f0e..0f3376823bb0b01e4b010a735dc51d7171a5e624 100644 (file)
@@ -606,9 +606,9 @@ static int find_location_for_match(
                 /* FIXME: missing: find by monotonic */
 
                 if (j->current_location.type == LOCATION_HEAD)
-                        return direction == DIRECTION_DOWN ? journal_file_next_entry_for_data(f, d, DIRECTION_DOWN, ret, offset) : 0;
+                        return journal_file_next_entry_for_data(f, d, DIRECTION_DOWN, ret, offset);
                 if (j->current_location.type == LOCATION_TAIL)
-                        return direction == DIRECTION_UP ? journal_file_next_entry_for_data(f, d, DIRECTION_UP, ret, offset) : 0;
+                        return journal_file_next_entry_for_data(f, d, DIRECTION_UP, ret, offset);
                 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
                         return journal_file_move_to_entry_by_seqnum_for_data(f, d, j->current_location.seqnum, direction, ret, offset);
                 if (j->current_location.monotonic_set) {
@@ -701,9 +701,9 @@ static int find_location_with_matches(
                 /* No matches is simple */
 
                 if (j->current_location.type == LOCATION_HEAD)
-                        return direction == DIRECTION_DOWN ? journal_file_next_entry(f, 0, DIRECTION_DOWN, ret, offset) : 0;
+                        return journal_file_next_entry(f, 0, DIRECTION_DOWN, ret, offset);
                 if (j->current_location.type == LOCATION_TAIL)
-                        return direction == DIRECTION_UP ? journal_file_next_entry(f, 0, DIRECTION_UP, ret, offset) : 0;
+                        return journal_file_next_entry(f, 0, DIRECTION_UP, ret, offset);
                 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
                         return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
                 if (j->current_location.monotonic_set) {
@@ -1828,7 +1828,6 @@ static int add_search_paths(sd_journal *j) {
         static const char search_paths[] =
                 "/run/log/journal\0"
                 "/var/log/journal\0";
-        const char *p;
 
         assert(j);
 
index 601f61bf639f2bae1a451044a5a92c7c1613d93a..ded64a66efad22e7b367b752a616047f6c084b3f 100644 (file)
@@ -7,6 +7,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "nulstr-util.h"
 #include "path-lookup.h"
 #include "path-util.h"
 #include "string-util.h"
index e44e287282774874ccfd0bdbceb67913fae0464e..966f07d08327a50112493bd1cd8b8655c5396e35 100644 (file)
@@ -72,22 +72,19 @@ static int print_status_info(StatusInfo *i) {
                         return log_error_errno(r, "Failed to build locale settings from kernel command line: %m");
         }
 
-        table = table_new("key", "value");
+        table = table_new_vertical();
         if (!table)
                 return log_oom();
 
         assert_se(cell = table_get_cell(table, 0, 0));
         (void) table_set_ellipsize_percent(table, cell, 100);
-        (void) table_set_align_percent(table, cell, 100);
-
-        table_set_header(table, false);
 
         table_set_ersatz_string(table, TABLE_ERSATZ_UNSET);
 
         if (!strv_isempty(kernel_locale)) {
                 log_warning("Warning: Settings on kernel command line override system locale settings in /etc/locale.conf.");
                 r = table_add_many(table,
-                                   TABLE_STRING, "Command Line:",
+                                   TABLE_FIELD, "Command Line",
                                    TABLE_SET_COLOR, ansi_highlight_yellow(),
                                    TABLE_STRV, kernel_locale,
                                    TABLE_SET_COLOR, ansi_highlight_yellow());
@@ -96,30 +93,30 @@ static int print_status_info(StatusInfo *i) {
         }
 
         r = table_add_many(table,
-                           TABLE_STRING, "System Locale:",
+                           TABLE_FIELD, "System Locale",
                            TABLE_STRV, i->locale,
-                           TABLE_STRING, "VC Keymap:",
+                           TABLE_FIELD, "VC Keymap",
                            TABLE_STRING, i->vconsole_keymap);
         if (r < 0)
                 return table_log_add_error(r);
 
         if (!isempty(i->vconsole_keymap_toggle)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "VC Toggle Keymap:",
+                                   TABLE_FIELD, "VC Toggle Keymap",
                                    TABLE_STRING, i->vconsole_keymap_toggle);
                 if (r < 0)
                         return table_log_add_error(r);
         }
 
         r = table_add_many(table,
-                           TABLE_STRING, "X11 Layout:",
+                           TABLE_FIELD, "X11 Layout",
                            TABLE_STRING, i->x11_layout);
         if (r < 0)
                 return table_log_add_error(r);
 
         if (!isempty(i->x11_model)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "X11 Model:",
+                                   TABLE_FIELD, "X11 Model",
                                    TABLE_STRING, i->x11_model);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -127,7 +124,7 @@ static int print_status_info(StatusInfo *i) {
 
         if (!isempty(i->x11_variant)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "X11 Variant:",
+                                   TABLE_FIELD, "X11 Variant",
                                    TABLE_STRING, i->x11_variant);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -135,7 +132,7 @@ static int print_status_info(StatusInfo *i) {
 
         if (!isempty(i->x11_options)) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "X11 Options:",
+                                   TABLE_FIELD, "X11 Options",
                                    TABLE_STRING, i->x11_options);
                 if (r < 0)
                         return table_log_add_error(r);
index dd2bbf5bfb80a58e1d2256c2bced26854b498fcc..880d0d209c5c35e020e45db27540aa8cb6ce517d 100644 (file)
@@ -432,7 +432,6 @@ int vconsole_convert_to_x11(Context *c) {
 }
 
 int find_converted_keymap(const char *x11_layout, const char *x11_variant, char **new_keymap) {
-        const char *dir;
         _cleanup_free_ char *n = NULL;
 
         if (x11_variant)
index 0a245247ec766672c15bf5c09f4e0a590d0ed443..75f397dd6bac8ee27f9d9c6389b7a6968fc1eb54 100644 (file)
@@ -1014,9 +1014,9 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
                  * 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy
                  * the UID/GIDs as they are. */
                 if (copy_from)
-                        r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, uid_shift == 0 ? UID_INVALID : 0, uid_shift == 0 ? GID_INVALID : 0, copy_flags);
+                        r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, uid_shift == 0 ? UID_INVALID : 0, uid_shift == 0 ? GID_INVALID : 0, copy_flags, NULL);
                 else
-                        r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, uid_shift == 0 ? UID_INVALID : uid_shift, uid_shift == 0 ? GID_INVALID : uid_shift, copy_flags);
+                        r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, uid_shift == 0 ? UID_INVALID : uid_shift, uid_shift == 0 ? GID_INVALID : uid_shift, copy_flags, NULL);
 
                 hostfd = safe_close(hostfd);
                 containerfd = safe_close(containerfd);
index af0f5d861206a9837eb84f2d1288e4f1aeac2680..93761a1da9bdff993b5a7a25f8c347025246d7c2 100644 (file)
@@ -98,7 +98,7 @@ static OutputFlags get_output_flags(void) {
 static int call_get_os_release(sd_bus *bus, const char *method, const char *name, const char *query, ...) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
-        const char *k, *v, *iter, **query_res = NULL;
+        const char *k, *v, **query_res = NULL;
         size_t count = 0, awaited_args = 0;
         va_list ap;
         int r;
index 77f4c2ac88e2c42fb21c04daa21fe3808b8ac290..27044fadd2a877083d651725a46238ae9333dd34 100644 (file)
@@ -88,6 +88,7 @@ static int add_syscall_filters(
                 { 0,                  "sched_getparam"         },
                 { 0,                  "sched_getscheduler"     },
                 { 0,                  "sched_rr_get_interval"  },
+                { 0,                  "sched_rr_get_interval_time64" },
                 { 0,                  "sched_yield"            },
                 { 0,                  "seccomp"                },
                 { 0,                  "sendfile"               },
index fb9fbed12cbed749e108df2fde5f6349b46c4766..ef5825ef7573c2ea80d2882058ea3bc56c8287ec 100644 (file)
@@ -2219,7 +2219,6 @@ static int copy_devnodes(const char *dest) {
                 "tty\0"
                 "net/tun\0";
 
-        const char *d;
         int r = 0;
 
         assert(dest);
index a037d7be1d22b0acf3dfccf34a33ec6bf045f517..07efcd2857b8c6f3c68f956e0e9cb48195599e41 100644 (file)
@@ -51,6 +51,7 @@
 #include "mkfs-util.h"
 #include "mount-util.h"
 #include "mountpoint-util.h"
+#include "nulstr-util.h"
 #include "openssl-util.h"
 #include "parse-argument.h"
 #include "parse-helpers.h"
 #include "utf8.h"
 
 /* If not configured otherwise use a minimal partition size of 10M */
-#define DEFAULT_MIN_SIZE (10*1024*1024)
+#define DEFAULT_MIN_SIZE (10ULL*1024ULL*1024ULL)
 
 /* Hard lower limit for new partition sizes */
-#define HARD_MIN_SIZE 4096
+#define HARD_MIN_SIZE 4096ULL
 
 /* We know up front we're never going to put more than this in a verity sig partition. */
-#define VERITY_SIG_SIZE (HARD_MIN_SIZE * 4)
+#define VERITY_SIG_SIZE (HARD_MIN_SIZE*4ULL)
 
 /* libfdisk takes off slightly more than 1M of the disk size when creating a GPT disk label */
-#define GPT_METADATA_SIZE (1044*1024)
+#define GPT_METADATA_SIZE (1044ULL*1024ULL)
 
 /* LUKS2 takes off 16M of the partition size with its metadata by default */
-#define LUKS2_METADATA_SIZE (16*1024*1024)
+#define LUKS2_METADATA_SIZE (16ULL*1024ULL*1024ULL)
+
+/* To do LUKS2 offline encryption, we need to keep some extra free space at the end of the partition. */
+#define LUKS2_METADATA_KEEP_FREE (LUKS2_METADATA_SIZE*2ULL)
+
+/* LUKS2 volume key size. */
+#define VOLUME_KEY_SIZE (512ULL/8ULL)
 
 /* Note: When growing and placing new partitions we always align to 4K sector size. It's how newer hard disks
  * are designed, and if everything is aligned to that performance is best. And for older hard disks with 512B
@@ -102,6 +109,14 @@ static enum {
         EMPTY_CREATE,   /* create disk as loopback file, create a partition table always */
 } arg_empty = EMPTY_REFUSE;
 
+typedef enum {
+        FILTER_PARTITIONS_NONE,
+        FILTER_PARTITIONS_EXCLUDE,
+        FILTER_PARTITIONS_INCLUDE,
+        _FILTER_PARTITIONS_MAX,
+        _FILTER_PARTITIONS_INVALID = -EINVAL,
+} FilterPartitionsType;
+
 static bool arg_dry_run = true;
 static const char *arg_node = NULL;
 static char *arg_root = NULL;
@@ -127,6 +142,9 @@ static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
 static char *arg_tpm2_public_key = NULL;
 static uint32_t arg_tpm2_public_key_pcr_mask = UINT32_MAX;
 static bool arg_split = false;
+static sd_id128_t *arg_filter_partitions = NULL;
+static size_t arg_filter_partitions_size = 0;
+static FilterPartitionsType arg_filter_partitions_type = FILTER_PARTITIONS_NONE;
 
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
@@ -136,6 +154,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_private_key, EVP_PKEY_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_certificate, X509_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep);
 
 typedef struct Partition Partition;
 typedef struct FreeArea FreeArea;
@@ -163,10 +182,11 @@ struct Partition {
         char *definition_path;
         char **drop_in_files;
 
-        sd_id128_t type_uuid;
+        GptPartitionType type;
         sd_id128_t current_uuid, new_uuid;
         bool new_uuid_is_set;
         char *current_label, *new_label;
+        sd_id128_t fs_uuid;
 
         bool dropped;
         bool factory_reset;
@@ -190,6 +210,7 @@ struct Partition {
 
         char *copy_blocks_path;
         bool copy_blocks_auto;
+        const char *copy_blocks_root;
         int copy_blocks_fd;
         uint64_t copy_blocks_size;
 
@@ -199,6 +220,7 @@ struct Partition {
         EncryptMode encrypt;
         VerityMode verity;
         char *verity_match_key;
+        bool minimize;
 
         uint64_t gpt_flags;
         int no_auto;
@@ -255,12 +277,7 @@ static const char *verity_mode_table[_VERITY_MODE_MAX] = {
         [VERITY_SIG]  = "signature",
 };
 
-#if HAVE_LIBCRYPTSETUP
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(encrypt_mode, EncryptMode, ENCRYPT_KEY_FILE);
-#else
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(encrypt_mode, EncryptMode, ENCRYPT_KEY_FILE);
-#endif
-
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(verity_mode, VerityMode);
 
 static uint64_t round_down_size(uint64_t v, uint64_t p) {
@@ -343,20 +360,18 @@ static void partition_foreignize(Partition *p) {
 
         /* Reset several parameters set through definition file to make the partition foreign. */
 
-        p->new_label = mfree(p->new_label);
         p->definition_path = mfree(p->definition_path);
         p->drop_in_files = strv_free(p->drop_in_files);
 
         p->copy_blocks_path = mfree(p->copy_blocks_path);
         p->copy_blocks_fd = safe_close(p->copy_blocks_fd);
+        p->copy_blocks_root = NULL;
 
         p->format = mfree(p->format);
         p->copy_files = strv_free(p->copy_files);
         p->make_directories = strv_free(p->make_directories);
         p->verity_match_key = mfree(p->verity_match_key);
 
-        p->new_uuid = SD_ID128_NULL;
-        p->new_uuid_is_set = false;
         p->priority = 0;
         p->weight = 1000;
         p->padding_weight = 0;
@@ -370,6 +385,19 @@ static void partition_foreignize(Partition *p) {
         p->verity = VERITY_OFF;
 }
 
+static bool partition_skip(const Partition *p) {
+        assert(p);
+
+        if (arg_filter_partitions_type == FILTER_PARTITIONS_NONE)
+                return false;
+
+        for (size_t i = 0; i < arg_filter_partitions_size; i++)
+                if (sd_id128_equal(p->type.uuid, arg_filter_partitions[i]))
+                        return arg_filter_partitions_type == FILTER_PARTITIONS_EXCLUDE;
+
+        return arg_filter_partitions_type == FILTER_PARTITIONS_INCLUDE;
+}
+
 static Partition* partition_unlink_and_free(Context *context, Partition *p) {
         if (!p)
                 return NULL;
@@ -536,7 +564,7 @@ static uint64_t partition_min_size(const Context *context, const Partition *p) {
                 uint64_t d = 0;
 
                 if (p->encrypt != ENCRYPT_OFF)
-                        d += round_up_size(LUKS2_METADATA_SIZE, context->grain_size);
+                        d += round_up_size(LUKS2_METADATA_KEEP_FREE, context->grain_size);
 
                 if (p->copy_blocks_size != UINT64_MAX)
                         d += round_up_size(p->copy_blocks_size, context->grain_size);
@@ -1023,21 +1051,30 @@ static int context_grow_partitions(Context *context) {
         return 0;
 }
 
-static void context_place_partitions(Context *context) {
+static uint64_t find_first_unused_partno(Context *context) {
         uint64_t partno = 0;
 
         assert(context);
 
-        /* Determine next partition number to assign */
-        LIST_FOREACH(partitions, p, context->partitions) {
-                if (!PARTITION_EXISTS(p))
-                        continue;
+        for (bool changed = true; changed;) {
+                changed = false;
 
-                assert(p->partno != UINT64_MAX);
-                if (p->partno >= partno)
-                        partno = p->partno + 1;
+                LIST_FOREACH(partitions, p, context->partitions) {
+                        if (p->partno != UINT64_MAX && p->partno == partno) {
+                                partno++;
+                                changed = true;
+                                break;
+                        }
+                }
         }
 
+        return partno;
+}
+
+static void context_place_partitions(Context *context) {
+
+        assert(context);
+
         for (size_t i = 0; i < context->n_free_areas; i++) {
                 FreeArea *a = context->free_areas[i];
                 _unused_ uint64_t left;
@@ -1060,7 +1097,7 @@ static void context_place_partitions(Context *context) {
                                 continue;
 
                         p->offset = start;
-                        p->partno = partno++;
+                        p->partno = find_first_unused_partno(context);
 
                         assert(left >= p->new_size);
                         start += p->new_size;
@@ -1085,12 +1122,12 @@ static int config_parse_type(
                 void *data,
                 void *userdata) {
 
-        sd_id128_t *type_uuid = ASSERT_PTR(data);
+        GptPartitionType *type = ASSERT_PTR(data);
         int r;
 
         assert(rvalue);
 
-        r = gpt_partition_type_uuid_from_string(rvalue, type_uuid);
+        r = gpt_partition_type_from_string(rvalue, type);
         if (r < 0)
                 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse partition type: %s", rvalue);
 
@@ -1337,6 +1374,7 @@ static int config_parse_copy_blocks(
         if (streq(rvalue, "auto")) {
                 partition->copy_blocks_path = mfree(partition->copy_blocks_path);
                 partition->copy_blocks_auto = true;
+                partition->copy_blocks_root = arg_root;
                 return 0;
         }
 
@@ -1353,6 +1391,7 @@ static int config_parse_copy_blocks(
 
         free_and_replace(partition->copy_blocks_path, d);
         partition->copy_blocks_auto = false;
+        partition->copy_blocks_root = arg_root;
         return 0;
 }
 
@@ -1474,7 +1513,7 @@ static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, V
 static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
 
         ConfigTableItem table[] = {
-                { "Partition", "Type",            config_parse_type,        0, &p->type_uuid         },
+                { "Partition", "Type",            config_parse_type,        0, &p->type              },
                 { "Partition", "Label",           config_parse_label,       0, &p->new_label         },
                 { "Partition", "UUID",            config_parse_uuid,        0, p                     },
                 { "Partition", "Priority",        config_parse_int32,       0, &p->priority          },
@@ -1497,6 +1536,7 @@ static int partition_read_definition(Partition *p, const char *path, const char
                 { "Partition", "NoAuto",          config_parse_tristate,    0, &p->no_auto           },
                 { "Partition", "GrowFileSystem",  config_parse_tristate,    0, &p->growfs            },
                 { "Partition", "SplitName",       config_parse_string,      0, &p->split_name_format },
+                { "Partition", "Minimize",        config_parse_bool,        0, &p->minimize          },
                 {}
         };
         int r;
@@ -1505,7 +1545,7 @@ static int partition_read_definition(Partition *p, const char *path, const char
 
         r = path_extract_filename(path, &filename);
         if (r < 0)
-                return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);;
+                return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
 
         dropin_dirname = strjoina(filename, ".d");
 
@@ -1530,7 +1570,7 @@ static int partition_read_definition(Partition *p, const char *path, const char
                 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
                                   "PaddingMinBytes= larger than PaddingMaxBytes=, refusing.");
 
-        if (sd_id128_is_null(p->type_uuid))
+        if (sd_id128_is_null(p->type.uuid))
                 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
                                   "Type= not defined, refusing.");
 
@@ -1550,6 +1590,19 @@ static int partition_read_definition(Partition *p, const char *path, const char
                         return log_oom();
         }
 
+        if (p->minimize && !p->format)
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                  "Minimize= can only be enabled if Format= is set");
+
+        if ((!strv_isempty(p->copy_files) || !strv_isempty(p->make_directories)) && !mkfs_supports_root_option(p->format) && geteuid() != 0)
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EPERM),
+                                  "Need to be root to populate %s filesystems with CopyFiles=/MakeDirectories=",
+                                  p->format);
+
+        if (p->format && fstype_is_ro(p->format) && strv_isempty(p->copy_files) && strv_isempty(p->make_directories))
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                  "Cannot format %s filesystem without source files, refusing", p->format);
+
         if (p->verity != VERITY_OFF || p->encrypt != ENCRYPT_OFF) {
                 r = dlopen_cryptsetup();
                 if (r < 0)
@@ -1590,13 +1643,13 @@ static int partition_read_definition(Partition *p, const char *path, const char
                                   verity_mode_to_string(p->verity));
 
         /* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
-        if ((gpt_partition_type_is_root_verity(p->type_uuid) ||
-             gpt_partition_type_is_usr_verity(p->type_uuid)) &&
+        if ((p->type.designator == PARTITION_ROOT_VERITY ||
+             p->type.designator == PARTITION_USR_VERITY) &&
             p->read_only < 0)
                 p->read_only = true;
 
         /* Default to "growfs" on, unless read-only */
-        if (gpt_partition_type_knows_growfs(p->type_uuid) &&
+        if (gpt_partition_type_knows_growfs(p->type) &&
             p->read_only <= 0)
                 p->growfs = true;
 
@@ -2090,7 +2143,7 @@ static int context_load_partition_table(
                 LIST_FOREACH(partitions, pp, context->partitions) {
                         last = pp;
 
-                        if (!sd_id128_equal(pp->type_uuid, ptid))
+                        if (!sd_id128_equal(pp->type.uuid, ptid))
                                 continue;
 
                         if (!pp->current_partition) {
@@ -2127,7 +2180,7 @@ static int context_load_partition_table(
                                 return log_oom();
 
                         np->current_uuid = id;
-                        np->type_uuid = ptid;
+                        np->type = gpt_partition_type_from_uuid(ptid);
                         np->current_size = sz;
                         np->offset = start;
                         np->partno = partno;
@@ -2286,7 +2339,7 @@ static const char *partition_label(const Partition *p) {
         if (p->current_label)
                 return p->current_label;
 
-        return gpt_partition_type_uuid_to_string(p->type_uuid);
+        return gpt_partition_type_uuid_to_string(p->type.uuid);
 }
 
 static int context_dump_partitions(Context *context, const char *node) {
@@ -2360,7 +2413,7 @@ static int context_dump_partitions(Context *context, const char *node) {
 
                 r = table_add_many(
                                 t,
-                                TABLE_STRING, gpt_partition_type_uuid_to_string_harder(p->type_uuid, uuid_buffer),
+                                TABLE_STRING, gpt_partition_type_uuid_to_string_harder(p->type.uuid, uuid_buffer),
                                 TABLE_STRING, empty_to_null(label) ?: "-", TABLE_SET_COLOR, empty_to_null(label) ? NULL : ansi_grey(),
                                 TABLE_UUID, p->new_uuid_is_set ? p->new_uuid : p->current_uuid,
                                 TABLE_STRING, p->definition_path ? basename(p->definition_path) : "-", TABLE_SET_COLOR, p->definition_path ? NULL : ansi_grey(),
@@ -2494,7 +2547,7 @@ static int partition_hint(const Partition *p, const char *node, char **ret) {
         else if (!sd_id128_is_null(p->current_uuid))
                 id = p->current_uuid;
         else
-                id = p->type_uuid;
+                id = p->type.uuid;
 
         buf = strdup(SD_ID128_TO_UUID_STRING(id));
 
@@ -2912,6 +2965,9 @@ static int context_wipe_and_discard(Context *context, bool from_scratch) {
                 if (!p->allocated_to_area)
                         continue;
 
+                if (partition_skip(p))
+                        continue;
+
                 r = context_wipe_partition(context, p);
                 if (r < 0)
                         return r;
@@ -2936,71 +2992,251 @@ static int context_wipe_and_discard(Context *context, bool from_scratch) {
         return 0;
 }
 
-static int partition_encrypt(
+typedef struct {
+        LoopDevice *loop;
+        int fd;
+        char *path;
+        int whole_fd;
+} PartitionTarget;
+
+static int partition_target_fd(PartitionTarget *t) {
+        assert(t);
+        assert(t->loop || t->fd >= 0 || t->whole_fd >= 0);
+        return t->loop ? t->loop->fd : t->fd >= 0 ?  t->fd : t->whole_fd;
+}
+
+static const char* partition_target_path(PartitionTarget *t) {
+        assert(t);
+        assert(t->loop || t->path);
+        return t->loop ? t->loop->node : t->path;
+}
+
+static PartitionTarget *partition_target_free(PartitionTarget *t) {
+        if (!t)
+                return NULL;
+
+        loop_device_unref(t->loop);
+        safe_close(t->fd);
+        unlink_and_free(t->path);
+
+        return mfree(t);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(PartitionTarget*, partition_target_free);
+
+static int partition_target_prepare(
                 Context *context,
                 Partition *p,
-                const char *node,
-                struct crypt_device **ret_cd,
-                char **ret_volume,
-                int *ret_fd) {
-#if HAVE_LIBCRYPTSETUP
-        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
-        _cleanup_(erase_and_freep) void *volume_key = NULL;
-        _cleanup_free_ char *dm_name = NULL, *vol = NULL;
-        size_t volume_key_size = 256 / 8;
-        sd_id128_t uuid;
+                uint64_t size,
+                bool need_path,
+                PartitionTarget **ret) {
+
+        _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
+        struct stat st;
+        int whole_fd;
         int r;
 
         assert(context);
         assert(p);
-        assert(p->encrypt != ENCRYPT_OFF);
+        assert(ret);
 
-        log_debug("Encryption mode for partition %" PRIu64 ": %s", p->partno, encrypt_mode_to_string(p->encrypt));
+        assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
 
-        r = dlopen_cryptsetup();
-        if (r < 0)
-                return log_error_errno(r, "libcryptsetup not found, cannot encrypt: %m");
+        if (fstat(whole_fd, &st) < 0)
+                return -errno;
+
+        /* If we're operating on a block device, we definitely need privileges to access block devices so we
+         * can just use loop devices as our target. Otherwise, we're operating on a regular file, in that
+         * case, let's write to regular files and copy those into the final image so we can run without root
+         * privileges. On filesystems with reflinking support, we can take advantage of this and just reflink
+         * the result into the image.
+         */
 
-        if (asprintf(&dm_name, "luks-repart-%08" PRIx64, random_u64()) < 0)
+        t = new0(PartitionTarget, 1);
+        if (!t)
                 return log_oom();
 
-        if (ret_volume) {
-                vol = path_join("/dev/mapper/", dm_name);
-                if (!vol)
+        if (S_ISBLK(st.st_mode) || (p->format && !mkfs_supports_root_option(p->format))) {
+                _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+
+                /* Loopback block devices are not only useful to turn regular files into block devices, but
+                 * also to cut out sections of block devices into new block devices. */
+
+                r = loop_device_make(whole_fd, O_RDWR, p->offset, size, 0, 0, LOCK_EX, &d);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno);
+
+                *t = (PartitionTarget) {
+                        .loop = TAKE_PTR(d),
+                        .fd = -1,
+                };
+        } else if (need_path) {
+                _cleanup_(unlink_and_freep) char *temp = NULL;
+                _cleanup_close_ int fd = -1;
+                const char *vt;
+
+                r = var_tmp_dir(&vt);
+                if (r < 0)
+                        return log_error_errno(r, "Could not determine temporary directory: %m");
+
+                temp = path_join(vt, "repart-XXXXXX");
+                if (!temp)
                         return log_oom();
+
+                fd = mkostemp_safe(temp);
+                if (fd < 0)
+                        return log_error_errno(fd, "Failed to create temporary file: %m");
+
+                if (ftruncate(fd, size) < 0)
+                        return log_error_errno(errno, "Failed to truncate temporary file to %s: %m",
+                                               FORMAT_BYTES(size));
+
+                *t = (PartitionTarget) {
+                        .fd = TAKE_FD(fd),
+                        .path = TAKE_PTR(temp),
+                };
+        } else {
+                if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
+                        return log_error_errno(errno, "Failed to seek to partition offset: %m");
+
+                *t = (PartitionTarget) {
+                        .fd = -1,
+                        .whole_fd = whole_fd,
+                };
+        }
+
+        *ret = TAKE_PTR(t);
+
+        return 0;
+}
+
+static int partition_target_grow(PartitionTarget *t, uint64_t size) {
+        int r;
+
+        assert(t);
+
+        if (t->loop) {
+                r = loop_device_refresh_size(t->loop, UINT64_MAX, size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to refresh loopback device size: %m");
+        } else if (t->fd >= 0) {
+                if (ftruncate(t->fd, size) < 0)
+                        return log_error_errno(errno, "Failed to grow '%s' to %s by truncation: %m",
+                                               t->path, FORMAT_BYTES(size));
+        }
+
+        return 0;
+}
+
+static int partition_target_sync(Context *context, Partition *p, PartitionTarget *t) {
+        int whole_fd, r;
+
+        assert(context);
+        assert(p);
+        assert(t);
+
+        assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
+
+        if (t->loop) {
+                r = loop_device_sync(t->loop);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to sync loopback device: %m");
+        } else if (t->fd >= 0) {
+                if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
+                        return log_error_errno(errno, "Failed to seek to partition offset: %m");
+
+                r = copy_bytes(t->fd, whole_fd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_FSYNC);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to copy bytes to partition: %m");
+        } else {
+                if (fsync(t->whole_fd) < 0)
+                        return log_error_errno(errno, "Failed to sync changes: %m");
         }
 
+        return 0;
+}
+
+static int partition_encrypt(Context *context, Partition *p, const char *node) {
+#if HAVE_LIBCRYPTSETUP && HAVE_CRYPT_SET_DATA_OFFSET && HAVE_CRYPT_REENCRYPT_INIT_BY_PASSPHRASE && HAVE_CRYPT_REENCRYPT
+        struct crypt_params_luks2 luks_params = {
+                .label = strempty(p->new_label),
+                .sector_size = context->sector_size,
+                .data_device = node,
+        };
+        struct crypt_params_reencrypt reencrypt_params = {
+                .mode = CRYPT_REENCRYPT_ENCRYPT,
+                .direction = CRYPT_REENCRYPT_BACKWARD,
+                .resilience = "datashift",
+                .data_shift = LUKS2_METADATA_SIZE / 512,
+                .luks2 = &luks_params,
+                .flags = CRYPT_REENCRYPT_INITIALIZE_ONLY|CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT,
+        };
+        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+        _cleanup_fclose_ FILE *h = NULL;
+        _cleanup_free_ char *hp = NULL;
+        const char *passphrase = NULL;
+        size_t passphrase_size = 0;
+        sd_id128_t uuid;
+        const char *vt;
+        int r;
+
+        assert(context);
+        assert(p);
+        assert(p->encrypt != ENCRYPT_OFF);
+
+        r = dlopen_cryptsetup();
+        if (r < 0)
+                return log_error_errno(r, "libcryptsetup not found, cannot encrypt: %m");
+
         r = derive_uuid(p->new_uuid, "luks-uuid", &uuid);
         if (r < 0)
                 return r;
 
         log_info("Encrypting future partition %" PRIu64 "...", p->partno);
 
-        volume_key = malloc(volume_key_size);
-        if (!volume_key)
-                return log_oom();
+        r = var_tmp_dir(&vt);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine temporary files directory: %m");
 
-        r = crypto_random_bytes(volume_key, volume_key_size);
+        r = fopen_temporary_child(vt, &h, &hp);
         if (r < 0)
-                return log_error_errno(r, "Failed to generate volume key: %m");
+                return log_error_errno(r, "Failed to create temporary LUKS header file: %m");
 
-        r = sym_crypt_init(&cd, node);
+        /* Weird cryptsetup requirement which requires the header file to be the size of at least one sector. */
+        r = posix_fallocate(fileno(h), 0, context->sector_size);
         if (r < 0)
-                return log_error_errno(r, "Failed to allocate libcryptsetup context: %m");
+                return log_error_errno(r, "Failed to grow temporary LUKS header file: %m");
+
+        r = sym_crypt_init(&cd, hp);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", hp);
 
         cryptsetup_enable_logging(cd);
 
+        /* Disable kernel keyring usage by libcryptsetup as a workaround for
+         * https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/273. This makes sure that we can do
+         * offline encryption even when repart is running in a container. */
+        r = sym_crypt_volume_key_keyring(cd, false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to disable kernel keyring: %m");
+
+        r = sym_crypt_metadata_locking(cd, false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to disable metadata locking: %m");
+
+        r = sym_crypt_set_data_offset(cd, LUKS2_METADATA_SIZE / 512);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set data offset: %m");
+
         r = sym_crypt_format(cd,
                          CRYPT_LUKS2,
                          "aes",
                          "xts-plain64",
                          SD_ID128_TO_UUID_STRING(uuid),
-                         volume_key,
-                         volume_key_size,
-                         &(struct crypt_params_luks2) {
-                                 .label = strempty(p->new_label),
-                                 .sector_size = context->sector_size,
-                         });
+                         NULL,
+                         VOLUME_KEY_SIZE,
+                         &luks_params);
         if (r < 0)
                 return log_error_errno(r, "Failed to LUKS2 format future partition: %m");
 
@@ -3008,17 +3244,19 @@ static int partition_encrypt(
                 r = sym_crypt_keyslot_add_by_volume_key(
                                 cd,
                                 CRYPT_ANY_SLOT,
-                                volume_key,
-                                volume_key_size,
+                                NULL,
+                                VOLUME_KEY_SIZE,
                                 strempty(arg_key),
                                 arg_key_size);
                 if (r < 0)
                         return log_error_errno(r, "Failed to add LUKS2 key: %m");
+
+                passphrase = strempty(arg_key);
+                passphrase_size = arg_key_size;
         }
 
         if (IN_SET(p->encrypt, ENCRYPT_TPM2, ENCRYPT_KEY_FILE_TPM2)) {
 #if HAVE_TPM2
-                _cleanup_(erase_and_freep) char *base64_encoded = NULL;
                 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
                 _cleanup_(erase_and_freep) void *secret = NULL;
                 _cleanup_free_ void *pubkey = NULL;
@@ -3062,12 +3300,12 @@ static int partition_encrypt(
                 keyslot = sym_crypt_keyslot_add_by_volume_key(
                                 cd,
                                 CRYPT_ANY_SLOT,
-                                volume_key,
-                                volume_key_size,
+                                NULL,
+                                VOLUME_KEY_SIZE,
                                 base64_encoded,
                                 strlen(base64_encoded));
                 if (keyslot < 0)
-                        return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
+                        return log_error_errno(keyslot, "Failed to add new TPM2 key: %m");
 
                 r = tpm2_make_luks2_json(
                                 keyslot,
@@ -3086,150 +3324,355 @@ static int partition_encrypt(
                 r = cryptsetup_add_token_json(cd, v);
                 if (r < 0)
                         return log_error_errno(r, "Failed to add TPM2 JSON token to LUKS2 header: %m");
+
+                passphrase = base64_encoded;
+                passphrase_size = strlen(base64_encoded);
 #else
                 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                        "Support for TPM2 enrollment not enabled.");
 #endif
         }
 
-        r = sym_crypt_activate_by_volume_key(
+        r = sym_crypt_reencrypt_init_by_passphrase(
                         cd,
-                        dm_name,
-                        volume_key,
-                        volume_key_size,
-                        arg_discard ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0);
+                        NULL,
+                        passphrase,
+                        passphrase_size,
+                        CRYPT_ANY_SLOT,
+                        0,
+                        sym_crypt_get_cipher(cd),
+                        sym_crypt_get_cipher_mode(cd),
+                        &reencrypt_params);
         if (r < 0)
-                return log_error_errno(r, "Failed to activate LUKS superblock: %m");
-
-        log_info("Successfully encrypted future partition %" PRIu64 ".", p->partno);
-
-        if (ret_fd) {
-                _cleanup_close_ int dev_fd = -1;
-
-                dev_fd = open(vol, O_RDWR|O_CLOEXEC|O_NOCTTY);
-                if (dev_fd < 0)
-                        return log_error_errno(errno, "Failed to open LUKS volume '%s': %m", vol);
-
-                *ret_fd = TAKE_FD(dev_fd);
-        }
+                return log_error_errno(r, "Failed to prepare for reencryption: %m");
 
-        if (ret_cd)
-                *ret_cd = TAKE_PTR(cd);
-        if (ret_volume)
-                *ret_volume = TAKE_PTR(vol);
+        /* crypt_reencrypt_init_by_passphrase() doesn't actually put the LUKS header at the front, we have
+         * to do that ourselves. */
 
-        return 0;
-#else
-        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libcryptsetup is not supported, cannot encrypt: %m");
-#endif
-}
+        sym_crypt_free(cd);
+        cd = NULL;
 
-static int deactivate_luks(struct crypt_device *cd, const char *node) {
-#if HAVE_LIBCRYPTSETUP
-        int r;
+        r = sym_crypt_init(&cd, node);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", node);
 
-        if (!cd)
-                return 0;
+        r = sym_crypt_header_restore(cd, CRYPT_LUKS2, hp);
+        if (r < 0)
+                return log_error_errno(r, "Failed to place new LUKS header at head of %s: %m", node);
 
-        assert(node);
+        reencrypt_params.flags &= ~CRYPT_REENCRYPT_INITIALIZE_ONLY;
 
-        /* udev or so might access out block device in the background while we are done. Let's hence force
-         * detach the volume. We sync'ed before, hence this should be safe. */
+        r = sym_crypt_reencrypt_init_by_passphrase(
+                        cd,
+                        NULL,
+                        passphrase,
+                        passphrase_size,
+                        CRYPT_ANY_SLOT,
+                        0,
+                        NULL,
+                        NULL,
+                        &reencrypt_params);
+        if (r < 0)
+                return log_error_errno(r, "Failed to load reencryption context: %m");
 
-        r = sym_crypt_deactivate_by_name(cd, basename(node), CRYPT_DEACTIVATE_FORCE);
+        r = sym_crypt_reencrypt(cd, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to deactivate LUKS device: %m");
+                return log_error_errno(r, "Failed to encrypt %s: %m", node);
+
+        log_info("Successfully encrypted future partition %" PRIu64 ".", p->partno);
 
-        return 1;
-#else
         return 0;
+#else
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                               "libcryptsetup is not supported or is missing required symbols, cannot encrypt: %m");
 #endif
 }
 
-static int context_copy_blocks(Context *context) {
-        int whole_fd = -1, r;
-
-        assert(context);
-
-        /* Copy in file systems on the block level */
-
-        LIST_FOREACH(partitions, p, context->partitions) {
-                _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
-                _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
-                _cleanup_free_ char *encrypted = NULL;
-                _cleanup_close_ int encrypted_dev_fd = -1;
-                int target_fd;
+static int partition_format_verity_hash(
+                Context *context,
+                Partition *p,
+                const char *data_node) {
 
-                if (p->copy_blocks_fd < 0)
-                        continue;
+#if HAVE_LIBCRYPTSETUP
+        Partition *dp;
+        _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
+        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_free_ uint8_t *rh = NULL;
+        size_t rhs;
+        int r;
 
-                if (p->dropped)
-                        continue;
+        assert(context);
+        assert(p);
+        assert(data_node);
 
-                if (PARTITION_EXISTS(p)) /* Never copy over existing partitions */
-                        continue;
+        if (p->dropped)
+                return 0;
 
-                assert(p->new_size != UINT64_MAX);
-                assert(p->copy_blocks_size != UINT64_MAX);
-                assert(p->new_size >= p->copy_blocks_size);
+        if (PARTITION_EXISTS(p)) /* Never format existing partitions */
+                return 0;
 
-                if (whole_fd < 0)
-                        assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
+        if (p->verity != VERITY_HASH)
+                return 0;
 
-                if (p->encrypt != ENCRYPT_OFF) {
-                        r = loop_device_make(whole_fd, O_RDWR, p->offset, p->new_size, 0, 0, LOCK_EX, &d);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno);
+        if (partition_skip(p))
+                return 0;
 
-                        r = partition_encrypt(context, p, d->node, &cd, &encrypted, &encrypted_dev_fd);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to encrypt device: %m");
+        assert_se(dp = p->siblings[VERITY_DATA]);
+        assert(!dp->dropped);
 
-                        if (flock(encrypted_dev_fd, LOCK_EX) < 0)
-                                return log_error_errno(errno, "Failed to lock LUKS device: %m");
+        r = dlopen_cryptsetup();
+        if (r < 0)
+                return log_error_errno(r, "libcryptsetup not found, cannot setup verity: %m");
 
-                        target_fd = encrypted_dev_fd;
-                } else {
-                        if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
-                                return log_error_errno(errno, "Failed to seek to partition offset: %m");
+        r = partition_target_prepare(context, p, p->new_size, /*need_path=*/ true, &t);
+        if (r < 0)
+                return r;
 
-                        target_fd = whole_fd;
-                }
+        r = sym_crypt_init(&cd, partition_target_path(t));
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate libcryptsetup context: %m");
 
-                log_info("Copying in '%s' (%s) on block level into future partition %" PRIu64 ".",
-                         p->copy_blocks_path, FORMAT_BYTES(p->copy_blocks_size), p->partno);
+        r = sym_crypt_format(
+                        cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0,
+                        &(struct crypt_params_verity){
+                                .data_device = data_node,
+                                .flags = CRYPT_VERITY_CREATE_HASH,
+                                .hash_name = "sha256",
+                                .hash_type = 1,
+                                .data_block_size = context->sector_size,
+                                .hash_block_size = context->sector_size,
+                                .salt_size = 32,
+                        });
+        if (r < 0)
+                return log_error_errno(r, "Failed to setup verity hash data: %m");
 
-                r = copy_bytes_full(p->copy_blocks_fd, target_fd, p->copy_blocks_size, 0, NULL, NULL, NULL, NULL);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path);
+        r = partition_target_sync(context, p, t);
+        if (r < 0)
+                return r;
 
-                if (fsync(target_fd) < 0)
-                        return log_error_errno(errno, "Failed to synchronize copied data blocks: %m");
+        r = sym_crypt_get_volume_key_size(cd);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine verity root hash size: %m");
+        rhs = (size_t) r;
 
-                if (p->encrypt != ENCRYPT_OFF) {
-                        encrypted_dev_fd = safe_close(encrypted_dev_fd);
+        rh = malloc(rhs);
+        if (!rh)
+                return log_oom();
 
-                        r = deactivate_luks(cd, encrypted);
-                        if (r < 0)
-                                return r;
+        r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, (char *) rh, &rhs, NULL, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get verity root hash: %m");
 
-                        sym_crypt_free(cd);
-                        cd = NULL;
+        assert(rhs >= sizeof(sd_id128_t) * 2);
 
-                        r = loop_device_sync(d);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to sync loopback device: %m");
-                }
+        if (!dp->new_uuid_is_set) {
+                memcpy_safe(dp->new_uuid.bytes, rh, sizeof(sd_id128_t));
+                dp->new_uuid_is_set = true;
+        }
 
-                log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
+        if (!p->new_uuid_is_set) {
+                memcpy_safe(p->new_uuid.bytes, rh + rhs - sizeof(sd_id128_t), sizeof(sd_id128_t));
+                p->new_uuid_is_set = true;
         }
 
+        p->roothash = TAKE_PTR(rh);
+        p->roothash_size = rhs;
+
         return 0;
+#else
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libcryptsetup is not supported, cannot setup verity hashes: %m");
+#endif
 }
 
-static int do_copy_files(Partition *p, const char *root) {
-        int r;
-
+static int sign_verity_roothash(
+                const uint8_t *roothash,
+                size_t roothash_size,
+                uint8_t **ret_signature,
+                size_t *ret_signature_size) {
+
+#if HAVE_OPENSSL
+        _cleanup_(BIO_freep) BIO *rb = NULL;
+        _cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
+        _cleanup_free_ char *hex = NULL;
+        _cleanup_free_ uint8_t *sig = NULL;
+        int sigsz;
+
+        assert(roothash);
+        assert(roothash_size > 0);
+        assert(ret_signature);
+        assert(ret_signature_size);
+
+        hex = hexmem(roothash, roothash_size);
+        if (!hex)
+                return log_oom();
+
+        rb = BIO_new_mem_buf(hex, -1);
+        if (!rb)
+                return log_oom();
+
+        p7 = PKCS7_sign(arg_certificate, arg_private_key, NULL, rb, PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY);
+        if (!p7)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to calculate PKCS7 signature: %s",
+                                       ERR_error_string(ERR_get_error(), NULL));
+
+        sigsz = i2d_PKCS7(p7, &sig);
+        if (sigsz < 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert PKCS7 signature to DER: %s",
+                                       ERR_error_string(ERR_get_error(), NULL));
+
+        *ret_signature = TAKE_PTR(sig);
+        *ret_signature_size = sigsz;
+
+        return 0;
+#else
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot setup verity signature: %m");
+#endif
+}
+
+static int partition_format_verity_sig(Context *context, Partition *p) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        _cleanup_free_ uint8_t *sig = NULL;
+        _cleanup_free_ char *text = NULL;
+        Partition *hp;
+        uint8_t fp[X509_FINGERPRINT_SIZE];
+        size_t sigsz = 0, padsz; /* avoid false maybe-uninitialized warning */
+        int whole_fd, r;
+
+        assert(p->verity == VERITY_SIG);
+
+        if (p->dropped)
+                return 0;
+
+        if (PARTITION_EXISTS(p))
+                return 0;
+
+        if (partition_skip(p))
+                return 0;
+
+        assert_se(hp = p->siblings[VERITY_HASH]);
+        assert(!hp->dropped);
+
+        assert(arg_certificate);
+
+        assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
+
+        r = sign_verity_roothash(hp->roothash, hp->roothash_size, &sig, &sigsz);
+        if (r < 0)
+                return r;
+
+        r = x509_fingerprint(arg_certificate, fp);
+        if (r < 0)
+                return log_error_errno(r, "Unable to calculate X509 certificate fingerprint: %m");
+
+        r = json_build(&v,
+                        JSON_BUILD_OBJECT(
+                                JSON_BUILD_PAIR("rootHash", JSON_BUILD_HEX(hp->roothash, hp->roothash_size)),
+                                JSON_BUILD_PAIR(
+                                        "certificateFingerprint",
+                                        JSON_BUILD_HEX(fp, sizeof(fp))
+                                ),
+                                JSON_BUILD_PAIR("signature", JSON_BUILD_BASE64(sig, sigsz))
+                        )
+        );
+        if (r < 0)
+                return log_error_errno(r, "Failed to build JSON object: %m");
+
+        r = json_variant_format(v, 0, &text);
+        if (r < 0)
+                return log_error_errno(r, "Failed to format JSON object: %m");
+
+        padsz = round_up_size(strlen(text), 4096);
+        assert_se(padsz <= p->new_size);
+
+        r = strgrowpad0(&text, padsz);
+        if (r < 0)
+                return log_error_errno(r, "Failed to pad string to %s", FORMAT_BYTES(padsz));
+
+        if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
+                return log_error_errno(errno, "Failed to seek to partition offset: %m");
+
+        r = loop_write(whole_fd, text, padsz, /*do_poll=*/ false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write verity signature to partition: %m");
+
+        if (fsync(whole_fd) < 0)
+                return log_error_errno(errno, "Failed to synchronize verity signature JSON: %m");
+
+        return 0;
+}
+
+static int context_copy_blocks(Context *context) {
+        int r;
+
+        assert(context);
+
+        /* Copy in file systems on the block level */
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+                _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
+
+                if (p->copy_blocks_fd < 0)
+                        continue;
+
+                if (p->dropped)
+                        continue;
+
+                if (PARTITION_EXISTS(p)) /* Never copy over existing partitions */
+                        continue;
+
+                if (partition_skip(p))
+                        continue;
+
+                assert(p->new_size != UINT64_MAX);
+                assert(p->copy_blocks_size != UINT64_MAX);
+                assert(p->new_size >= p->copy_blocks_size + (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0));
+
+                r = partition_target_prepare(context, p, p->new_size,
+                                             /*need_path=*/ p->encrypt != ENCRYPT_OFF || p->siblings[VERITY_HASH],
+                                             &t);
+                if (r < 0)
+                        return r;
+
+                log_info("Copying in '%s' (%s) on block level into future partition %" PRIu64 ".",
+                         p->copy_blocks_path, FORMAT_BYTES(p->copy_blocks_size), p->partno);
+
+                r = copy_bytes(p->copy_blocks_fd, partition_target_fd(t), p->copy_blocks_size, COPY_REFLINK);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path);
+
+                if (p->encrypt != ENCRYPT_OFF) {
+                        r = partition_encrypt(context, p, partition_target_path(t));
+                        if (r < 0)
+                                return r;
+                }
+
+                r = partition_target_sync(context, p, t);
+                if (r < 0)
+                        return r;
+
+                log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
+
+                if (p->siblings[VERITY_HASH]) {
+                        r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
+                                                         partition_target_path(t));
+                        if (r < 0)
+                                return r;
+                }
+
+                if (p->siblings[VERITY_SIG]) {
+                        r = partition_format_verity_sig(context, p->siblings[VERITY_SIG]);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        return 0;
+}
+
+static int do_copy_files(Partition *p, const char *root, const Set *denylist) {
+
+        int r;
+
         assert(p);
         assert(root);
 
@@ -3272,16 +3715,19 @@ static int do_copy_files(Partition *p, const char *root) {
                                 r = copy_tree_at(
                                                 sfd, ".",
                                                 pfd, fn,
-                                                UID_INVALID, GID_INVALID,
-                                                COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS);
+                                                getuid(), getgid(),
+                                                COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS,
+                                                denylist);
                         } else
                                 r = copy_tree_at(
                                                 sfd, ".",
                                                 tfd, ".",
-                                                UID_INVALID, GID_INVALID,
-                                                COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS);
+                                                getuid(), getgid(),
+                                                COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS,
+                                                denylist);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
+                                return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m",
+                                                       strempty(arg_root), *source, strempty(root), *target);
                 } else {
                         _cleanup_free_ char *dn = NULL, *fn = NULL;
 
@@ -3310,7 +3756,7 @@ static int do_copy_files(Partition *p, const char *root) {
                         if (tfd < 0)
                                 return log_error_errno(errno, "Failed to create target file '%s': %m", *target);
 
-                        r = copy_bytes(sfd, tfd, UINT64_MAX, COPY_REFLINK|COPY_SIGINT);
+                        r = copy_bytes(sfd, tfd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_SIGINT);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
 
@@ -3331,7 +3777,7 @@ static int do_make_directories(Partition *p, const char *root) {
 
         STRV_FOREACH(d, p->make_directories) {
 
-                r = mkdir_p_root(root, *d, UID_INVALID, GID_INVALID, 0755);
+                r = mkdir_p_root(root, *d, getuid(), getgid(), 0755);
                 if (r < 0)
                         return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
         }
@@ -3339,38 +3785,14 @@ static int do_make_directories(Partition *p, const char *root) {
         return 0;
 }
 
-static int partition_populate_directory(Partition *p, char **ret_root, char **ret_tmp_root) {
+static int partition_populate_directory(Partition *p, const Set *denylist, char **ret) {
         _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
         int r;
 
-        assert(ret_root);
-        assert(ret_tmp_root);
-
-        /* When generating read-only filesystems, we need the source tree to be available when we generate
-         * the read-only filesystem. Because we might have multiple source trees, we build a temporary source
-         * tree beforehand where we merge all our inputs. We then use this merged source tree to create the
-         * read-only filesystem. */
-
-        if (!fstype_is_ro(p->format)) {
-                *ret_root = NULL;
-                *ret_tmp_root = NULL;
-                return 0;
-        }
-
-        /* If we only have a single directory that's meant to become the root directory of the filesystem,
-         * we can shortcut this function and just use that directory as the root directory instead. If we
-         * allocate a temporary directory, it's stored in "ret_tmp_root" to indicate it should be removed.
-         * Otherwise, we return the directory to use in "root" to indicate it should not be removed. */
-
-        if (strv_length(p->copy_files) == 2 && strv_length(p->make_directories) == 0 && streq(p->copy_files[1], "/")) {
-                _cleanup_free_ char *s = NULL;
-
-                r = chase_symlinks(p->copy_files[0], arg_root, CHASE_PREFIX_ROOT, &s, NULL);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to resolve source '%s%s': %m", strempty(arg_root), p->copy_files[0]);
+        assert(ret);
 
-                *ret_root = TAKE_PTR(s);
-                *ret_tmp_root = NULL;
+        if ((strv_isempty(p->copy_files) && strv_isempty(p->make_directories))) {
+                *ret = NULL;
                 return 0;
         }
 
@@ -3378,7 +3800,14 @@ static int partition_populate_directory(Partition *p, char **ret_root, char **re
         if (r < 0)
                 return log_error_errno(r, "Failed to create temporary directory: %m");
 
-        r = do_copy_files(p, root);
+        if (chmod(root, 0755) < 0)
+                return log_error_errno(errno, "Failed to change mode of temporary directory: %m");
+
+        /* Make sure everything is owned by the user running repart so that make_filesystem() can map the
+         * user running repart to "root" in a user namespace to have the files owned by root in the final
+         * image. */
+
+        r = do_copy_files(p, root, denylist);
         if (r < 0)
                 return r;
 
@@ -3386,24 +3815,33 @@ static int partition_populate_directory(Partition *p, char **ret_root, char **re
         if (r < 0)
                 return r;
 
-        *ret_root = NULL;
-        *ret_tmp_root = TAKE_PTR(root);
+        *ret = TAKE_PTR(root);
         return 0;
 }
 
-static int partition_populate_filesystem(Partition *p, const char *node) {
+static int partition_populate_filesystem(Partition *p, const char *node, const Set *denylist) {
+        _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+        struct stat st;
         int r;
 
         assert(p);
         assert(node);
 
-        if (fstype_is_ro(p->format))
-                return 0;
-
         if (strv_isempty(p->copy_files) && strv_isempty(p->make_directories))
                 return 0;
 
-        log_info("Populating partition %" PRIu64 " with files.", p->partno);
+        if (stat(node, &st) < 0)
+                return log_error_errno(errno, "Failed to stat %s: %m", node);
+
+        if (!S_ISBLK(st.st_mode)) {
+                r = loop_device_make_by_path(node, O_RDWR, 0, LOCK_EX, &d);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to make loopback device of %s: %m", node);
+
+                node = d->node;
+        }
+
+        log_info("Populating %s filesystem with files.", p->format);
 
         /* We copy in a child process, since we have to mount the fs for that, and we don't want that fs to
          * appear in the host namespace. Hence we fork a child that has its own file system namespace and
@@ -3425,7 +3863,7 @@ static int partition_populate_filesystem(Partition *p, const char *node) {
                 if (mount_nofollow_verbose(LOG_ERR, node, fs, p->format, MS_NOATIME|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL) < 0)
                         _exit(EXIT_FAILURE);
 
-                if (do_copy_files(p, fs) < 0)
+                if (do_copy_files(p, fs, denylist) < 0)
                         _exit(EXIT_FAILURE);
 
                 if (do_make_directories(p, fs) < 0)
@@ -3440,243 +3878,151 @@ static int partition_populate_filesystem(Partition *p, const char *node) {
                 _exit(EXIT_SUCCESS);
         }
 
-        log_info("Successfully populated partition %" PRIu64 " with files.", p->partno);
+        log_info("Successfully populated %s filesystem with files.", p->format);
         return 0;
 }
 
-static int context_mkfs(Context *context) {
-        int fd = -1, r;
+static int make_copy_files_denylist(Context *context, Set **ret) {
+        _cleanup_set_free_ Set *denylist = NULL;
+        int r;
 
         assert(context);
-
-        /* Make a file system */
+        assert(ret);
 
         LIST_FOREACH(partitions, p, context->partitions) {
-                _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
-                _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
-                _cleanup_(rm_rf_physical_and_freep) char *tmp_root = NULL;
-                _cleanup_free_ char *encrypted = NULL, *root = NULL;
-                _cleanup_close_ int encrypted_dev_fd = -1;
-                const char *fsdev;
-                sd_id128_t fs_uuid;
-
-                if (p->dropped)
+                const char *sources = gpt_partition_type_mountpoint_nulstr(p->type);
+                if (!sources)
                         continue;
 
-                if (PARTITION_EXISTS(p)) /* Never format existing partitions */
-                        continue;
-
-                if (!p->format)
-                        continue;
-
-                assert(p->offset != UINT64_MAX);
-                assert(p->new_size != UINT64_MAX);
-
-                if (fd < 0)
-                        assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
-
-                /* Loopback block devices are not only useful to turn regular files into block devices, but
-                 * also to cut out sections of block devices into new block devices. */
-
-                r = loop_device_make(fd, O_RDWR, p->offset, p->new_size, 0, 0, LOCK_EX, &d);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno);
+                NULSTR_FOREACH(s, sources) {
+                        _cleanup_free_ char *d = NULL;
+                        struct stat st;
 
-                if (p->encrypt != ENCRYPT_OFF) {
-                        r = partition_encrypt(context, p, d->node, &cd, &encrypted, &encrypted_dev_fd);
+                        r = chase_symlinks_and_stat(s, arg_root, CHASE_PREFIX_ROOT, NULL, &st, NULL);
+                        if (r == -ENOENT)
+                                continue;
                         if (r < 0)
-                                return log_error_errno(r, "Failed to encrypt device: %m");
-
-                        if (flock(encrypted_dev_fd, LOCK_EX) < 0)
-                                return log_error_errno(errno, "Failed to lock LUKS device: %m");
-
-                        fsdev = encrypted;
-                } else
-                        fsdev = d->node;
-
-                log_info("Formatting future partition %" PRIu64 ".", p->partno);
-
-                /* Calculate the UUID for the file system as HMAC-SHA256 of the string "file-system-uuid",
-                 * keyed off the partition UUID. */
-                r = derive_uuid(p->new_uuid, "file-system-uuid", &fs_uuid);
-                if (r < 0)
-                        return r;
-
-                /* Ideally, we populate filesystems using our own code after creating the filesystem to
-                 * ensure consistent handling of chattrs, xattrs and other similar things. However, when
-                 * using read-only filesystems such as squashfs, we can't populate after creating the
-                 * filesystem because it's read-only, so instead we create a temporary root to use as the
-                 * source tree when generating the read-only filesystem. */
-                r = partition_populate_directory(p, &root, &tmp_root);
-                if (r < 0)
-                        return r;
-
-                r = make_filesystem(fsdev, p->format, strempty(p->new_label), root ?: tmp_root, fs_uuid, arg_discard);
-                if (r < 0) {
-                        encrypted_dev_fd = safe_close(encrypted_dev_fd);
-                        (void) deactivate_luks(cd, encrypted);
-                        return r;
-                }
-
-                log_info("Successfully formatted future partition %" PRIu64 ".", p->partno);
-
-                /* The file system is now created, no need to delay udev further */
-                if (p->encrypt != ENCRYPT_OFF)
-                        if (flock(encrypted_dev_fd, LOCK_UN) < 0)
-                                return log_error_errno(errno, "Failed to unlock LUKS device: %m");
-
-                /* Now, we can populate all the other filesystems that aren't read-only. */
-                r = partition_populate_filesystem(p, fsdev);
-                if (r < 0) {
-                        encrypted_dev_fd = safe_close(encrypted_dev_fd);
-                        (void) deactivate_luks(cd, encrypted);
-                        return r;
-                }
+                                return log_error_errno(r, "Failed to stat source file '%s%s': %m",
+                                                       strempty(arg_root), s);
 
-                /* Note that we always sync explicitly here, since mkfs.fat doesn't do that on its own, and
-                 * if we don't sync before detaching a block device the in-flight sectors possibly won't hit
-                 * the disk. */
-
-                if (p->encrypt != ENCRYPT_OFF) {
-                        if (fsync(encrypted_dev_fd) < 0)
-                                return log_error_errno(errno, "Failed to synchronize LUKS volume: %m");
-                        encrypted_dev_fd = safe_close(encrypted_dev_fd);
+                        if (set_contains(denylist, &st))
+                                continue;
 
-                        r = deactivate_luks(cd, encrypted);
-                        if (r < 0)
-                                return r;
+                        d = memdup(&st, sizeof(st));
+                        if (!d)
+                                return log_oom();
+                        if (set_ensure_put(&denylist, &inode_hash_ops, d) < 0)
+                                return log_oom();
 
-                        sym_crypt_free(cd);
-                        cd = NULL;
+                        TAKE_PTR(d);
                 }
-
-                r = loop_device_sync(d);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to sync loopback device: %m");
         }
 
+        *ret = TAKE_PTR(denylist);
         return 0;
 }
 
-static int do_verity_format(
-                LoopDevice *data_device,
-                LoopDevice *hash_device,
-                uint64_t sector_size,
-                uint8_t **ret_roothash,
-                size_t *ret_roothash_size) {
-
-#if HAVE_LIBCRYPTSETUP
-        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
-        _cleanup_free_ uint8_t *rh = NULL;
-        size_t rhs;
+static int context_mkfs(Context *context) {
+        _cleanup_set_free_ Set *denylist = NULL;
         int r;
 
-        assert(data_device);
-        assert(hash_device);
-        assert(sector_size > 0);
-        assert(ret_roothash);
-        assert(ret_roothash_size);
+        assert(context);
 
-        r = dlopen_cryptsetup();
-        if (r < 0)
-                return log_error_errno(r, "libcryptsetup not found, cannot setup verity: %m");
+        /* Make a file system */
 
-        r = sym_crypt_init(&cd, hash_device->node);
+        r = make_copy_files_denylist(context, &denylist);
         if (r < 0)
-                return log_error_errno(r, "Failed to allocate libcryptsetup context: %m");
+                return r;
 
-        r = sym_crypt_format(
-                        cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0,
-                        &(struct crypt_params_verity){
-                                .data_device = data_device->node,
-                                .flags = CRYPT_VERITY_CREATE_HASH,
-                                .hash_name = "sha256",
-                                .hash_type = 1,
-                                .data_block_size = sector_size,
-                                .hash_block_size = sector_size,
-                                .salt_size = 32,
-                        });
-        if (r < 0)
-                return log_error_errno(r, "Failed to setup verity hash data: %m");
+        LIST_FOREACH(partitions, p, context->partitions) {
+                _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
+                _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
 
-        r = sym_crypt_get_volume_key_size(cd);
-        if (r < 0)
-                return log_error_errno(r, "Failed to determine verity root hash size: %m");
-        rhs = (size_t) r;
+                if (p->dropped)
+                        continue;
 
-        rh = malloc(rhs);
-        if (!rh)
-                return log_oom();
+                if (PARTITION_EXISTS(p)) /* Never format existing partitions */
+                        continue;
 
-        r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, (char *) rh, &rhs, NULL, 0);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get verity root hash: %m");
+                if (!p->format)
+                        continue;
 
-        *ret_roothash = TAKE_PTR(rh);
-        *ret_roothash_size = rhs;
+                /* Minimized partitions will use the copy blocks logic so let's make sure to skip those here. */
+                if (p->copy_blocks_fd >= 0)
+                        continue;
 
-        return 0;
-#else
-        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libcryptsetup is not supported, cannot setup verity hashes: %m");
-#endif
-}
+                if (partition_skip(p))
+                        continue;
 
-static int context_verity_hash(Context *context) {
-        int fd = -1, r;
+                assert(p->offset != UINT64_MAX);
+                assert(p->new_size != UINT64_MAX);
+                assert(p->new_size >= (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0));
+
+                /* If we're doing encryption, we make sure we keep free space at the end which is required
+                 * for cryptsetup's offline encryption. */
+                r = partition_target_prepare(context, p,
+                                             p->new_size - (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0),
+                                             /*need_path=*/ true,
+                                             &t);
+                if (r < 0)
+                        return r;
 
-        assert(context);
+                log_info("Formatting future partition %" PRIu64 ".", p->partno);
 
-        LIST_FOREACH(partitions, p, context->partitions) {
-                Partition *dp;
-                _cleanup_(loop_device_unrefp) LoopDevice *hash_device = NULL, *data_device = NULL;
-                _cleanup_free_ uint8_t *rh = NULL;
-                size_t rhs = 0; /* Initialize to work around for GCC false positive. */
+                /* We prefer (or are required in the case of read-only filesystems) to populate filesystems
+                 * directly via the corresponding mkfs binary if it supports a --rootdir (or equivalent)
+                 * option. To do that, we need to setup the final directory tree beforehand. */
 
-                if (p->dropped)
-                        continue;
+                if (mkfs_supports_root_option(p->format)) {
+                        r = partition_populate_directory(p, denylist, &root);
+                        if (r < 0)
+                                return r;
+                }
 
-                if (PARTITION_EXISTS(p)) /* Never format existing partitions */
-                        continue;
+                r = make_filesystem(partition_target_path(t), p->format, strempty(p->new_label), root,
+                                    p->fs_uuid, arg_discard);
+                if (r < 0)
+                        return r;
 
-                if (p->verity != VERITY_HASH)
-                        continue;
+                log_info("Successfully formatted future partition %" PRIu64 ".", p->partno);
 
-                assert_se(dp = p->siblings[VERITY_DATA]);
-                assert(!dp->dropped);
+                /* Now, we can populate all the other filesystems that we couldn't populate earlier. */
+                if (!mkfs_supports_root_option(p->format)) {
+                        r = partition_populate_filesystem(p, partition_target_path(t), denylist);
+                        if (r < 0)
+                                return r;
+                }
 
-                if (fd < 0)
-                        assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
+                if (p->encrypt != ENCRYPT_OFF) {
+                        r = partition_target_grow(t, p->new_size);
+                        if (r < 0)
+                                return r;
 
-                r = loop_device_make(fd, O_RDONLY, dp->offset, dp->new_size, 0, 0, LOCK_EX, &data_device);
-                if (r < 0)
-                        return log_error_errno(r,
-                                               "Failed to make loopback device of verity data partition %" PRIu64 ": %m",
-                                               p->partno);
+                        r = partition_encrypt(context, p, partition_target_path(t));
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to encrypt device: %m");
+                }
 
-                r = loop_device_make(fd, O_RDWR, p->offset, p->new_size, 0, 0, LOCK_EX, &hash_device);
-                if (r < 0)
-                        return log_error_errno(r,
-                                               "Failed to make loopback device of verity hash partition %" PRIu64 ": %m",
-                                               p->partno);
+                /* Note that we always sync explicitly here, since mkfs.fat doesn't do that on its own, and
+                 * if we don't sync before detaching a block device the in-flight sectors possibly won't hit
+                 * the disk. */
 
-                r = do_verity_format(data_device, hash_device, context->sector_size, &rh, &rhs);
+                r = partition_target_sync(context, p, t);
                 if (r < 0)
                         return r;
 
-                assert(rhs >= sizeof(sd_id128_t) * 2);
-
-                if (!dp->new_uuid_is_set) {
-                        memcpy_safe(dp->new_uuid.bytes, rh, sizeof(sd_id128_t));
-                        dp->new_uuid_is_set = true;
+                if (p->siblings[VERITY_HASH]) {
+                        r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
+                                                         partition_target_path(t));
+                        if (r < 0)
+                                return r;
                 }
 
-                if (!p->new_uuid_is_set) {
-                        memcpy_safe(p->new_uuid.bytes, rh + rhs - sizeof(sd_id128_t), sizeof(sd_id128_t));
-                        p->new_uuid_is_set = true;
+                if (p->siblings[VERITY_SIG]) {
+                        r = partition_format_verity_sig(context, p->siblings[VERITY_SIG]);
+                        if (r < 0)
+                                return r;
                 }
-
-                p->roothash = TAKE_PTR(rh);
-                p->roothash_size = rhs;
         }
 
         return 0;
@@ -3736,127 +4082,6 @@ static int parse_private_key(const char *key, size_t key_size, EVP_PKEY **ret) {
 #endif
 }
 
-static int sign_verity_roothash(
-                const uint8_t *roothash,
-                size_t roothash_size,
-                uint8_t **ret_signature,
-                size_t *ret_signature_size) {
-
-#if HAVE_OPENSSL
-        _cleanup_(BIO_freep) BIO *rb = NULL;
-        _cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
-        _cleanup_free_ char *hex = NULL;
-        _cleanup_free_ uint8_t *sig = NULL;
-        int sigsz;
-
-        assert(roothash);
-        assert(roothash_size > 0);
-        assert(ret_signature);
-        assert(ret_signature_size);
-
-        hex = hexmem(roothash, roothash_size);
-        if (!hex)
-                return log_oom();
-
-        rb = BIO_new_mem_buf(hex, -1);
-        if (!rb)
-                return log_oom();
-
-        p7 = PKCS7_sign(arg_certificate, arg_private_key, NULL, rb, PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY);
-        if (!p7)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to calculate PKCS7 signature: %s",
-                                       ERR_error_string(ERR_get_error(), NULL));
-
-        sigsz = i2d_PKCS7(p7, &sig);
-        if (sigsz < 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert PKCS7 signature to DER: %s",
-                                       ERR_error_string(ERR_get_error(), NULL));
-
-        *ret_signature = TAKE_PTR(sig);
-        *ret_signature_size = sigsz;
-
-        return 0;
-#else
-        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot setup verity signature: %m");
-#endif
-}
-
-static int context_verity_sig(Context *context) {
-        int fd = -1, r;
-
-        assert(context);
-
-        LIST_FOREACH(partitions, p, context->partitions) {
-                _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
-                _cleanup_free_ uint8_t *sig = NULL;
-                _cleanup_free_ char *text = NULL;
-                Partition *hp;
-                uint8_t fp[X509_FINGERPRINT_SIZE];
-                size_t sigsz = 0, padsz; /* avoid false maybe-uninitialized warning */
-
-                if (p->dropped)
-                        continue;
-
-                if (PARTITION_EXISTS(p))
-                        continue;
-
-                if (p->verity != VERITY_SIG)
-                        continue;
-
-                assert_se(hp = p->siblings[VERITY_HASH]);
-                assert(!hp->dropped);
-
-                assert(arg_certificate);
-
-                if (fd < 0)
-                        assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
-
-                r = sign_verity_roothash(hp->roothash, hp->roothash_size, &sig, &sigsz);
-                if (r < 0)
-                        return r;
-
-                r = x509_fingerprint(arg_certificate, fp);
-                if (r < 0)
-                        return log_error_errno(r, "Unable to calculate X509 certificate fingerprint: %m");
-
-                r = json_build(&v,
-                               JSON_BUILD_OBJECT(
-                                        JSON_BUILD_PAIR("rootHash", JSON_BUILD_HEX(hp->roothash, hp->roothash_size)),
-                                        JSON_BUILD_PAIR(
-                                                "certificateFingerprint",
-                                                JSON_BUILD_HEX(fp, sizeof(fp))
-                                        ),
-                                        JSON_BUILD_PAIR("signature", JSON_BUILD_BASE64(sig, sigsz))
-                               )
-                );
-                if (r < 0)
-                        return log_error_errno(r, "Failed to build JSON object: %m");
-
-                r = json_variant_format(v, 0, &text);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to format JSON object: %m");
-
-                padsz = round_up_size(strlen(text), 4096);
-                assert_se(padsz <= p->new_size);
-
-                r = strgrowpad0(&text, padsz);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to pad string to %s", FORMAT_BYTES(padsz));
-
-                if (lseek(fd, p->offset, SEEK_SET) == (off_t) -1)
-                        return log_error_errno(errno, "Failed to seek to partition offset: %m");
-
-                r = loop_write(fd, text, padsz, /*do_poll=*/ false);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to write verity signature to partition: %m");
-
-                if (fsync(fd) < 0)
-                        return log_error_errno(errno, "Failed to synchronize verity signature JSON: %m");
-        }
-
-        return 0;
-}
-
 static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *ret) {
         struct {
                 sd_id128_t type_uuid;
@@ -3897,13 +4122,13 @@ static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *re
                 if (p == q)
                         break;
 
-                if (!sd_id128_equal(p->type_uuid, q->type_uuid))
+                if (!sd_id128_equal(p->type.uuid, q->type.uuid))
                         continue;
 
                 k++;
         }
 
-        plaintext.type_uuid = p->type_uuid;
+        plaintext.type_uuid = p->type.uuid;
         plaintext.counter = htole64(k);
 
         hmac_sha256(context->seed.bytes, sizeof(context->seed.bytes),
@@ -3944,7 +4169,7 @@ static int partition_acquire_label(Context *context, Partition *p, char **ret) {
         assert(p);
         assert(ret);
 
-        prefix = gpt_partition_type_uuid_to_string(p->type_uuid);
+        prefix = gpt_partition_type_uuid_to_string(p->type.uuid);
         if (!prefix)
                 prefix = "linux";
 
@@ -4011,6 +4236,12 @@ static int context_acquire_partition_uuids_and_labels(Context *context) {
                         p->new_uuid_is_set = true;
                 }
 
+                /* Calculate the UUID for the file system as HMAC-SHA256 of the string "file-system-uuid",
+                 * keyed off the partition UUID. */
+                r = derive_uuid(p->new_uuid, "file-system-uuid", &p->fs_uuid);
+                if (r < 0)
+                        return r;
+
                 if (!isempty(p->current_label)) {
                         /* never change initialized labels */
                         r = free_and_strdup_warn(&p->new_label, p->current_label);
@@ -4054,35 +4285,35 @@ static uint64_t partition_merge_flags(Partition *p) {
         f = p->gpt_flags;
 
         if (p->no_auto >= 0) {
-                if (gpt_partition_type_knows_no_auto(p->type_uuid))
+                if (gpt_partition_type_knows_no_auto(p->type))
                         SET_FLAG(f, SD_GPT_FLAG_NO_AUTO, p->no_auto);
                 else {
                         char buffer[SD_ID128_UUID_STRING_MAX];
                         log_warning("Configured NoAuto=%s for partition type '%s' that doesn't support it, ignoring.",
                                     yes_no(p->no_auto),
-                                    gpt_partition_type_uuid_to_string_harder(p->type_uuid, buffer));
+                                    gpt_partition_type_uuid_to_string_harder(p->type.uuid, buffer));
                 }
         }
 
         if (p->read_only >= 0) {
-                if (gpt_partition_type_knows_read_only(p->type_uuid))
+                if (gpt_partition_type_knows_read_only(p->type))
                         SET_FLAG(f, SD_GPT_FLAG_READ_ONLY, p->read_only);
                 else {
                         char buffer[SD_ID128_UUID_STRING_MAX];
                         log_warning("Configured ReadOnly=%s for partition type '%s' that doesn't support it, ignoring.",
                                     yes_no(p->read_only),
-                                    gpt_partition_type_uuid_to_string_harder(p->type_uuid, buffer));
+                                    gpt_partition_type_uuid_to_string_harder(p->type.uuid, buffer));
                 }
         }
 
         if (p->growfs >= 0) {
-                if (gpt_partition_type_knows_growfs(p->type_uuid))
+                if (gpt_partition_type_knows_growfs(p->type))
                         SET_FLAG(f, SD_GPT_FLAG_GROWFS, p->growfs);
                 else {
                         char buffer[SD_ID128_UUID_STRING_MAX];
                         log_warning("Configured GrowFileSystem=%s for partition type '%s' that doesn't support it, ignoring.",
                                     yes_no(p->growfs),
-                                    gpt_partition_type_uuid_to_string_harder(p->type_uuid, buffer));
+                                    gpt_partition_type_uuid_to_string_harder(p->type.uuid, buffer));
                 }
         }
 
@@ -4098,6 +4329,9 @@ static int context_mangle_partitions(Context *context) {
                 if (p->dropped)
                         continue;
 
+                if (partition_skip(p))
+                        continue;
+
                 assert(p->new_size != UINT64_MAX);
                 assert(p->offset != UINT64_MAX);
                 assert(p->partno != UINT64_MAX);
@@ -4161,7 +4395,7 @@ static int context_mangle_partitions(Context *context) {
                         if (!t)
                                 return log_oom();
 
-                        r = fdisk_parttype_set_typestr(t, SD_ID128_TO_UUID_STRING(p->type_uuid));
+                        r = fdisk_parttype_set_typestr(t, SD_ID128_TO_UUID_STRING(p->type.uuid));
                         if (r < 0)
                                 return log_error_errno(r, "Failed to initialize partition type: %m");
 
@@ -4220,8 +4454,8 @@ static int split_name_printf(Partition *p) {
         assert(p);
 
         const Specifier table[] = {
-                { 't', specifier_string, GPT_PARTITION_TYPE_UUID_TO_STRING_HARDER(p->type_uuid) },
-                { 'T', specifier_id128,  &p->type_uuid                                          },
+                { 't', specifier_string, GPT_PARTITION_TYPE_UUID_TO_STRING_HARDER(p->type.uuid) },
+                { 'T', specifier_id128,  &p->type.uuid                                          },
                 { 'U', specifier_id128,  &p->new_uuid                                           },
                 { 'n', specifier_uint64, &p->partno                                             },
 
@@ -4338,6 +4572,9 @@ static int context_split(Context *context) {
                 if (!p->split_name_resolved)
                         continue;
 
+                if (partition_skip(p))
+                        continue;
+
                 fname = strjoin(base, ".", p->split_name_resolved, ext);
                 if (!fname)
                         return log_oom();
@@ -4352,7 +4589,7 @@ static int context_split(Context *context) {
                 if (lseek(fd, p->offset, SEEK_SET) < 0)
                         return log_error_errno(errno, "Failed to seek to partition offset: %m");
 
-                r = copy_bytes_full(fd, fdt, p->new_size, COPY_REFLINK|COPY_HOLES, NULL, NULL, NULL, NULL);
+                r = copy_bytes(fd, fdt, p->new_size, COPY_REFLINK|COPY_HOLES);
                 if (r < 0)
                         return log_error_errno(r, "Failed to copy to split partition %s: %m", fname);
         }
@@ -4416,14 +4653,6 @@ static int context_write_partition_table(
         if (r < 0)
                 return r;
 
-        r = context_verity_hash(context);
-        if (r < 0)
-                return r;
-
-        r = context_verity_sig(context);
-        if (r < 0)
-                return r;
-
         r = context_mangle_partitions(context);
         if (r < 0)
                 return r;
@@ -4550,7 +4779,7 @@ static int context_can_factory_reset(Context *context) {
 
 static int resolve_copy_blocks_auto_candidate(
                 dev_t partition_devno,
-                sd_id128_t partition_type_uuid,
+                GptPartitionType partition_type,
                 dev_t restrict_devno,
                 sd_id128_t *ret_uuid) {
 
@@ -4642,10 +4871,10 @@ static int resolve_copy_blocks_auto_candidate(
                 return false;
         }
 
-        if (!sd_id128_equal(pt_parsed, partition_type_uuid)) {
+        if (!sd_id128_equal(pt_parsed, partition_type.uuid)) {
                 log_debug("Partition %u:%u has non-matching partition type " SD_ID128_FORMAT_STR " (needed: " SD_ID128_FORMAT_STR "), ignoring.",
                           major(partition_devno), minor(partition_devno),
-                          SD_ID128_FORMAT_VAL(pt_parsed), SD_ID128_FORMAT_VAL(partition_type_uuid));
+                          SD_ID128_FORMAT_VAL(pt_parsed), SD_ID128_FORMAT_VAL(partition_type.uuid));
                 return false;
         }
 
@@ -4702,7 +4931,7 @@ static int find_backing_devno(
 }
 
 static int resolve_copy_blocks_auto(
-                sd_id128_t type_uuid,
+                GptPartitionType type,
                 const char *root,
                 dev_t restrict_devno,
                 dev_t *ret_devno,
@@ -4732,30 +4961,30 @@ static int resolve_copy_blocks_auto(
          * partitions in the host, using the appropriate directory as key and ensuring that the partition
          * type matches. */
 
-        if (gpt_partition_type_is_root(type_uuid))
+        if (type.designator == PARTITION_ROOT)
                 try1 = "/";
-        else if (gpt_partition_type_is_usr(type_uuid))
+        else if (type.designator == PARTITION_USR)
                 try1 = "/usr/";
-        else if (gpt_partition_type_is_root_verity(type_uuid))
+        else if (type.designator == PARTITION_ROOT_VERITY)
                 try1 = "/";
-        else if (gpt_partition_type_is_usr_verity(type_uuid))
+        else if (type.designator == PARTITION_USR_VERITY)
                 try1 = "/usr/";
-        else if (sd_id128_equal(type_uuid, SD_GPT_ESP)) {
+        else if (type.designator == PARTITION_ESP) {
                 try1 = "/efi/";
                 try2 = "/boot/";
-        } else if (sd_id128_equal(type_uuid, SD_GPT_XBOOTLDR))
+        } else if (type.designator == PARTITION_XBOOTLDR)
                 try1 = "/boot/";
         else
                 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                        "Partition type " SD_ID128_FORMAT_STR " not supported from automatic source block device discovery.",
-                                       SD_ID128_FORMAT_VAL(type_uuid));
+                                       SD_ID128_FORMAT_VAL(type.uuid));
 
         r = find_backing_devno(try1, root, &devno);
         if (r == -ENOENT && try2)
                 r = find_backing_devno(try2, root, &devno);
         if (r < 0)
                 return log_error_errno(r, "Failed to resolve automatic CopyBlocks= path for partition type " SD_ID128_FORMAT_STR ", sorry: %m",
-                                       SD_ID128_FORMAT_VAL(type_uuid));
+                                       SD_ID128_FORMAT_VAL(type.uuid));
 
         xsprintf_sys_block_path(p, "/slaves", devno);
         d = opendir(p);
@@ -4797,7 +5026,7 @@ static int resolve_copy_blocks_auto(
                                 continue;
                         }
 
-                        r = resolve_copy_blocks_auto_candidate(sl, type_uuid, restrict_devno, &u);
+                        r = resolve_copy_blocks_auto_candidate(sl, type, restrict_devno, &u);
                         if (r < 0)
                                 return r;
                         if (r > 0) {
@@ -4813,7 +5042,7 @@ static int resolve_copy_blocks_auto(
         } else if (errno != ENOENT)
                 return log_error_errno(errno, "Failed open %s: %m", p);
         else {
-                r = resolve_copy_blocks_auto_candidate(devno, type_uuid, restrict_devno, &found_uuid);
+                r = resolve_copy_blocks_auto_candidate(devno, type, restrict_devno, &found_uuid);
                 if (r < 0)
                         return r;
                 if (r > 0)
@@ -4835,7 +5064,6 @@ static int resolve_copy_blocks_auto(
 
 static int context_open_copy_block_paths(
                 Context *context,
-                const char *root,
                 dev_t restrict_devno) {
 
         int r;
@@ -4857,7 +5085,7 @@ static int context_open_copy_block_paths(
 
                 if (p->copy_blocks_path) {
 
-                        source_fd = chase_symlinks_and_open(p->copy_blocks_path, root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NONBLOCK, &opened);
+                        source_fd = chase_symlinks_and_open(p->copy_blocks_path, p->copy_blocks_root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NONBLOCK, &opened);
                         if (source_fd < 0)
                                 return log_error_errno(source_fd, "Failed to open '%s': %m", p->copy_blocks_path);
 
@@ -4871,7 +5099,7 @@ static int context_open_copy_block_paths(
                 } else if (p->copy_blocks_auto) {
                         dev_t devno;
 
-                        r = resolve_copy_blocks_auto(p->type_uuid, root, restrict_devno, &devno, &uuid);
+                        r = resolve_copy_blocks_auto(p->type, p->copy_blocks_root, restrict_devno, &devno, &uuid);
                         if (r < 0)
                                 return r;
 
@@ -4940,6 +5168,203 @@ static int context_open_copy_block_paths(
         return 0;
 }
 
+static int fd_apparent_size(int fd, uint64_t *ret) {
+        off_t initial = 0;
+        uint64_t size = 0;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        initial = lseek(fd, 0, SEEK_CUR);
+        if (initial < 0)
+                return log_error_errno(errno, "Failed to get file offset: %m");
+
+        for (off_t off = 0;;) {
+                off_t r;
+
+                r = lseek(fd, off, SEEK_DATA);
+                if (r < 0 && errno == ENXIO)
+                        /* If errno == ENXIO, that means we've reached the final hole of the file and
+                         * that hole isn't followed by more data. */
+                        break;
+                if (r < 0)
+                        return log_error_errno(errno, "Failed to seek data in file from offset %"PRIi64": %m", off);
+
+                off = r; /* Set the offset to the start of the data segment. */
+
+                /* After copying a potential hole, find the end of the data segment by looking for
+                 * the next hole. If we get ENXIO, we're at EOF. */
+                r = lseek(fd, off, SEEK_HOLE);
+                if (r < 0) {
+                        if (errno == ENXIO)
+                                break;
+                        return log_error_errno(errno, "Failed to seek hole in file from offset %"PRIi64": %m", off);
+                }
+
+                size += r - off;
+                off = r;
+        }
+
+        if (lseek(fd, initial, SEEK_SET) < 0)
+                return log_error_errno(errno, "Failed to reset file offset: %m");
+
+        *ret = size;
+
+        return 0;
+}
+
+static int context_minimize(Context *context) {
+        _cleanup_set_free_ Set *denylist = NULL;
+        const char *vt;
+        int r;
+
+        assert(context);
+
+        r = make_copy_files_denylist(context, &denylist);
+        if (r < 0)
+                return r;
+
+        r = var_tmp_dir(&vt);
+        if (r < 0)
+                return log_error_errno(r, "Could not determine temporary directory: %m");
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+                _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
+                _cleanup_(unlink_and_freep) char *temp = NULL;
+                _cleanup_close_ int fd = -1;
+                sd_id128_t fs_uuid;
+                uint64_t fsz;
+
+                if (p->dropped)
+                        continue;
+
+                if (PARTITION_EXISTS(p)) /* Never format existing partitions */
+                        continue;
+
+                if (!p->format)
+                        continue;
+
+                if (!p->minimize)
+                        continue;
+
+                assert(!p->copy_blocks_path);
+
+                r = tempfn_random_child(vt, "repart", &temp);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to generate temporary file path: %m");
+
+                if (fstype_is_ro(p->format))
+                        fs_uuid = p->fs_uuid;
+                else {
+                        fd = open(temp, O_CREAT|O_EXCL|O_CLOEXEC|O_RDWR|O_NOCTTY, 0600);
+                        if (fd < 0)
+                                return log_error_errno(errno, "Failed to open temporary file %s: %m", temp);
+
+                        /* This may seem huge but it will be created sparse so it doesn't take up any space
+                        * on disk until written to. */
+                        if (ftruncate(fd, 1024ULL * 1024ULL * 1024ULL * 1024ULL) < 0)
+                                return log_error_errno(errno, "Failed to truncate temporary file to %s: %m",
+                                                       FORMAT_BYTES(1024ULL * 1024ULL * 1024ULL * 1024ULL));
+
+                        /* We're going to populate this filesystem twice so use a random UUID the first time
+                         * to avoid UUID conflicts. */
+                        r = sd_id128_randomize(&fs_uuid);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (mkfs_supports_root_option(p->format)) {
+                        r = partition_populate_directory(p, denylist, &root);
+                        if (r < 0)
+                                return r;
+                }
+
+                r = make_filesystem(temp, p->format, strempty(p->new_label), root, fs_uuid, arg_discard);
+                if (r < 0)
+                        return r;
+
+                /* Read-only filesystems are minimal from the first try because they create and size the
+                 * loopback file for us. */
+                if (fstype_is_ro(p->format)) {
+                        p->copy_blocks_path = TAKE_PTR(temp);
+                        continue;
+                }
+
+                if (!mkfs_supports_root_option(p->format)) {
+                        r = partition_populate_filesystem(p, temp, denylist);
+                        if (r < 0)
+                                return r;
+                }
+
+                /* Other filesystems need to be provided with a pre-sized loopback file and will adapt to
+                 * fully occupy it. Because we gave the filesystem a 1T sparse file, we need to shrink the
+                 * filesystem down to a reasonable size again to fit it in the disk image. While there are
+                 * some filesystems that support shrinking, it doesn't always work properly (e.g. shrinking
+                 * btrfs gives us a 2.0G filesystem regardless of what we put in it). Instead, let's populate
+                 * the filesystem again, but this time, instead of providing the filesystem with a 1T sparse
+                 * loopback file, let's size the loopback file based on the actual data used by the
+                 * filesystem in the sparse file after the first attempt. This should be a good guess of the
+                 * minimal amount of space needed in the filesystem to fit all the required data.
+                 */
+                r = fd_apparent_size(fd, &fsz);
+                if (r < 0)
+                        return r;
+
+                /* Massage the size a bit because just going by actual data used in the sparse file isn't
+                 * fool-proof. */
+                fsz = round_up_size(fsz + (fsz / 2), context->grain_size);
+                if (minimal_size_by_fs_name(p->format) != UINT64_MAX)
+                        fsz = MAX(minimal_size_by_fs_name(p->format), fsz);
+
+                /* Erase the previous filesystem first. */
+                if (ftruncate(fd, 0))
+                        return log_error_errno(errno, "Failed to erase temporary file: %m");
+
+                if (ftruncate(fd, fsz))
+                        return log_error_errno(errno, "Failed to truncate temporary file to %s: %m", FORMAT_BYTES(fsz));
+
+                r = make_filesystem(temp, p->format, strempty(p->new_label), root, p->fs_uuid, arg_discard);
+                if (r < 0)
+                        return r;
+
+                if (!mkfs_supports_root_option(p->format)) {
+                        r = partition_populate_filesystem(p, temp, denylist);
+                        if (r < 0)
+                                return r;
+                }
+
+                p->copy_blocks_path = TAKE_PTR(temp);
+        }
+
+        return 0;
+}
+
+static int parse_filter_partitions(const char *p) {
+        int r;
+
+        for (;;) {
+                _cleanup_free_ char *name = NULL;
+                GptPartitionType type;
+
+                r = extract_first_word(&p, &name, ",", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS);
+                if (r == 0)
+                        break;
+                if (r < 0)
+                        return log_error_errno(r, "Failed to extract partition designator: %s", optarg);
+
+                r = gpt_partition_type_from_string(name, &type);
+                if (r < 0)
+                        return log_error_errno(r, "'%s' is not a valid partition designator", name);
+
+                if (!GREEDY_REALLOC(arg_filter_partitions, arg_filter_partitions_size + 1))
+                        return log_oom();
+
+                arg_filter_partitions[arg_filter_partitions_size++] = type.uuid;
+        }
+
+        return 0;
+}
+
 static int help(void) {
         _cleanup_free_ char *link = NULL;
         int r;
@@ -4982,6 +5407,10 @@ static int help(void) {
                "     --json=pretty|short|off\n"
                "                          Generate JSON output\n"
                "     --split=BOOL         Whether to generate split artifacts\n"
+               "     --include-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
+               "                          Only operate on partitions of the specified types\n"
+               "     --exclude-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
+               "                          Don't operate on partitions of the specified types\n"
                "\nSee the %s for details.\n",
                program_invocation_short_name,
                ansi_highlight(),
@@ -5017,6 +5446,8 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_TPM2_PUBLIC_KEY,
                 ARG_TPM2_PUBLIC_KEY_PCRS,
                 ARG_SPLIT,
+                ARG_INCLUDE_PARTITIONS,
+                ARG_EXCLUDE_PARTITIONS,
         };
 
         static const struct option options[] = {
@@ -5044,6 +5475,8 @@ static int parse_argv(int argc, char *argv[]) {
                 { "tpm2-public-key",      required_argument, NULL, ARG_TPM2_PUBLIC_KEY      },
                 { "tpm2-public-key-pcrs", required_argument, NULL, ARG_TPM2_PUBLIC_KEY_PCRS },
                 { "split",                required_argument, NULL, ARG_SPLIT                },
+                { "include-partitions",   required_argument, NULL, ARG_INCLUDE_PARTITIONS   },
+                { "exclude-partitions",   required_argument, NULL, ARG_EXCLUDE_PARTITIONS   },
                 {}
         };
 
@@ -5298,6 +5731,32 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_split = r;
                         break;
 
+                case ARG_INCLUDE_PARTITIONS:
+                        if (arg_filter_partitions_type == FILTER_PARTITIONS_EXCLUDE)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Combination of --include-partitions= and --exclude-partitions= is invalid.");
+
+                        r = parse_filter_partitions(optarg);
+                        if (r < 0)
+                                return r;
+
+                        arg_filter_partitions_type = FILTER_PARTITIONS_INCLUDE;
+
+                        break;
+
+                case ARG_EXCLUDE_PARTITIONS:
+                        if (arg_filter_partitions_type == FILTER_PARTITIONS_INCLUDE)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Combination of --include-partitions= and --exclude-partitions= is invalid.");
+
+                        r = parse_filter_partitions(optarg);
+                        if (r < 0)
+                                return r;
+
+                        arg_filter_partitions_type = FILTER_PARTITIONS_EXCLUDE;
+
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -5900,10 +6359,18 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
+        /* Make sure each partition has a unique UUID and unique label */
+        r = context_acquire_partition_uuids_and_labels(context);
+        if (r < 0)
+                return r;
+
+        r = context_minimize(context);
+        if (r < 0)
+                return r;
+
         /* Open all files to copy blocks from now, since we want to take their size into consideration */
         r = context_open_copy_block_paths(
                         context,
-                        arg_root,
                         loop_device ? loop_device->devno :         /* if --image= is specified, only allow partitions on the loopback device */
                                       arg_root && !arg_image ? 0 : /* if --root= is specified, don't accept any block device */
                                       (dev_t) -1);                 /* if neither is specified, make no restrictions */
@@ -5956,11 +6423,6 @@ static int run(int argc, char *argv[]) {
         /* Now calculate where each new partition gets placed */
         context_place_partitions(context);
 
-        /* Make sure each partition has a unique UUID and unique label */
-        r = context_acquire_partition_uuids_and_labels(context);
-        if (r < 0)
-                return r;
-
         (void) context_dump(context, node, /*late=*/ false);
 
         r = context_write_partition_table(context, node, from_scratch);
index 04c2a2976238999c6cc3e6232b99e4f9bcb76879..020840e0dfdaf84173133dfae98375ec1a8b61f0 100644 (file)
 
 #include "alloc-util.h"
 #include "build.h"
+#include "chase-symlinks.h"
+#include "efi-loader.h"
 #include "fd-util.h"
+#include "find-esp.h"
 #include "fs-util.h"
 #include "io-util.h"
 #include "log.h"
@@ -26,6 +29,7 @@
 #include "mkdir.h"
 #include "parse-argument.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "pretty-print.h"
 #include "random-util.h"
 #include "string-table.h"
@@ -185,7 +189,7 @@ static int load_seed_file(
         if (ret_hash_state) {
                 struct sha256_ctx *hash_state;
 
-                hash_state = malloc(sizeof(struct sha256_ctx));
+                hash_state = new(struct sha256_ctx, 1);
                 if (!hash_state)
                         return log_oom();
 
@@ -311,6 +315,102 @@ static int save_seed_file(
         return 0;
 }
 
+static int refresh_boot_seed(void) {
+        uint8_t buffer[RANDOM_EFI_SEED_SIZE];
+        struct sha256_ctx hash_state;
+        _cleanup_free_ void *seed_file_bytes = NULL;
+        _cleanup_free_ char *esp_path = NULL;
+        _cleanup_close_ int seed_fd = -1, dir_fd = -1;
+        size_t len;
+        ssize_t n;
+        int r;
+
+        assert_cc(RANDOM_EFI_SEED_SIZE == SHA256_DIGEST_SIZE);
+
+        r = find_esp_and_warn(NULL, NULL, /* unprivileged_mode= */ false, &esp_path,
+                              NULL, NULL, NULL, NULL, NULL);
+        if (r < 0) {
+                if (r == -ENOKEY) {
+                        log_debug_errno(r, "Couldn't find any ESP, so not updating ESP random seed.");
+                        return 0;
+                }
+                return r; /* find_esp_and_warn() already logged */
+        }
+
+        r = chase_symlinks("/loader", esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, &dir_fd);
+        if (r < 0) {
+                if (r == -ENOENT) {
+                        log_debug_errno(r, "Couldn't find ESP loader directory, so not updating ESP random seed.");
+                        return 0;
+                }
+                return log_error_errno(r, "Failed to open ESP loader directory: %m");
+        }
+        seed_fd = openat(dir_fd, "random-seed", O_NOFOLLOW|O_RDWR|O_CLOEXEC|O_NOCTTY);
+        if (seed_fd < 0 && errno == ENOENT) {
+                uint64_t features;
+                r = efi_loader_get_features(&features);
+                if (r == 0 && FLAGS_SET(features, EFI_LOADER_FEATURE_RANDOM_SEED))
+                        seed_fd = openat(dir_fd, "random-seed", O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
+                else {
+                        log_debug_errno(seed_fd, "Couldn't find ESP random seed, and not booted with systemd-boot, so not updating ESP random seed.");
+                        return 0;
+                }
+        }
+        if (seed_fd < 0)
+                return log_error_errno(errno, "Failed to open EFI seed path: %m");
+        r = random_seed_size(seed_fd, &len);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine EFI seed path length: %m");
+        seed_file_bytes = malloc(len);
+        if (!seed_file_bytes)
+                return log_oom();
+        n = loop_read(seed_fd, seed_file_bytes, len, false);
+        if (n < 0)
+                return log_error_errno(n, "Failed to read EFI seed file: %m");
+
+        /* Hash the old seed in so that we never regress in entropy. */
+        sha256_init_ctx(&hash_state);
+        sha256_process_bytes(&n, sizeof(n), &hash_state);
+        sha256_process_bytes(seed_file_bytes, n, &hash_state);
+
+        /* We're doing this opportunistically, so if the seeding dance before didn't manage to initialize the
+         * RNG, there's no point in doing it here. Secondly, getrandom(GRND_NONBLOCK) has been around longer
+         * than EFI seeding anyway, so there's no point in having non-getrandom() fallbacks here. So if this
+         * fails, just return early to cut our losses. */
+        n = getrandom(buffer, sizeof(buffer), GRND_NONBLOCK);
+        if (n < 0) {
+                if (errno == EAGAIN) {
+                        log_debug_errno(errno, "Random pool not initialized yet, so skipping EFI seed update");
+                        return 0;
+                }
+                if (errno == ENOSYS) {
+                        log_debug_errno(errno, "getrandom() not available, so skipping EFI seed update");
+                        return 0;
+                }
+                return log_error_errno(errno, "Failed to generate random bytes for EFI seed: %m");
+        }
+        assert(n == sizeof(buffer));
+
+        /* Hash the new seed into the state containing the old one to generate our final seed. */
+        sha256_process_bytes(&n, sizeof(n), &hash_state);
+        sha256_process_bytes(buffer, n, &hash_state);
+        sha256_finish_ctx(&hash_state, buffer);
+
+        if (lseek(seed_fd, 0, SEEK_SET) < 0)
+                return log_error_errno(errno, "Failed to seek to beginning of EFI seed file: %m");
+        r = loop_write(seed_fd, buffer, sizeof(buffer), false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write new EFI seed file: %m");
+        if (ftruncate(seed_fd, sizeof(buffer)) < 0)
+                return log_error_errno(errno, "Failed to truncate EFI seed file: %m");
+        r = fsync_full(seed_fd);
+        if (r < 0)
+                return log_error_errno(r, "Failed to fsync EFI seed file: %m");
+
+        log_debug("Updated random seed in ESP");
+        return 0;
+}
+
 static int help(int argc, char *argv[], void *userdata) {
         _cleanup_free_ char *link = NULL;
         int r;
@@ -402,15 +502,15 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return log_error_errno(r, "Failed to create directory " RANDOM_SEED_DIR ": %m");
 
+        random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY);
+        if (random_fd < 0)
+                return log_error_errno(errno, "Failed to open /dev/urandom: %m");
+
         /* When we load the seed we read it and write it to the device and then immediately update the saved
          * seed with new data, to make sure the next boot gets seeded differently. */
 
         switch (arg_action) {
         case ACTION_LOAD:
-                random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY);
-                if (random_fd < 0)
-                        return log_error_errno(errno, "Failed to open /dev/urandom: %m");
-
                 /* First, let's write the machine ID into /dev/urandom, not crediting entropy. See
                  * load_machine_id() for an explanation why. */
                 load_machine_id(random_fd);
@@ -428,8 +528,10 @@ static int run(int argc, char *argv[]) {
 
                                 log_full_errno(level, open_rw_error, "Failed to open " RANDOM_SEED " for writing: %m");
                                 log_full_errno(level, errno, "Failed to open " RANDOM_SEED " for reading: %m");
+                                r = -errno;
 
-                                return missing ? 0 : -errno;
+                                (void) refresh_boot_seed();
+                                return missing ? 0 : r;
                         }
                 } else
                         write_seed_file = true;
@@ -439,10 +541,7 @@ static int run(int argc, char *argv[]) {
                 break;
 
         case ACTION_SAVE:
-                random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
-                if (random_fd < 0)
-                        return log_error_errno(errno, "Failed to open /dev/urandom: %m");
-
+                (void) refresh_boot_seed();
                 seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600);
                 if (seed_fd < 0)
                         return log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m");
@@ -460,9 +559,11 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
-        if (read_seed_file)
+        if (read_seed_file) {
                 r = load_seed_file(seed_fd, random_fd, seed_size,
                                    write_seed_file ? &hash_state : NULL);
+                (void) refresh_boot_seed();
+        }
 
         if (r >= 0 && write_seed_file)
                 r = save_seed_file(seed_fd, random_fd, seed_size, synchronous, hash_state);
index dd0a8e121e256e39125c186d4f6c8928be21a340..ff645fc0d704315e422067b110a6bfa46505702a 100644 (file)
@@ -33,6 +33,7 @@
 #include "pretty-print.h"
 #include "process-util.h"
 #include "resolvconf-compat.h"
+#include "resolve-util.h"
 #include "resolvectl.h"
 #include "resolved-def.h"
 #include "resolved-dns-packet.h"
@@ -1114,49 +1115,49 @@ static int show_statistics(int argc, char **argv, void *userdata) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        table = table_new("key", "value");
+        table = table_new_vertical();
         if (!table)
                 return log_oom();
 
-        table_set_header(table, false);
-
         r = table_add_many(table,
                            TABLE_STRING, "Transactions",
                            TABLE_SET_COLOR, ansi_highlight(),
+                           TABLE_SET_ALIGN_PERCENT, 0,
                            TABLE_EMPTY,
-                           TABLE_STRING, "Current Transactions:",
+                           TABLE_FIELD, "Current Transactions",
                            TABLE_SET_ALIGN_PERCENT, 100,
                            TABLE_UINT64, n_current_transactions,
-                           TABLE_STRING, "Total Transactions:",
+                           TABLE_SET_ALIGN_PERCENT, 100,
+                           TABLE_FIELD, "Total Transactions",
                            TABLE_UINT64, n_total_transactions,
                            TABLE_EMPTY, TABLE_EMPTY,
                            TABLE_STRING, "Cache",
                            TABLE_SET_COLOR, ansi_highlight(),
                            TABLE_SET_ALIGN_PERCENT, 0,
                            TABLE_EMPTY,
-                           TABLE_STRING, "Current Cache Size:",
+                           TABLE_FIELD, "Current Cache Size",
                            TABLE_SET_ALIGN_PERCENT, 100,
                            TABLE_UINT64, cache_size,
-                           TABLE_STRING, "Cache Hits:",
+                           TABLE_FIELD, "Cache Hits",
                            TABLE_UINT64, n_cache_hit,
-                           TABLE_STRING, "Cache Misses:",
+                           TABLE_FIELD, "Cache Misses",
                            TABLE_UINT64, n_cache_miss,
                            TABLE_EMPTY, TABLE_EMPTY,
                            TABLE_STRING, "DNSSEC Verdicts",
                            TABLE_SET_COLOR, ansi_highlight(),
                            TABLE_SET_ALIGN_PERCENT, 0,
                            TABLE_EMPTY,
-                           TABLE_STRING, "Secure:",
+                           TABLE_FIELD, "Secure",
                            TABLE_SET_ALIGN_PERCENT, 100,
                            TABLE_UINT64, n_dnssec_secure,
-                           TABLE_STRING, "Insecure:",
+                           TABLE_FIELD, "Insecure",
                            TABLE_UINT64, n_dnssec_insecure,
-                           TABLE_STRING, "Bogus:",
+                           TABLE_FIELD, "Bogus",
                            TABLE_UINT64, n_dnssec_bogus,
-                           TABLE_STRING, "Indeterminate:",
+                           TABLE_FIELD, "Indeterminate:",
                            TABLE_UINT64, n_dnssec_indeterminate);
         if (r < 0)
-                table_log_add_error(r);
+                return table_log_add_error(r);
 
         r = table_print(table, NULL);
         if (r < 0)
@@ -1476,14 +1477,14 @@ static void global_info_clear(GlobalInfo *p) {
         strv_free(p->ntas);
 }
 
-static int dump_list(Table *table, const char *prefix, char * const *l) {
+static int dump_list(Table *table, const char *field, char * const *l) {
         int r;
 
         if (strv_isempty(l))
                 return 0;
 
         r = table_add_many(table,
-                           TABLE_STRING, prefix,
+                           TABLE_FIELD, field,
                            TABLE_STRV_WRAPPED, l);
         if (r < 0)
                 return table_log_add_error(r);
@@ -1655,15 +1656,13 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
         printf("%sLink %i (%s)%s\n",
                ansi_highlight(), ifindex, name, ansi_normal());
 
-        table = table_new("key", "value");
+        table = table_new_vertical();
         if (!table)
                 return log_oom();
 
-        table_set_header(table, false);
-
         r = table_add_many(table,
-                           TABLE_STRING, "Current Scopes:",
-                           TABLE_SET_ALIGN_PERCENT, 100);
+                           TABLE_FIELD, "Current Scopes",
+                           TABLE_SET_MINIMUM_WIDTH, 19);
         if (r < 0)
                 return table_log_add_error(r);
 
@@ -1695,24 +1694,24 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
                 return log_oom();
 
         r = table_add_many(table,
-                           TABLE_STRING,       "Protocols:",
+                           TABLE_FIELD,       "Protocols",
                            TABLE_STRV_WRAPPED, pstatus);
         if (r < 0)
                 return table_log_add_error(r);
 
         if (link_info.current_dns) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Current DNS Server:",
+                                   TABLE_FIELD, "Current DNS Server",
                                    TABLE_STRING, link_info.current_dns_ex ?: link_info.current_dns);
                 if (r < 0)
                         return table_log_add_error(r);
         }
 
-        r = dump_list(table, "DNS Servers:", link_info.dns_ex ?: link_info.dns);
+        r = dump_list(table, "DNS Servers", link_info.dns_ex ?: link_info.dns);
         if (r < 0)
                 return r;
 
-        r = dump_list(table, "DNS Domain:", link_info.domains);
+        r = dump_list(table, "DNS Domain", link_info.domains);
         if (r < 0)
                 return r;
 
@@ -1901,26 +1900,24 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
 
         printf("%sGlobal%s\n", ansi_highlight(), ansi_normal());
 
-        table = table_new("key", "value");
+        table = table_new_vertical();
         if (!table)
                 return log_oom();
 
-        table_set_header(table, false);
-
         _cleanup_strv_free_ char **pstatus = global_protocol_status(&global_info);
         if (!pstatus)
                 return log_oom();
 
         r = table_add_many(table,
-                           TABLE_STRING,            "Protocols:",
-                           TABLE_SET_ALIGN_PERCENT, 100,
+                           TABLE_FIELD,            "Protocols",
+                           TABLE_SET_MINIMUM_WIDTH, 19,
                            TABLE_STRV_WRAPPED,      pstatus);
         if (r < 0)
                 return table_log_add_error(r);
 
         if (global_info.resolv_conf_mode) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "resolv.conf mode:",
+                                   TABLE_FIELD, "resolv.conf mode",
                                    TABLE_STRING, global_info.resolv_conf_mode);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -1928,7 +1925,7 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
 
         if (global_info.current_dns) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Current DNS Server:",
+                                   TABLE_FIELD, "Current DNS Server",
                                    TABLE_STRING, global_info.current_dns_ex ?: global_info.current_dns);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -2277,6 +2274,8 @@ static int verb_default_route(int argc, char **argv, void *userdata) {
 
 static int verb_llmnr(int argc, char **argv, void *userdata) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_free_ char *global_llmnr_support_str = NULL;
+        ResolveSupport global_llmnr_support, llmnr_support;
         sd_bus *bus = ASSERT_PTR(userdata);
         int r;
 
@@ -2292,6 +2291,22 @@ static int verb_llmnr(int argc, char **argv, void *userdata) {
         if (argc < 3)
                 return status_ifindex(bus, arg_ifindex, NULL, STATUS_LLMNR, NULL);
 
+        llmnr_support = resolve_support_from_string(argv[2]);
+        if (llmnr_support < 0)
+                return log_error_errno(llmnr_support, "Invalid LLMNR setting: %s", argv[2]);
+
+        r = bus_get_property_string(bus, bus_resolve_mgr, "LLMNR", &error, &global_llmnr_support_str);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get the global LLMNR support state: %s", bus_error_message(&error, r));
+
+        global_llmnr_support = resolve_support_from_string(global_llmnr_support_str);
+        if (global_llmnr_support < 0)
+                return log_error_errno(global_llmnr_support, "Received invalid global LLMNR setting: %s", global_llmnr_support_str);
+
+        if (global_llmnr_support < llmnr_support)
+                log_warning("Setting LLMNR support level \"%s\" for \"%s\", but the global support level is \"%s\".",
+                            argv[2], arg_ifname, global_llmnr_support_str);
+
         r = bus_call_method(bus, bus_resolve_mgr, "SetLinkLLMNR", &error, NULL, "is", arg_ifindex, argv[2]);
         if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
                 sd_bus_error_free(&error);
@@ -2311,6 +2326,8 @@ static int verb_llmnr(int argc, char **argv, void *userdata) {
 
 static int verb_mdns(int argc, char **argv, void *userdata) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_free_ char *global_mdns_support_str = NULL;
+        ResolveSupport global_mdns_support, mdns_support;
         sd_bus *bus = ASSERT_PTR(userdata);
         int r;
 
@@ -2326,6 +2343,22 @@ static int verb_mdns(int argc, char **argv, void *userdata) {
         if (argc < 3)
                 return status_ifindex(bus, arg_ifindex, NULL, STATUS_MDNS, NULL);
 
+        mdns_support = resolve_support_from_string(argv[2]);
+        if (mdns_support < 0)
+                return log_error_errno(mdns_support, "Invalid mDNS setting: %s", argv[2]);
+
+        r = bus_get_property_string(bus, bus_resolve_mgr, "MulticastDNS", &error, &global_mdns_support_str);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get the global mDNS support state: %s", bus_error_message(&error, r));
+
+        global_mdns_support = resolve_support_from_string(global_mdns_support_str);
+        if (global_mdns_support < 0)
+                return log_error_errno(global_mdns_support, "Received invalid global mDNS setting: %s", global_mdns_support_str);
+
+        if (global_mdns_support < mdns_support)
+                log_warning("Setting mDNS support level \"%s\" for \"%s\", but the global support level is \"%s\".",
+                            argv[2], arg_ifname, global_mdns_support_str);
+
         r = bus_call_method(bus, bus_resolve_mgr, "SetLinkMulticastDNS", &error, NULL, "is", arg_ifindex, argv[2]);
         if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
                 sd_bus_error_free(&error);
index 91bbeaed5bfce1504a25e78d61759ea88a574bcd..b7dc60c9c8b66369bc946920a482769b976f95b6 100644 (file)
@@ -21,7 +21,7 @@ struct DnsQuestionItem {
 struct DnsQuestion {
         unsigned n_ref;
         size_t n_keys, n_allocated;
-        DnsQuestionItem items[0];
+        DnsQuestionItem items[];
 };
 
 DnsQuestion *dns_question_new(size_t n);
index be9efb0a79a592eb4573fe5953c826e11e766bd5..f939b534c3fd98d384718ef40f18a0a301cd5b14 100644 (file)
@@ -44,8 +44,8 @@ typedef enum DnsServerFeatureLevel {
 #define DNS_SERVER_FEATURE_LEVEL_IS_DNSSEC(x) ((x) >= DNS_SERVER_FEATURE_LEVEL_DO)
 #define DNS_SERVER_FEATURE_LEVEL_IS_UDP(x) IN_SET(x, DNS_SERVER_FEATURE_LEVEL_UDP, DNS_SERVER_FEATURE_LEVEL_EDNS0, DNS_SERVER_FEATURE_LEVEL_DO)
 
-const char* dns_server_feature_level_to_string(int i) _const_;
-int dns_server_feature_level_from_string(const char *s) _pure_;
+const char* dns_server_feature_level_to_string(DnsServerFeatureLevel i) _const_;
+DnsServerFeatureLevel dns_server_feature_level_from_string(const char *s) _pure_;
 
 struct DnsServer {
         Manager *manager;
index 828045f68fb878e728839b4848d9e8dc8bde6982..ca670b30f1e63d8ec787aebd0819570ad40169f8 100644 (file)
@@ -165,7 +165,6 @@ static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) {
                 /* Defined by RFC 8375. The most official choice. */
                 "home.arpa\0";
 
-        const char *name;
         int r;
 
         assert(d);
index 5b1096daf6dad5381a0d1eb175ce244dbc5bb90e..9b6d14f20cc92d197566d48cfcfa386e62fedb00 100644 (file)
@@ -22,6 +22,8 @@
 
 static BUS_DEFINE_PROPERTY_GET(property_get_dnssec_supported, "b", Link, link_dnssec_supported);
 static BUS_DEFINE_PROPERTY_GET2(property_get_dnssec_mode, "s", Link, link_get_dnssec_mode, dnssec_mode_to_string);
+static BUS_DEFINE_PROPERTY_GET2(property_get_llmnr_support, "s", Link, link_get_llmnr_support, resolve_support_to_string);
+static BUS_DEFINE_PROPERTY_GET2(property_get_mdns_support, "s", Link, link_get_mdns_support, resolve_support_to_string);
 
 static int property_get_dns_over_tls_mode(
                 sd_bus *bus,
@@ -864,8 +866,8 @@ static const sd_bus_vtable link_vtable[] = {
         SD_BUS_PROPERTY("CurrentDNSServerEx", "(iayqs)", property_get_current_dns_server_ex, offsetof(Link, current_dns_server), 0),
         SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
         SD_BUS_PROPERTY("DefaultRoute", "b", property_get_default_route, 0, 0),
-        SD_BUS_PROPERTY("LLMNR", "s", bus_property_get_resolve_support, offsetof(Link, llmnr_support), 0),
-        SD_BUS_PROPERTY("MulticastDNS", "s", bus_property_get_resolve_support, offsetof(Link, mdns_support), 0),
+        SD_BUS_PROPERTY("LLMNR", "s", property_get_llmnr_support, 0, 0),
+        SD_BUS_PROPERTY("MulticastDNS", "s", property_get_mdns_support, 0, 0),
         SD_BUS_PROPERTY("DNSOverTLS", "s", property_get_dns_over_tls_mode, 0, 0),
         SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, 0, 0),
         SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0),
index 5b34677cbefea230565c789f24e15c01d8444921..d41f6f3e5477585982540441dd46d0bb50fd4160 100644 (file)
@@ -140,8 +140,7 @@ void link_allocate_scopes(Link *l) {
                 l->unicast_scope = dns_scope_free(l->unicast_scope);
 
         if (link_relevant(l, AF_INET, true) &&
-            l->llmnr_support != RESOLVE_SUPPORT_NO &&
-            l->manager->llmnr_support != RESOLVE_SUPPORT_NO) {
+            link_get_llmnr_support(l) != RESOLVE_SUPPORT_NO) {
                 if (!l->llmnr_ipv4_scope) {
                         r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
                         if (r < 0)
@@ -151,9 +150,7 @@ void link_allocate_scopes(Link *l) {
                 l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
 
         if (link_relevant(l, AF_INET6, true) &&
-            l->llmnr_support != RESOLVE_SUPPORT_NO &&
-            l->manager->llmnr_support != RESOLVE_SUPPORT_NO &&
-            socket_ipv6_is_supported()) {
+            link_get_llmnr_support(l) != RESOLVE_SUPPORT_NO) {
                 if (!l->llmnr_ipv6_scope) {
                         r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
                         if (r < 0)
@@ -163,8 +160,7 @@ void link_allocate_scopes(Link *l) {
                 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
 
         if (link_relevant(l, AF_INET, true) &&
-            l->mdns_support != RESOLVE_SUPPORT_NO &&
-            l->manager->mdns_support != RESOLVE_SUPPORT_NO) {
+            link_get_mdns_support(l) != RESOLVE_SUPPORT_NO) {
                 if (!l->mdns_ipv4_scope) {
                         r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, l, DNS_PROTOCOL_MDNS, AF_INET);
                         if (r < 0)
@@ -174,8 +170,7 @@ void link_allocate_scopes(Link *l) {
                 l->mdns_ipv4_scope = dns_scope_free(l->mdns_ipv4_scope);
 
         if (link_relevant(l, AF_INET6, true) &&
-            l->mdns_support != RESOLVE_SUPPORT_NO &&
-            l->manager->mdns_support != RESOLVE_SUPPORT_NO) {
+            link_get_mdns_support(l) != RESOLVE_SUPPORT_NO) {
                 if (!l->mdns_ipv6_scope) {
                         r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, l, DNS_PROTOCOL_MDNS, AF_INET6);
                         if (r < 0)
@@ -192,8 +187,7 @@ void link_add_rrs(Link *l, bool force_remove) {
                 link_address_add_rrs(a, force_remove);
 
         if (!force_remove &&
-            l->mdns_support == RESOLVE_SUPPORT_YES &&
-            l->manager->mdns_support == RESOLVE_SUPPORT_YES) {
+            link_get_mdns_support(l) == RESOLVE_SUPPORT_YES) {
 
                 if (l->mdns_ipv4_scope) {
                         r = dns_scope_add_dnssd_services(l->mdns_ipv4_scope);
@@ -652,13 +646,13 @@ int link_update(Link *l) {
         if (r < 0)
                 return r;
 
-        if (l->llmnr_support != RESOLVE_SUPPORT_NO) {
+        if (link_get_llmnr_support(l) != RESOLVE_SUPPORT_NO) {
                 r = manager_llmnr_start(l->manager);
                 if (r < 0)
                         return r;
         }
 
-        if (l->mdns_support != RESOLVE_SUPPORT_NO) {
+        if (link_get_mdns_support(l) != RESOLVE_SUPPORT_NO) {
                 r = manager_mdns_start(l->manager);
                 if (r < 0)
                         return r;
@@ -803,6 +797,24 @@ bool link_dnssec_supported(Link *l) {
         return true;
 }
 
+ResolveSupport link_get_llmnr_support(Link *link) {
+        assert(link);
+        assert(link->manager);
+
+        /* This provides the effective LLMNR support level for the link, instead of the 'internal' per-link setting. */
+
+        return MIN(link->llmnr_support, link->manager->llmnr_support);
+}
+
+ResolveSupport link_get_mdns_support(Link *link) {
+        assert(link);
+        assert(link->manager);
+
+        /* This provides the effective mDNS support level for the link, instead of the 'internal' per-link setting. */
+
+        return MIN(link->mdns_support, link->manager->mdns_support);
+}
+
 int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
         LinkAddress *a;
 
@@ -886,8 +898,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
                 if (!force_remove &&
                     link_address_relevant(a, true) &&
                     a->link->llmnr_ipv4_scope &&
-                    a->link->llmnr_support == RESOLVE_SUPPORT_YES &&
-                    a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) {
+                    link_get_llmnr_support(a->link) == RESOLVE_SUPPORT_YES) {
 
                         if (!a->link->manager->llmnr_host_ipv4_key) {
                                 a->link->manager->llmnr_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->llmnr_hostname);
@@ -940,8 +951,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
                 if (!force_remove &&
                     link_address_relevant(a, true) &&
                     a->link->mdns_ipv4_scope &&
-                    a->link->mdns_support == RESOLVE_SUPPORT_YES &&
-                    a->link->manager->mdns_support == RESOLVE_SUPPORT_YES) {
+                    link_get_mdns_support(a->link) == RESOLVE_SUPPORT_YES) {
                         if (!a->link->manager->mdns_host_ipv4_key) {
                                 a->link->manager->mdns_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->mdns_hostname);
                                 if (!a->link->manager->mdns_host_ipv4_key) {
@@ -996,8 +1006,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
                 if (!force_remove &&
                     link_address_relevant(a, true) &&
                     a->link->llmnr_ipv6_scope &&
-                    a->link->llmnr_support == RESOLVE_SUPPORT_YES &&
-                    a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) {
+                    link_get_llmnr_support(a->link) == RESOLVE_SUPPORT_YES) {
 
                         if (!a->link->manager->llmnr_host_ipv6_key) {
                                 a->link->manager->llmnr_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->llmnr_hostname);
@@ -1050,8 +1059,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
                 if (!force_remove &&
                     link_address_relevant(a, true) &&
                     a->link->mdns_ipv6_scope &&
-                    a->link->mdns_support == RESOLVE_SUPPORT_YES &&
-                    a->link->manager->mdns_support == RESOLVE_SUPPORT_YES) {
+                    link_get_mdns_support(a->link) == RESOLVE_SUPPORT_YES) {
 
                         if (!a->link->manager->mdns_host_ipv6_key) {
                                 a->link->manager->mdns_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->mdns_hostname);
index b5299e0b5b87ae5577e88cf26b644c7675451ace..d2043a10008050b0f84d423760d9257b3de911cc 100644 (file)
@@ -104,6 +104,9 @@ bool link_dnssec_supported(Link *l);
 
 DnsOverTlsMode link_get_dns_over_tls_mode(Link *l);
 
+ResolveSupport link_get_llmnr_support(Link *link);
+ResolveSupport link_get_mdns_support(Link *link);
+
 int link_save_user(Link *l);
 int link_load_user(Link *l);
 void link_remove_user(Link *l);
index 668f6c3eee9b80bfd8f83c93b3cf559d4136ddb4..9f77997d5a59843a2e87449a68cbb70b8654f9f9 100644 (file)
@@ -61,6 +61,37 @@ struct acpi_fpdt_boot {
         uint64_t exit_services_exit;
 } _packed;
 
+/* /dev/mem is deprecated on many systems, try using /sys/firmware/acpi/fpdt parsing instead.
+ * This code requires kernel version 5.12 on x86 based machines or 6.2 for arm64 */
+static int acpi_get_boot_usec_kernel_parsed(usec_t *ret_loader_start, usec_t *ret_loader_exit) {
+        usec_t start, end;
+        int r;
+
+        r = read_timestamp_file("/sys/firmware/acpi/fpdt/boot/exitbootservice_end_ns", &end);
+        if (r < 0)
+                return r;
+
+        if (end == 0)
+                /* Non-UEFI compatible boot. */
+                return -ENODATA;
+
+        r = read_timestamp_file("/sys/firmware/acpi/fpdt/boot/bootloader_launch_ns", &start);
+        if (r < 0)
+                return r;
+
+        if (start == 0 || end < start)
+                return -EINVAL;
+        if (end > NSEC_PER_HOUR)
+                return -EINVAL;
+
+        if (ret_loader_start)
+                *ret_loader_start = start / 1000;
+        if (ret_loader_exit)
+                *ret_loader_exit = end / 1000;
+
+        return 0;
+}
+
 int acpi_get_boot_usec(usec_t *ret_loader_start, usec_t *ret_loader_exit) {
         _cleanup_free_ char *buf = NULL;
         struct acpi_table_header *tbl;
@@ -73,6 +104,10 @@ int acpi_get_boot_usec(usec_t *ret_loader_start, usec_t *ret_loader_exit) {
         struct acpi_fpdt_boot_header hbrec;
         struct acpi_fpdt_boot brec;
 
+        r = acpi_get_boot_usec_kernel_parsed(ret_loader_start, ret_loader_exit);
+        if (r != -ENOENT) /* fallback to /dev/mem hack only if kernel doesn't support the new sysfs files */
+                return r;
+
         r = read_full_virtual_file("/sys/firmware/acpi/tables/FPDT", &buf, &l);
         if (r < 0)
                 return r;
index 84d7b31bad7db78d33e03b7fb0cd050566780ed0..e7db23c2017bef81ae881d71c99e7dbaf4c92ca1 100644 (file)
@@ -34,6 +34,7 @@
 #include "memory-util.h"
 #include "missing_syscall.h"
 #include "mkdir-label.h"
+#include "nulstr-util.h"
 #include "process-util.h"
 #include "random-util.h"
 #include "signal-util.h"
@@ -112,6 +113,10 @@ static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **pa
         if (r < 0)
                 return r;
 
+        /* chop off the final NUL byte. We do this because we want to use the separator NUL bytes only if we
+         * have multiple passwords. */
+        n = LESS_BY(n, (size_t) 1);
+
         serial = add_key("user", keyname, p, n, KEY_SPEC_USER_KEYRING);
         if (serial == -1)
                 return -errno;
index 5b4f674ec2b01b3516f861e356d9b5fdf1765356..c2902d35082e40b9d26a69269a64e634885ae518 100644 (file)
@@ -135,7 +135,7 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) {
                         continue;
 
                 if (table[i].target) {
-                        const char *target = NULL, *s;
+                        const char *target = NULL;
 
                         /* check if one of the targets exists */
                         NULSTR_FOREACH(s, table[i].target) {
index 6a34b10c044c3c403e9295d5fcaf962cacde7399..ff5cd9bc1fbe7cf16e7c5e8b9dba1b3e75add83d 100644 (file)
@@ -19,6 +19,7 @@
 #include "pretty-print.h"
 #include "recurse-dir.h"
 #include "sort-util.h"
+#include "stat-util.h"
 #include "string-table.h"
 #include "strv.h"
 #include "terminal-util.h"
@@ -507,7 +508,7 @@ static int boot_loader_read_conf_path(BootConfig *config, const char *root, cons
         assert(config);
         assert(path);
 
-        r = chase_symlinks_and_fopen_unlocked(path, root, CHASE_PREFIX_ROOT, "re", &full, &f);
+        r = chase_symlinks_and_fopen_unlocked(path, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, "re", &full, &f);
         if (r == -ENOENT)
                 return 0;
         if (r < 0)
@@ -543,23 +544,6 @@ static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
         return -strverscmp_improved(a->id, b->id);
 }
 
-static void inode_hash_func(const struct stat *q, struct siphash *state) {
-        siphash24_compress(&q->st_dev, sizeof(q->st_dev), state);
-        siphash24_compress(&q->st_ino, sizeof(q->st_ino), state);
-}
-
-static int inode_compare_func(const struct stat *a, const struct stat *b) {
-        int r;
-
-        r = CMP(a->st_dev, b->st_dev);
-        if (r != 0)
-                return r;
-
-        return CMP(a->st_ino, b->st_ino);
-}
-
-DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_hash_ops, struct stat, inode_hash_func, inode_compare_func, free);
-
 static int config_check_inode_relevant_and_unseen(BootConfig *config, int fd, const char *fname) {
         _cleanup_free_ char *d = NULL;
         struct stat st;
@@ -609,7 +593,7 @@ static int boot_entries_find_type1(
         assert(root);
         assert(dir);
 
-        dir_fd = chase_symlinks_and_open(dir, root, CHASE_PREFIX_ROOT, O_DIRECTORY|O_CLOEXEC, &full);
+        dir_fd = chase_symlinks_and_open(dir, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, O_DIRECTORY|O_CLOEXEC, &full);
         if (dir_fd == -ENOENT)
                 return 0;
         if (dir_fd < 0)
@@ -726,7 +710,7 @@ static int boot_entry_load_unified(
         if (!tmp.root)
                 return log_oom();
 
-        tmp.kernel = strdup(skip_leading_chars(k, "/"));
+        tmp.kernel = path_make_absolute(k, "/");
         if (!tmp.kernel)
                 return log_oom();
 
@@ -869,7 +853,7 @@ static int boot_entries_find_unified(
         assert(config);
         assert(dir);
 
-        r = chase_symlinks_and_opendir(dir, root, CHASE_PREFIX_ROOT, &full, &d);
+        r = chase_symlinks_and_opendir(dir, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &full, &d);
         if (r == -ENOENT)
                 return 0;
         if (r < 0)
@@ -994,6 +978,12 @@ static int boot_config_find(const BootConfig *config, const char *id) {
         if (!id)
                 return -1;
 
+        if (id[0] == '@') {
+                if (!strcaseeq(id, "@saved"))
+                        return -1;
+                id = config->entry_selected;
+        }
+
         for (size_t i = 0; i < config->n_entries; i++)
                 if (fnmatch(id, config->entries[i].id, FNM_CASEFOLD) == 0)
                         return i;
@@ -1276,9 +1266,13 @@ static void boot_entry_file_list(
         assert(p);
         assert(ret_status);
 
-        int status = chase_symlinks_and_access(p, root, CHASE_PREFIX_ROOT, F_OK, NULL, NULL);
+        int status = chase_symlinks_and_access(p, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, NULL, NULL);
+
+        /* Note that this shows two '/' between the root and the file. This is intentional to highlight (in
+         * the abscence of color support) to the user that the boot loader is only interested in the second
+         * part of the file. */
+        printf("%13s%s %s%s/%s", strempty(field), field ? ":" : " ", ansi_grey(), root, ansi_normal());
 
-        printf("%13s%s ", strempty(field), field ? ":" : " ");
         if (status < 0) {
                 errno = -status;
                 printf("%s%s%s (%m)\n", ansi_highlight_red(), p, ansi_normal());
index 92a9bcde4f7d7a8d50c1e03794b7e0056853ca99..4574a7899e1cfd3eaa67240ac9f62201c5af8707 100644 (file)
@@ -245,7 +245,6 @@ int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offs
 
         assert(infd >= 0);
         assert(outfd >= 0);
-        assert(sz > 0);
 
         r = fd_verify_regular(outfd);
         if (r < 0)
index 27b6f88cd0557388b51f739c6899f2b45b7f97c0..b0267427fa1bf9b60e463c25b4f676f7e3a82d3e 100644 (file)
@@ -162,7 +162,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b
 
                         bus_print_property_value(name, expected_value, flags, "[not set]");
 
-                else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "MemoryAvailable") && u == CGROUP_LIMIT_MAX) ||
+                else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryZSwapMax", "MemoryLimit", "MemoryAvailable") && u == CGROUP_LIMIT_MAX) ||
                          (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == UINT64_MAX) ||
                          (startswith(name, "Limit") && u == UINT64_MAX) ||
                          (startswith(name, "DefaultLimit") && u == UINT64_MAX))
index b850a28e859119af2cce489d315892d4c41e8284..784ae7794dce7bd7450fb4dd3b5a4f6df7952c88 100644 (file)
@@ -523,6 +523,7 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
                               "MemoryHigh",
                               "MemoryMax",
                               "MemorySwapMax",
+                              "MemoryZSwapMax",
                               "MemoryLimit",
                               "TasksMax")) {
 
index e34a68ef8696ae1dc112029aabd6d3b506f9958f..e6f67fc714920e5a61a868202157b3f0b670a27b 100644 (file)
@@ -211,7 +211,6 @@ static int show_cgroup_name(
 
         if (FLAGS_SET(flags, OUTPUT_CGROUP_XATTRS) && fd >= 0) {
                 _cleanup_free_ char *nl = NULL;
-                char *xa;
 
                 r = flistxattr_malloc(fd, &nl);
                 if (r < 0)
index f404d99878b023ba6b07e838ff619789ecef7249..d5fdbbf9e07d5c57916112ed21b79388a0059245 100644 (file)
 #include "fs-util.h"
 #include "glob-util.h"
 #include "hostname-util.h"
-#include "initrd-util.h"
 #include "ima-util.h"
+#include "initrd-util.h"
 #include "limits-util.h"
 #include "list.h"
 #include "macro.h"
 #include "mountpoint-util.h"
+#include "nulstr-util.h"
 #include "os-util.h"
 #include "parse-util.h"
 #include "path-util.h"
index 69932865ba59fc33ceddd8e30012e8847288228c..29051ca0e34b11af116945c3f35e05998c7f3a6d 100644 (file)
@@ -211,7 +211,6 @@ static int parse_line(
 
                 if (sections && !nulstr_contains(sections, n)) {
                         bool ignore;
-                        const char *t;
 
                         ignore = (flags & CONFIG_PARSE_RELAXED) || startswith(n, "X-");
 
index 182544a206b755666014d88a983d91dc1596e242..2b492c38a5af63a2fba1453a2b86b84dd260f7fa 100644 (file)
@@ -687,6 +687,7 @@ static int fd_copy_tree_generic(
                 uid_t override_uid,
                 gid_t override_gid,
                 CopyFlags copy_flags,
+                const Set *denylist,
                 HardlinkContext *hardlink_context,
                 const char *display_path,
                 copy_progress_path_t progress_path,
@@ -877,6 +878,7 @@ static int fd_copy_directory(
                 uid_t override_uid,
                 gid_t override_gid,
                 CopyFlags copy_flags,
+                const Set *denylist,
                 HardlinkContext *hardlink_context,
                 const char *display_path,
                 copy_progress_path_t progress_path,
@@ -979,6 +981,11 @@ static int fd_copy_directory(
                                 return r;
                 }
 
+                if (set_contains(denylist, &buf)) {
+                        log_debug("%s/%s is in the denylist, skipping", from, de->d_name);
+                        continue;
+                }
+
                 if (S_ISDIR(buf.st_mode)) {
                         /*
                          * Don't descend into directories on other file systems, if this is requested. We do a simple
@@ -1011,7 +1018,10 @@ static int fd_copy_directory(
                         }
                 }
 
-                q = fd_copy_tree_generic(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, depth_left-1, override_uid, override_gid, copy_flags, hardlink_context, child_display_path, progress_path, progress_bytes, userdata);
+                q = fd_copy_tree_generic(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device,
+                                         depth_left-1, override_uid, override_gid, copy_flags, denylist,
+                                         hardlink_context, child_display_path, progress_path, progress_bytes,
+                                         userdata);
 
                 if (q == -EINTR) /* Propagate SIGINT/SIGTERM up instantly */
                         return q;
@@ -1082,6 +1092,7 @@ static int fd_copy_tree_generic(
                 uid_t override_uid,
                 gid_t override_gid,
                 CopyFlags copy_flags,
+                const Set *denylist,
                 HardlinkContext *hardlink_context,
                 const char *display_path,
                 copy_progress_path_t progress_path,
@@ -1090,7 +1101,9 @@ static int fd_copy_tree_generic(
         int r;
 
         if (S_ISDIR(st->st_mode))
-                return fd_copy_directory(df, from, st, dt, to, original_device, depth_left-1, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_path, progress_bytes, userdata);
+                return fd_copy_directory(df, from, st, dt, to, original_device, depth_left-1, override_uid,
+                                         override_gid, copy_flags, denylist, hardlink_context, display_path,
+                                         progress_path, progress_bytes, userdata);
 
         r = fd_copy_leaf(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_bytes, userdata);
         /* We just tried to copy a leaf node of the tree. If it failed because the node already exists *and* the COPY_REPLACE flag has been provided, we should unlink the node and re-copy. */
@@ -1113,6 +1126,7 @@ int copy_tree_at_full(
                 uid_t override_uid,
                 gid_t override_gid,
                 CopyFlags copy_flags,
+                const Set *denylist,
                 copy_progress_path_t progress_path,
                 copy_progress_bytes_t progress_bytes,
                 void *userdata) {
@@ -1126,7 +1140,9 @@ int copy_tree_at_full(
         if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
                 return -errno;
 
-        r = fd_copy_tree_generic(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
+        r = fd_copy_tree_generic(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid,
+                                 override_gid, copy_flags, denylist, NULL, NULL, progress_path,
+                                 progress_bytes, userdata);
         if (r < 0)
                 return r;
 
@@ -1188,7 +1204,7 @@ int copy_directory_fd_full(
                         COPY_DEPTH_MAX,
                         UID_INVALID, GID_INVALID,
                         copy_flags,
-                        NULL, NULL,
+                        NULL, NULL, NULL,
                         progress_path,
                         progress_bytes,
                         userdata);
@@ -1231,7 +1247,7 @@ int copy_directory_full(
                         COPY_DEPTH_MAX,
                         UID_INVALID, GID_INVALID,
                         copy_flags,
-                        NULL, NULL,
+                        NULL, NULL, NULL,
                         progress_path,
                         progress_bytes,
                         userdata);
@@ -1539,7 +1555,6 @@ int copy_rights_with_fallback(int fdf, int fdt, const char *patht) {
 int copy_xattr(int fdf, int fdt, CopyFlags copy_flags) {
         _cleanup_free_ char *names = NULL;
         int ret = 0, r;
-        const char *p;
 
         r = flistxattr_malloc(fdf, &names);
         if (r < 0)
index d755916bd98dea3a89f419010e8721165bdbc1bb..d19361c9a29352ae5cc4517cc7ed9d38ce06324b 100644 (file)
@@ -9,6 +9,8 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include "set.h"
+
 typedef enum CopyFlags {
         COPY_REFLINK     = 1 << 0,  /* Try to reflink */
         COPY_MERGE       = 1 << 1,  /* Merge existing trees with our new one to copy */
@@ -45,12 +47,12 @@ static inline int copy_file_atomic(const char *from, const char *to, mode_t mode
         return copy_file_atomic_full(from, to, mode, chattr_flags, chattr_mask, copy_flags, NULL, NULL);
 }
 
-int copy_tree_at_full(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
-static inline int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
-        return copy_tree_at_full(fdf, from, fdt, to, override_uid, override_gid, copy_flags, NULL, NULL, NULL);
+int copy_tree_at_full(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, const Set *denylist, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
+static inline int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, const Set *denylist) {
+        return copy_tree_at_full(fdf, from, fdt, to, override_uid, override_gid, copy_flags, denylist, NULL, NULL, NULL);
 }
-static inline int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
-        return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, NULL, NULL, NULL);
+static inline int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, const Set *denylist) {
+        return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, denylist, NULL, NULL, NULL);
 }
 
 int copy_directory_fd_full(int dirfd, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
index 401e7a3f9c7d9f7134b5d17d86f3c1dbd76048bb..7437cbed6bab448c29cc85c1e12810a0efbeec73 100644 (file)
@@ -49,6 +49,18 @@ int (*sym_crypt_token_max)(const char *type);
 #endif
 crypt_token_info (*sym_crypt_token_status)(struct crypt_device *cd, int token, const char **type);
 int (*sym_crypt_volume_key_get)(struct crypt_device *cd, int keyslot, char *volume_key, size_t *volume_key_size, const char *passphrase, size_t passphrase_size);
+#if HAVE_CRYPT_REENCRYPT_INIT_BY_PASSPHRASE
+int (*sym_crypt_reencrypt_init_by_passphrase)(struct crypt_device *cd, const char *name, const char *passphrase, size_t passphrase_size, int keyslot_old, int keyslot_new, const char *cipher, const char *cipher_mode, const struct crypt_params_reencrypt *params);
+#endif
+#if HAVE_CRYPT_REENCRYPT
+int (*sym_crypt_reencrypt)(struct crypt_device *cd, int (*progress)(uint64_t size, uint64_t offset, void *usrptr));
+#endif
+int (*sym_crypt_metadata_locking)(struct crypt_device *cd, int enable);
+#if HAVE_CRYPT_SET_DATA_OFFSET
+int (*sym_crypt_set_data_offset)(struct crypt_device *cd, uint64_t data_offset);
+#endif
+int (*sym_crypt_header_restore)(struct crypt_device *cd, const char *requested_type, const char *backup_file);
+int (*sym_crypt_volume_key_keyring)(struct crypt_device *cd, int enable);
 
 static void cryptsetup_log_glue(int level, const char *msg, void *usrptr) {
 
@@ -234,7 +246,19 @@ int dlopen_cryptsetup(void) {
                         DLSYM_ARG(crypt_token_max),
 #endif
                         DLSYM_ARG(crypt_token_status),
-                        DLSYM_ARG(crypt_volume_key_get));
+                        DLSYM_ARG(crypt_volume_key_get),
+#if HAVE_CRYPT_REENCRYPT_INIT_BY_PASSPHRASE
+                        DLSYM_ARG(crypt_reencrypt_init_by_passphrase),
+#endif
+#if HAVE_CRYPT_REENCRYPT
+                        DLSYM_ARG(crypt_reencrypt),
+#endif
+                        DLSYM_ARG(crypt_metadata_locking),
+#if HAVE_CRYPT_SET_DATA_OFFSET
+                        DLSYM_ARG(crypt_set_data_offset),
+#endif
+                        DLSYM_ARG(crypt_header_restore),
+                        DLSYM_ARG(crypt_volume_key_keyring));
         if (r <= 0)
                 return r;
 
index b390dc9a5cbb0de52799609799c370dd2560cad9..5ff439d9c2973368baf353ca47638d22477fa026 100644 (file)
@@ -64,6 +64,18 @@ static inline int crypt_token_max(_unused_ const char *type) {
 #endif
 extern crypt_token_info (*sym_crypt_token_status)(struct crypt_device *cd, int token, const char **type);
 extern int (*sym_crypt_volume_key_get)(struct crypt_device *cd, int keyslot, char *volume_key, size_t *volume_key_size, const char *passphrase, size_t passphrase_size);
+#if HAVE_CRYPT_REENCRYPT_INIT_BY_PASSPHRASE
+extern int (*sym_crypt_reencrypt_init_by_passphrase)(struct crypt_device *cd, const char *name, const char *passphrase, size_t passphrase_size, int keyslot_old, int keyslot_new, const char *cipher, const char *cipher_mode, const struct crypt_params_reencrypt *params);
+#endif
+#if HAVE_CRYPT_REENCRYPT
+extern int (*sym_crypt_reencrypt)(struct crypt_device *cd, int (*progress)(uint64_t size, uint64_t offset, void *usrptr));
+#endif
+extern int (*sym_crypt_metadata_locking)(struct crypt_device *cd, int enable);
+#if HAVE_CRYPT_SET_DATA_OFFSET
+extern int (*sym_crypt_set_data_offset)(struct crypt_device *cd, uint64_t data_offset);
+#endif
+extern int (*sym_crypt_header_restore)(struct crypt_device *cd, const char *requested_type, const char *backup_file);
+extern int (*sym_crypt_volume_key_keyring)(struct crypt_device *cd, int enable);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct crypt_device *, crypt_free, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct crypt_device *, sym_crypt_free, NULL);
index a34052451034dfacc697d6aa053142a902afb06f..e0db777c96effafc3e2c40c333ecb0050c0f5852 100644 (file)
@@ -22,7 +22,6 @@ int dev_setup(const char *prefix, uid_t uid, gid_t gid) {
                 "/proc/self/fd/1\0"  "/dev/stdout\0"
                 "/proc/self/fd/2\0"  "/dev/stderr\0";
 
-        const char *j, *k;
         int r;
 
         NULSTR_FOREACH_PAIR(j, k, symlinks) {
index fad95f7f435ccf5032c4dcc82d6950201a34a32d..073e5e84338d50f0881bc4ad992e0774720c234d 100644 (file)
@@ -429,7 +429,6 @@ int image_find(ImageClass class,
                const char *root,
                Image **ret) {
 
-        const char *path;
         int r;
 
         assert(class >= 0);
@@ -533,7 +532,6 @@ int image_discover(
                 const char *root,
                 Hashmap *h) {
 
-        const char *path;
         int r;
 
         assert(class >= 0);
@@ -1262,8 +1260,6 @@ bool image_in_search_path(
                 const char *root,
                 const char *image) {
 
-        const char *path;
-
         assert(image);
 
         NULSTR_FOREACH(path, image_search_path[class]) {
index 2f1903121636aca5b88fd68e43b5055a38e5ab96..d67bdd501bcd932ae60d4b5ac86620d599144948 100644 (file)
@@ -333,6 +333,27 @@ static int open_partition(const char *node, bool is_partition, const LoopDevice
         return TAKE_FD(fd);
 }
 
+static int compare_arch(Architecture a, Architecture b) {
+        if (a == b)
+                return 0;
+
+        if (a == native_architecture())
+                return 1;
+
+        if (b == native_architecture())
+                return -1;
+
+#ifdef ARCHITECTURE_SECONDARY
+        if (a == ARCHITECTURE_SECONDARY)
+                return 1;
+
+        if (b == ARCHITECTURE_SECONDARY)
+                return -1;
+#endif
+
+        return 0;
+}
+
 static int dissect_image(
                 DissectedImage *m,
                 int fd,
@@ -349,7 +370,7 @@ static int dissect_image(
         _cleanup_(blkid_free_probep) blkid_probe b = NULL;
         _cleanup_free_ char *generic_node = NULL;
         sd_id128_t generic_uuid = SD_ID128_NULL;
-        const char *pttype = NULL;
+        const char *pttype = NULL, *sptuuid = NULL;
         blkid_partlist pl;
         int r, generic_nr = -1, n_partitions;
 
@@ -410,7 +431,7 @@ static int dissect_image(
         if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
                 /* Look for file system superblocks, unless we only shall look for GPT partition tables */
                 blkid_probe_enable_superblocks(b, 1);
-                blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE);
+                blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE|BLKID_SUBLKS_UUID);
         }
 
         blkid_probe_enable_partitions(b, 1);
@@ -433,8 +454,9 @@ static int dissect_image(
                 (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
                 if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
                         _cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
-                        const char *fstype = NULL, *options = NULL;
+                        const char *fstype = NULL, *options = NULL, *suuid = NULL;
                         _cleanup_close_ int mount_node_fd = -1;
+                        sd_id128_t uuid = SD_ID128_NULL;
 
                         if (FLAGS_SET(flags, DISSECT_IMAGE_OPEN_PARTITION_DEVICES)) {
                                 mount_node_fd = open_partition(devname, /* is_partition = */ false, m->loop);
@@ -444,6 +466,7 @@ static int dissect_image(
 
                         /* OK, we have found a file system, that's our root partition then. */
                         (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
+                        (void) blkid_probe_lookup_value(b, "UUID", &suuid, NULL);
 
                         if (fstype) {
                                 t = strdup(fstype);
@@ -451,6 +474,15 @@ static int dissect_image(
                                         return -ENOMEM;
                         }
 
+                        if (suuid) {
+                                /* blkid will return FAT's serial number as UUID, hence it is quite possible
+                                 * that parsing this will fail. We'll ignore the ID, since it's just too
+                                 * short to be useful as tru identifier. */
+                                r = sd_id128_from_string(suuid, &uuid);
+                                if (r < 0)
+                                        log_debug_errno(r, "Failed to parse file system UUID '%s', ignoring: %m", suuid);
+                        }
+
                         n = strdup(devname);
                         if (!n)
                                 return -ENOMEM;
@@ -467,6 +499,8 @@ static int dissect_image(
                         m->verity_sig_ready = m->verity_ready &&
                                 verity->root_hash_sig;
 
+                        m->image_uuid = uuid;
+
                         options = mount_options_from_designator(mount_options, PARTITION_ROOT);
                         if (options) {
                                 o = strdup(options);
@@ -515,6 +549,13 @@ static int dissect_image(
                         return -EPROTONOSUPPORT;
         }
 
+        (void) blkid_probe_lookup_value(b, "PTUUID", &sptuuid, NULL);
+        if (sptuuid) {
+                r = sd_id128_from_string(sptuuid, &m->image_uuid);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse partition table UUID '%s', ignoring: %m", sptuuid);
+        }
+
         errno = 0;
         pl = blkid_probe_get_partitions(b);
         if (!pl)
@@ -586,10 +627,9 @@ static int dissect_image(
                 }
 
                 if (is_gpt) {
-                        PartitionDesignator designator = _PARTITION_DESIGNATOR_INVALID;
-                        Architecture architecture = _ARCHITECTURE_INVALID;
                         const char *stype, *sid, *fstype = NULL, *label;
                         sd_id128_t type_id, id;
+                        GptPartitionType type;
                         bool rw = true, growfs = false;
 
                         sid = blkid_partition_get_uuid(pp);
@@ -604,9 +644,11 @@ static int dissect_image(
                         if (sd_id128_from_string(stype, &type_id) < 0)
                                 continue;
 
+                        type = gpt_partition_type_from_uuid(type_id);
+
                         label = blkid_partition_get_name(pp); /* libblkid returns NULL here if empty */
 
-                        if (sd_id128_equal(type_id, SD_GPT_HOME)) {
+                        if (type.designator == PARTITION_HOME) {
 
                                 check_partition_flags(node, pflags,
                                                       SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
@@ -614,11 +656,10 @@ static int dissect_image(
                                 if (pflags & SD_GPT_FLAG_NO_AUTO)
                                         continue;
 
-                                designator = PARTITION_HOME;
                                 rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
                                 growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
 
-                        } else if (sd_id128_equal(type_id, SD_GPT_SRV)) {
+                        } else if (type.designator == PARTITION_SRV) {
 
                                 check_partition_flags(node, pflags,
                                                       SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
@@ -626,11 +667,10 @@ static int dissect_image(
                                 if (pflags & SD_GPT_FLAG_NO_AUTO)
                                         continue;
 
-                                designator = PARTITION_SRV;
                                 rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
                                 growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
 
-                        } else if (sd_id128_equal(type_id, SD_GPT_ESP)) {
+                        } else if (type.designator == PARTITION_ESP) {
 
                                 /* Note that we don't check the SD_GPT_FLAG_NO_AUTO flag for the ESP, as it is
                                  * not defined there. We instead check the SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL, as
@@ -640,10 +680,9 @@ static int dissect_image(
                                 if (pflags & SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL)
                                         continue;
 
-                                designator = PARTITION_ESP;
                                 fstype = "vfat";
 
-                        } else if (sd_id128_equal(type_id, SD_GPT_XBOOTLDR)) {
+                        } else if (type.designator == PARTITION_XBOOTLDR) {
 
                                 check_partition_flags(node, pflags,
                                                       SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
@@ -651,11 +690,10 @@ static int dissect_image(
                                 if (pflags & SD_GPT_FLAG_NO_AUTO)
                                         continue;
 
-                                designator = PARTITION_XBOOTLDR;
                                 rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
                                 growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
 
-                        } else if (gpt_partition_type_is_root(type_id)) {
+                        } else if (type.designator == PARTITION_ROOT) {
 
                                 check_partition_flags(node, pflags,
                                                       SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
@@ -667,12 +705,10 @@ static int dissect_image(
                                 if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
                                         continue;
 
-                                assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
-                                designator = PARTITION_ROOT_OF_ARCH(architecture);
                                 rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
                                 growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
 
-                        } else if (gpt_partition_type_is_root_verity(type_id)) {
+                        } else if (type.designator == PARTITION_ROOT_VERITY) {
 
                                 check_partition_flags(node, pflags,
                                                       SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
@@ -692,12 +728,10 @@ static int dissect_image(
                                 if (!sd_id128_is_null(root_verity_uuid) && !sd_id128_equal(root_verity_uuid, id))
                                         continue;
 
-                                assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
-                                designator = PARTITION_VERITY_OF(PARTITION_ROOT_OF_ARCH(architecture));
                                 fstype = "DM_verity_hash";
                                 rw = false;
 
-                        } else if (gpt_partition_type_is_root_verity_sig(type_id)) {
+                        } else if (type.designator == PARTITION_ROOT_VERITY_SIG) {
 
                                 check_partition_flags(node, pflags,
                                                       SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
@@ -712,12 +746,10 @@ static int dissect_image(
                                 if (verity->designator >= 0 && verity->designator != PARTITION_ROOT)
                                         continue;
 
-                                assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
-                                designator = PARTITION_VERITY_SIG_OF(PARTITION_ROOT_OF_ARCH(architecture));
                                 fstype = "verity_hash_signature";
                                 rw = false;
 
-                        } else if (gpt_partition_type_is_usr(type_id)) {
+                        } else if (type.designator == PARTITION_USR) {
 
                                 check_partition_flags(node, pflags,
                                                       SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
@@ -729,12 +761,10 @@ static int dissect_image(
                                 if (!sd_id128_is_null(usr_uuid) && !sd_id128_equal(usr_uuid, id))
                                         continue;
 
-                                assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
-                                designator = PARTITION_USR_OF_ARCH(architecture);
                                 rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
                                 growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
 
-                        } else if (gpt_partition_type_is_usr_verity(type_id)) {
+                        } else if (type.designator == PARTITION_USR_VERITY) {
 
                                 check_partition_flags(node, pflags,
                                                       SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
@@ -753,12 +783,10 @@ static int dissect_image(
                                 if (!sd_id128_is_null(usr_verity_uuid) && !sd_id128_equal(usr_verity_uuid, id))
                                         continue;
 
-                                assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
-                                designator = PARTITION_VERITY_OF(PARTITION_USR_OF_ARCH(architecture));
                                 fstype = "DM_verity_hash";
                                 rw = false;
 
-                        } else if (gpt_partition_type_is_usr_verity_sig(type_id)) {
+                        } else if (type.designator == PARTITION_USR_VERITY_SIG) {
 
                                 check_partition_flags(node, pflags,
                                                       SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
@@ -773,21 +801,17 @@ static int dissect_image(
                                 if (verity->designator >= 0 && verity->designator != PARTITION_USR)
                                         continue;
 
-                                assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
-                                designator = PARTITION_VERITY_SIG_OF(PARTITION_USR_OF_ARCH(architecture));
                                 fstype = "verity_hash_signature";
                                 rw = false;
 
-                        } else if (sd_id128_equal(type_id, SD_GPT_SWAP)) {
+                        } else if (type.designator == PARTITION_SWAP) {
 
                                 check_partition_flags(node, pflags, SD_GPT_FLAG_NO_AUTO);
 
                                 if (pflags & SD_GPT_FLAG_NO_AUTO)
                                         continue;
 
-                                designator = PARTITION_SWAP;
-
-                        } else if (sd_id128_equal(type_id, SD_GPT_LINUX_GENERIC)) {
+                        } else if (type.designator == PARTITION_LINUX_GENERIC) {
 
                                 check_partition_flags(node, pflags,
                                                       SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
@@ -807,7 +831,7 @@ static int dissect_image(
                                                 return -ENOMEM;
                                 }
 
-                        } else if (sd_id128_equal(type_id, SD_GPT_TMP)) {
+                        } else if (type.designator == PARTITION_TMP) {
 
                                 check_partition_flags(node, pflags,
                                                       SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
@@ -815,11 +839,10 @@ static int dissect_image(
                                 if (pflags & SD_GPT_FLAG_NO_AUTO)
                                         continue;
 
-                                designator = PARTITION_TMP;
                                 rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
                                 growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
 
-                        } else if (sd_id128_equal(type_id, SD_GPT_VAR)) {
+                        } else if (type.designator == PARTITION_VAR) {
 
                                 check_partition_flags(node, pflags,
                                                       SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
@@ -848,27 +871,29 @@ static int dissect_image(
                                         }
                                 }
 
-                                designator = PARTITION_VAR;
                                 rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
                                 growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
                         }
 
-                        if (designator != _PARTITION_DESIGNATOR_INVALID) {
+                        if (type.designator != _PARTITION_DESIGNATOR_INVALID) {
                                 _cleanup_free_ char *t = NULL, *o = NULL, *l = NULL;
                                 _cleanup_close_ int mount_node_fd = -1;
                                 const char *options = NULL;
 
-                                if (m->partitions[designator].found) {
+                                if (m->partitions[type.designator].found) {
                                         /* For most partition types the first one we see wins. Except for the
                                          * rootfs and /usr, where we do a version compare of the label, and
                                          * let the newest version win. This permits a simple A/B versioning
                                          * scheme in OS images. */
 
-                                        if (!PARTITION_DESIGNATOR_VERSIONED(designator) ||
-                                            strverscmp_improved(m->partitions[designator].label, label) >= 0)
+                                        if (compare_arch(type.arch, m->partitions[type.designator].architecture) <= 0)
+                                                continue;
+
+                                        if (!partition_designator_is_versioned(type.designator) ||
+                                            strverscmp_improved(m->partitions[type.designator].label, label) >= 0)
                                                 continue;
 
-                                        dissected_partition_done(m->partitions + designator);
+                                        dissected_partition_done(m->partitions + type.designator);
                                 }
 
                                 if (FLAGS_SET(flags, DISSECT_IMAGE_OPEN_PARTITION_DEVICES)) {
@@ -889,19 +914,19 @@ static int dissect_image(
                                                 return -ENOMEM;
                                 }
 
-                                options = mount_options_from_designator(mount_options, designator);
+                                options = mount_options_from_designator(mount_options, type.designator);
                                 if (options) {
                                         o = strdup(options);
                                         if (!o)
                                                 return -ENOMEM;
                                 }
 
-                                m->partitions[designator] = (DissectedPartition) {
+                                m->partitions[type.designator] = (DissectedPartition) {
                                         .found = true,
                                         .partno = nr,
                                         .rw = rw,
                                         .growfs = growfs,
-                                        .architecture = architecture,
+                                        .architecture = type.arch,
                                         .node = TAKE_PTR(node),
                                         .fstype = TAKE_PTR(t),
                                         .label = TAKE_PTR(l),
@@ -981,139 +1006,21 @@ static int dissect_image(
                 }
         }
 
-        if (m->partitions[PARTITION_ROOT].found) {
-                /* If we found the primary arch, then invalidate the secondary and other arch to avoid any
-                 * ambiguities, since we never want to mount the secondary or other arch in this case. */
-                m->partitions[PARTITION_ROOT_SECONDARY].found = false;
-                m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found = false;
-                m->partitions[PARTITION_ROOT_SECONDARY_VERITY_SIG].found = false;
-                m->partitions[PARTITION_USR_SECONDARY].found = false;
-                m->partitions[PARTITION_USR_SECONDARY_VERITY].found = false;
-                m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG].found = false;
-
-                m->partitions[PARTITION_ROOT_OTHER].found = false;
-                m->partitions[PARTITION_ROOT_OTHER_VERITY].found = false;
-                m->partitions[PARTITION_ROOT_OTHER_VERITY_SIG].found = false;
-                m->partitions[PARTITION_USR_OTHER].found = false;
-                m->partitions[PARTITION_USR_OTHER_VERITY].found = false;
-                m->partitions[PARTITION_USR_OTHER_VERITY_SIG].found = false;
-
-        } else if (m->partitions[PARTITION_ROOT_VERITY].found ||
-                   m->partitions[PARTITION_ROOT_VERITY_SIG].found)
-                return -EADDRNOTAVAIL; /* Verity found but no matching rootfs? Something is off, refuse. */
-
-        else if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
-
-                /* No root partition found but there's one for the secondary architecture? Then upgrade
-                 * secondary arch to first and invalidate the other arch. */
-
-                log_debug("No root partition found of the native architecture, falling back to a root "
-                          "partition of the secondary architecture.");
-
-                m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
-                zero(m->partitions[PARTITION_ROOT_SECONDARY]);
-                m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
-                zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
-                m->partitions[PARTITION_ROOT_VERITY_SIG] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY_SIG];
-                zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY_SIG]);
-
-                m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_SECONDARY];
-                zero(m->partitions[PARTITION_USR_SECONDARY]);
-                m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY];
-                zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
-                m->partitions[PARTITION_USR_VERITY_SIG] = m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG];
-                zero(m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG]);
-
-                m->partitions[PARTITION_ROOT_OTHER].found = false;
-                m->partitions[PARTITION_ROOT_OTHER_VERITY].found = false;
-                m->partitions[PARTITION_ROOT_OTHER_VERITY_SIG].found = false;
-                m->partitions[PARTITION_USR_OTHER].found = false;
-                m->partitions[PARTITION_USR_OTHER_VERITY].found = false;
-                m->partitions[PARTITION_USR_OTHER_VERITY_SIG].found = false;
-
-        } else if (m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found ||
-                   m->partitions[PARTITION_ROOT_SECONDARY_VERITY_SIG].found)
-                return -EADDRNOTAVAIL; /* as above */
-
-        else if (m->partitions[PARTITION_ROOT_OTHER].found) {
-
-                /* No root or secondary partition found but there's one for another architecture? Then
-                 * upgrade the other architecture to first. */
-
-                log_debug("No root partition found of the native architecture or the secondary architecture, "
-                          "falling back to a root partition of a non-native architecture (%s).",
-                          architecture_to_string(m->partitions[PARTITION_ROOT_OTHER].architecture));
-
-                m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_OTHER];
-                zero(m->partitions[PARTITION_ROOT_OTHER]);
-                m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_OTHER_VERITY];
-                zero(m->partitions[PARTITION_ROOT_OTHER_VERITY]);
-                m->partitions[PARTITION_ROOT_VERITY_SIG] = m->partitions[PARTITION_ROOT_OTHER_VERITY_SIG];
-                zero(m->partitions[PARTITION_ROOT_OTHER_VERITY_SIG]);
-
-                m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_OTHER];
-                zero(m->partitions[PARTITION_USR_OTHER]);
-                m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_OTHER_VERITY];
-                zero(m->partitions[PARTITION_USR_OTHER_VERITY]);
-                m->partitions[PARTITION_USR_VERITY_SIG] = m->partitions[PARTITION_USR_OTHER_VERITY_SIG];
-                zero(m->partitions[PARTITION_USR_OTHER_VERITY_SIG]);
-        }
+        if (!m->partitions[PARTITION_ROOT].found &&
+                (m->partitions[PARTITION_ROOT_VERITY].found ||
+                 m->partitions[PARTITION_ROOT_VERITY_SIG].found))
+                        return -EADDRNOTAVAIL; /* Verity found but no matching rootfs? Something is off, refuse. */
 
         /* Hmm, we found a signature partition but no Verity data? Something is off. */
         if (m->partitions[PARTITION_ROOT_VERITY_SIG].found && !m->partitions[PARTITION_ROOT_VERITY].found)
                 return -EADDRNOTAVAIL;
 
-        if (m->partitions[PARTITION_USR].found) {
-                /* Invalidate secondary and other arch /usr/ if we found the primary arch */
-                m->partitions[PARTITION_USR_SECONDARY].found = false;
-                m->partitions[PARTITION_USR_SECONDARY_VERITY].found = false;
-                m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG].found = false;
-
-                m->partitions[PARTITION_USR_OTHER].found = false;
-                m->partitions[PARTITION_USR_OTHER_VERITY].found = false;
-                m->partitions[PARTITION_USR_OTHER_VERITY_SIG].found = false;
-
-        } else if (m->partitions[PARTITION_USR_VERITY].found ||
-                   m->partitions[PARTITION_USR_VERITY_SIG].found)
-                return -EADDRNOTAVAIL; /* as above */
-
-        else if (m->partitions[PARTITION_USR_SECONDARY].found) {
-
-                log_debug("No usr partition found of the native architecture, falling back to a usr "
-                          "partition of the secondary architecture.");
-
-                /* Upgrade secondary arch to primary */
-                m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_SECONDARY];
-                zero(m->partitions[PARTITION_USR_SECONDARY]);
-                m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY];
-                zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
-                m->partitions[PARTITION_USR_VERITY_SIG] = m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG];
-                zero(m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG]);
-
-                m->partitions[PARTITION_USR_OTHER].found = false;
-                m->partitions[PARTITION_USR_OTHER_VERITY].found = false;
-                m->partitions[PARTITION_USR_OTHER_VERITY_SIG].found = false;
-
-        } else if (m->partitions[PARTITION_USR_SECONDARY_VERITY].found ||
-                   m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG].found)
-                return -EADDRNOTAVAIL; /* as above */
-
-        else if (m->partitions[PARTITION_USR_OTHER].found) {
-
-                log_debug("No usr partition found of the native architecture or the secondary architecture, "
-                          "falling back to a usr partition of a non-native architecture (%s).",
-                          architecture_to_string(m->partitions[PARTITION_ROOT_OTHER].architecture));
-
-                /* Upgrade other arch to primary */
-                m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_OTHER];
-                zero(m->partitions[PARTITION_USR_OTHER]);
-                m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_OTHER_VERITY];
-                zero(m->partitions[PARTITION_USR_OTHER_VERITY]);
-                m->partitions[PARTITION_USR_VERITY_SIG] = m->partitions[PARTITION_USR_OTHER_VERITY_SIG];
-                zero(m->partitions[PARTITION_USR_OTHER_VERITY_SIG]);
-        }
+        if (!m->partitions[PARTITION_USR].found &&
+                (m->partitions[PARTITION_USR_VERITY].found ||
+                 m->partitions[PARTITION_USR_VERITY_SIG].found))
+                        return -EADDRNOTAVAIL; /* as above */
 
-        /* Hmm, we found a signature partition but no Verity data? Something is off. */
+        /* as above */
         if (m->partitions[PARTITION_USR_VERITY_SIG].found && !m->partitions[PARTITION_USR_VERITY].found)
                 return -EADDRNOTAVAIL;
 
@@ -2349,7 +2256,7 @@ int dissected_image_decrypt(
                 if (r < 0)
                         return r;
 
-                k = PARTITION_VERITY_OF(i);
+                k = partition_verity_of(i);
                 if (k >= 0) {
                         r = verity_partition(i, p, m->partitions + k, verity, flags | DISSECT_IMAGE_VERITY_SHARE, d);
                         if (r < 0)
@@ -2691,7 +2598,7 @@ int dissected_image_load_verity_sig_partition(
         if (r == 0)
                 return 0;
 
-        d = PARTITION_VERITY_SIG_OF(verity->designator < 0 ? PARTITION_ROOT : verity->designator);
+        d = partition_verity_sig_of(verity->designator < 0 ? PARTITION_ROOT : verity->designator);
         assert(d >= 0);
 
         p = m->partitions + d;
@@ -2849,7 +2756,6 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_
 
                 for (unsigned k = 0; k < _META_MAX; k++) {
                         _cleanup_close_ int fd = -ENOENT;
-                        const char *p;
 
                         if (!paths[k])
                                 continue;
@@ -3150,7 +3056,7 @@ bool dissected_image_verity_candidate(const DissectedImage *image, PartitionDesi
         if (image->single_file_system)
                 return partition_designator == PARTITION_ROOT && image->has_verity;
 
-        return PARTITION_VERITY_OF(partition_designator) >= 0;
+        return partition_verity_of(partition_designator) >= 0;
 }
 
 bool dissected_image_verity_ready(const DissectedImage *image, PartitionDesignator partition_designator) {
@@ -3167,7 +3073,7 @@ bool dissected_image_verity_ready(const DissectedImage *image, PartitionDesignat
         if (image->single_file_system)
                 return partition_designator == PARTITION_ROOT;
 
-        k = PARTITION_VERITY_OF(partition_designator);
+        k = partition_verity_of(partition_designator);
         return k >= 0 && image->partitions[k].found;
 }
 
@@ -3184,7 +3090,7 @@ bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesi
         if (image->single_file_system)
                 return partition_designator == PARTITION_ROOT;
 
-        k = PARTITION_VERITY_SIG_OF(partition_designator);
+        k = partition_verity_sig_of(partition_designator);
         return k >= 0 && image->partitions[k].found;
 }
 
@@ -3286,34 +3192,6 @@ int mount_image_privately_interactively(
         return 0;
 }
 
-static const char *const partition_designator_table[] = {
-        [PARTITION_ROOT]                      = "root",
-        [PARTITION_ROOT_SECONDARY]            = "root-secondary",
-        [PARTITION_ROOT_OTHER]                = "root-other",
-        [PARTITION_USR]                       = "usr",
-        [PARTITION_USR_SECONDARY]             = "usr-secondary",
-        [PARTITION_USR_OTHER]                 = "usr-other",
-        [PARTITION_HOME]                      = "home",
-        [PARTITION_SRV]                       = "srv",
-        [PARTITION_ESP]                       = "esp",
-        [PARTITION_XBOOTLDR]                  = "xbootldr",
-        [PARTITION_SWAP]                      = "swap",
-        [PARTITION_ROOT_VERITY]               = "root-verity",
-        [PARTITION_ROOT_SECONDARY_VERITY]     = "root-secondary-verity",
-        [PARTITION_ROOT_OTHER_VERITY]         = "root-other-verity",
-        [PARTITION_USR_VERITY]                = "usr-verity",
-        [PARTITION_USR_SECONDARY_VERITY]      = "usr-secondary-verity",
-        [PARTITION_USR_OTHER_VERITY]          = "usr-other-verity",
-        [PARTITION_ROOT_VERITY_SIG]           = "root-verity-sig",
-        [PARTITION_ROOT_SECONDARY_VERITY_SIG] = "root-secondary-verity-sig",
-        [PARTITION_ROOT_OTHER_VERITY_SIG]     = "root-other-verity-sig",
-        [PARTITION_USR_VERITY_SIG]            = "usr-verity-sig",
-        [PARTITION_USR_SECONDARY_VERITY_SIG]  = "usr-secondary-verity-sig",
-        [PARTITION_USR_OTHER_VERITY_SIG]      = "usr-other-verity-sig",
-        [PARTITION_TMP]                       = "tmp",
-        [PARTITION_VAR]                       = "var",
-};
-
 static bool mount_options_relax_extension_release_checks(const MountOptions *options) {
         if (!options)
                 return false;
@@ -3441,5 +3319,3 @@ int verity_dissect_and_mount(
 
         return 0;
 }
-
-DEFINE_STRING_TABLE_LOOKUP(partition_designator, PartitionDesignator);
index 46675d22ab3a981f47ee810241560d4b095700b6..1a398010b53b9d9210c9fb8e4ea8336d12daa22f 100644 (file)
@@ -6,6 +6,7 @@
 #include "sd-id128.h"
 
 #include "architecture.h"
+#include "gpt.h"
 #include "list.h"
 #include "loop-util.h"
 #include "macro.h"
@@ -40,147 +41,12 @@ struct DissectedPartition {
                 .architecture = _ARCHITECTURE_INVALID,                  \
                 .mount_node_fd = -1,                                    \
         })
-
-typedef enum PartitionDesignator {
-        PARTITION_ROOT,
-        PARTITION_ROOT_SECONDARY,  /* Secondary architecture */
-        PARTITION_ROOT_OTHER,
-        PARTITION_USR,
-        PARTITION_USR_SECONDARY,
-        PARTITION_USR_OTHER,
-        PARTITION_HOME,
-        PARTITION_SRV,
-        PARTITION_ESP,
-        PARTITION_XBOOTLDR,
-        PARTITION_SWAP,
-        PARTITION_ROOT_VERITY, /* verity data for the PARTITION_ROOT partition */
-        PARTITION_ROOT_SECONDARY_VERITY, /* verity data for the PARTITION_ROOT_SECONDARY partition */
-        PARTITION_ROOT_OTHER_VERITY,
-        PARTITION_USR_VERITY,
-        PARTITION_USR_SECONDARY_VERITY,
-        PARTITION_USR_OTHER_VERITY,
-        PARTITION_ROOT_VERITY_SIG, /* PKCS#7 signature for root hash for the PARTITION_ROOT partition */
-        PARTITION_ROOT_SECONDARY_VERITY_SIG, /* ditto for the PARTITION_ROOT_SECONDARY partition */
-        PARTITION_ROOT_OTHER_VERITY_SIG,
-        PARTITION_USR_VERITY_SIG,
-        PARTITION_USR_SECONDARY_VERITY_SIG,
-        PARTITION_USR_OTHER_VERITY_SIG,
-        PARTITION_TMP,
-        PARTITION_VAR,
-        _PARTITION_DESIGNATOR_MAX,
-        _PARTITION_DESIGNATOR_INVALID = -EINVAL,
-} PartitionDesignator;
-
-static inline bool PARTITION_DESIGNATOR_VERSIONED(PartitionDesignator d) {
-        /* Returns true for all designators where we want to support a concept of "versioning", i.e. which
-         * likely contain software binaries (or hashes thereof) that make sense to be versioned as a
-         * whole. We use this check to automatically pick the newest version of these partitions, by version
-         * comparing the partition labels. */
-
-        return IN_SET(d,
-                      PARTITION_ROOT,
-                      PARTITION_ROOT_SECONDARY,
-                      PARTITION_ROOT_OTHER,
-                      PARTITION_USR,
-                      PARTITION_USR_SECONDARY,
-                      PARTITION_USR_OTHER,
-                      PARTITION_ROOT_VERITY,
-                      PARTITION_ROOT_SECONDARY_VERITY,
-                      PARTITION_ROOT_OTHER_VERITY,
-                      PARTITION_USR_VERITY,
-                      PARTITION_USR_SECONDARY_VERITY,
-                      PARTITION_USR_OTHER_VERITY,
-                      PARTITION_ROOT_VERITY_SIG,
-                      PARTITION_ROOT_SECONDARY_VERITY_SIG,
-                      PARTITION_ROOT_OTHER_VERITY_SIG,
-                      PARTITION_USR_VERITY_SIG,
-                      PARTITION_USR_SECONDARY_VERITY_SIG,
-                      PARTITION_USR_OTHER_VERITY_SIG);
-}
-
-static inline PartitionDesignator PARTITION_VERITY_OF(PartitionDesignator p) {
-        switch (p) {
-
-        case PARTITION_ROOT:
-                return PARTITION_ROOT_VERITY;
-
-        case PARTITION_ROOT_SECONDARY:
-                return PARTITION_ROOT_SECONDARY_VERITY;
-
-        case PARTITION_ROOT_OTHER:
-                return PARTITION_ROOT_OTHER_VERITY;
-
-        case PARTITION_USR:
-                return PARTITION_USR_VERITY;
-
-        case PARTITION_USR_SECONDARY:
-                return PARTITION_USR_SECONDARY_VERITY;
-
-        case PARTITION_USR_OTHER:
-                return PARTITION_USR_OTHER_VERITY;
-
-        default:
-                return _PARTITION_DESIGNATOR_INVALID;
-        }
-}
-
-static inline PartitionDesignator PARTITION_VERITY_SIG_OF(PartitionDesignator p) {
-        switch (p) {
-
-        case PARTITION_ROOT:
-                return PARTITION_ROOT_VERITY_SIG;
-
-        case PARTITION_ROOT_SECONDARY:
-                return PARTITION_ROOT_SECONDARY_VERITY_SIG;
-
-        case PARTITION_ROOT_OTHER:
-                return PARTITION_ROOT_OTHER_VERITY_SIG;
-
-        case PARTITION_USR:
-                return PARTITION_USR_VERITY_SIG;
-
-        case PARTITION_USR_SECONDARY:
-                return PARTITION_USR_SECONDARY_VERITY_SIG;
-
-        case PARTITION_USR_OTHER:
-                return PARTITION_USR_OTHER_VERITY_SIG;
-
-        default:
-                return _PARTITION_DESIGNATOR_INVALID;
-        }
-}
-
-static inline PartitionDesignator PARTITION_ROOT_OF_ARCH(Architecture arch) {
-        switch (arch) {
-
-        case native_architecture():
-                return PARTITION_ROOT;
-
-#ifdef ARCHITECTURE_SECONDARY
-        case ARCHITECTURE_SECONDARY:
-                return PARTITION_ROOT_SECONDARY;
-#endif
-
-        default:
-                return PARTITION_ROOT_OTHER;
-        }
-}
-
-static inline PartitionDesignator PARTITION_USR_OF_ARCH(Architecture arch) {
-        switch (arch) {
-
-        case native_architecture():
-                return PARTITION_USR;
-
-#ifdef ARCHITECTURE_SECONDARY
-        case ARCHITECTURE_SECONDARY:
-                return PARTITION_USR_SECONDARY;
-#endif
-
-        default:
-                return PARTITION_USR_OTHER;
-        }
-}
+#define TAKE_PARTITION(p)                                       \
+        ({                                                      \
+                DissectedPartition *_pp = &(p), _p = *_pp;      \
+                *_pp = DISSECTED_PARTITION_NULL;                \
+                _p;                                             \
+        })
 
 typedef enum DissectImageFlags {
         DISSECT_IMAGE_DEVICE_READ_ONLY         = 1 << 0,  /* Make device read-only */
@@ -229,6 +95,7 @@ struct DissectedImage {
 
         /* Meta information extracted from /etc/os-release and similar */
         char *image_name;
+        sd_id128_t image_uuid;
         char *hostname;
         sd_id128_t machine_id;
         char **machine_info;
@@ -297,9 +164,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DecryptedImage*, decrypted_image_unref);
 
 int dissected_image_relinquish(DissectedImage *m);
 
-const char* partition_designator_to_string(PartitionDesignator d) _const_;
-PartitionDesignator partition_designator_from_string(const char *name) _pure_;
-
 int verity_settings_load(VeritySettings *verity, const char *image, const char *root_hash_path, const char *root_hash_sig_path);
 void verity_settings_done(VeritySettings *verity);
 
index dfe0574aba6b16010662ade9fc0842b61320e43f..fa234c8b5fe96ddb92f52d2710f6020779c2066c 100644 (file)
@@ -165,59 +165,65 @@ static int verify_esp_udev(
 
         r = sd_device_get_devname(d, &node);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device node: %m");
+                return log_device_error_errno(d, r, "Failed to get device node: %m");
 
         r = sd_device_get_property_value(d, "ID_FS_TYPE", &v);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
+                return log_device_error_errno(d, r, "Failed to get device property: %m");
         if (!streq(v, "vfat"))
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                      "File system \"%s\" is not FAT.", node );
+                return log_device_full_errno(d,
+                                             searching ? LOG_DEBUG : LOG_ERR,
+                                             SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                             "File system \"%s\" is not FAT.", node );
 
         r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
+                return log_device_full_errno(d,
+                                             searching && r == -ENOENT ? LOG_DEBUG : LOG_ERR,
+                                             searching && r == -ENOENT ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : r,
+                                             "Failed to get device property: %m");
         if (!streq(v, "gpt"))
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                      "File system \"%s\" is not on a GPT partition table.", node);
+                return log_device_full_errno(d,
+                                             searching ? LOG_DEBUG : LOG_ERR,
+                                             SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                             "File system \"%s\" is not on a GPT partition table.", node);
 
         r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
+                return log_device_error_errno(d, r, "Failed to get device property: %m");
         if (sd_id128_string_equal(v, SD_GPT_ESP) <= 0)
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                       "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
+                return log_device_full_errno(d,
+                                             searching ? LOG_DEBUG : LOG_ERR,
+                                             SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                             "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
 
         r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
+                return log_device_error_errno(d, r, "Failed to get device property: %m");
         r = sd_id128_from_string(v, &uuid);
         if (r < 0)
-                return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
+                return log_device_error_errno(d, r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
 
         r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
+                return log_device_error_errno(d, r, "Failed to get device property: %m");
         r = safe_atou32(v, &part);
         if (r < 0)
-                return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
+                return log_device_error_errno(d, r, "Failed to parse PART_ENTRY_NUMBER field.");
 
         r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
+                return log_device_error_errno(d, r, "Failed to get device property: %m");
         r = safe_atou64(v, &pstart);
         if (r < 0)
-                return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
+                return log_device_error_errno(d, r, "Failed to parse PART_ENTRY_OFFSET field.");
 
         r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
+                return log_device_error_errno(d, r, "Failed to get device property: %m");
         r = safe_atou64(v, &psize);
         if (r < 0)
-                return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
+                return log_device_error_errno(d, r, "Failed to parse PART_ENTRY_SIZE field.");
 
         if (ret_part)
                 *ret_part = part;
@@ -572,10 +578,11 @@ static int verify_xbootldr_blkid(
         else if (r != 0)
                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "%s: Failed to probe file system: %m", node);
 
-        errno = 0;
         r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &type, NULL);
         if (r != 0)
-                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "%s: Failed to probe PART_ENTRY_SCHEME: %m", node);
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                      searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(EIO),
+                                      "%s: Failed to probe PART_ENTRY_SCHEME: %m", node);
         if (streq(type, "gpt")) {
 
                 errno = 0;
@@ -634,11 +641,14 @@ static int verify_xbootldr_udev(
 
         r = sd_device_get_devname(d, &node);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device node: %m");
+                return log_device_error_errno(d, r, "Failed to get device node: %m");
 
         r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &type);
         if (r < 0)
-                return log_device_error_errno(d, r, "Failed to query ID_PART_ENTRY_SCHEME: %m");
+                return log_device_full_errno(d,
+                                             searching && r == -ENOENT ? LOG_DEBUG : LOG_ERR,
+                                             searching && r == -ENOENT ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : r,
+                                             "Failed to query ID_PART_ENTRY_SCHEME: %m");
 
         if (streq(type, "gpt")) {
 
index 0549c9f12ef1153693954af5eecd71cbcfcdbbf8..1c1db2f43a97bcbea4be85d4624ddeaee8dd6375 100644 (file)
@@ -132,6 +132,8 @@ struct Table {
         size_t n_cells;
 
         bool header;   /* Whether to show the header row? */
+        bool vertical; /* Whether to field names are on the left rather than the first line */
+
         TableErsatz ersatz; /* What to show when we have an empty cell or an invalid value that cannot be rendered. */
 
         size_t width;  /* If == 0 format this as wide as necessary. If SIZE_MAX format this to console
@@ -197,14 +199,7 @@ Table *table_new_internal(const char *first_header, ...) {
         for (const char *h = first_header; h; h = va_arg(ap, const char*)) {
                 TableCell *cell;
 
-                r = table_add_cell(t, &cell, TABLE_STRING, h);
-                if (r < 0) {
-                        va_end(ap);
-                        return NULL;
-                }
-
-                /* Make the table header uppercase */
-                r = table_set_uppercase(t, cell, true);
+                r = table_add_cell(t, &cell, TABLE_HEADER, h);
                 if (r < 0) {
                         va_end(ap);
                         return NULL;
@@ -216,6 +211,32 @@ Table *table_new_internal(const char *first_header, ...) {
         return TAKE_PTR(t);
 }
 
+Table *table_new_vertical(void) {
+        _cleanup_(table_unrefp) Table *t = NULL;
+        TableCell *cell;
+
+        t = table_new_raw(2);
+        if (!t)
+                return NULL;
+
+        t->vertical = true;
+        t->header = false;
+
+        if (table_add_cell(t, &cell, TABLE_HEADER, "key") < 0)
+                return NULL;
+
+        if (table_set_align_percent(t, cell, 100) < 0)
+                return NULL;
+
+        if (table_add_cell(t, &cell, TABLE_HEADER, "value") < 0)
+                return NULL;
+
+        if (table_set_align_percent(t, cell, 0) < 0)
+                return NULL;
+
+        return TAKE_PTR(t);
+}
+
 static TableData *table_data_free(TableData *d) {
         assert(d);
 
@@ -260,6 +281,8 @@ static size_t table_data_size(TableDataType type, const void *data) {
 
         case TABLE_STRING:
         case TABLE_PATH:
+        case TABLE_FIELD:
+        case TABLE_HEADER:
                 return strlen(data) + 1;
 
         case TABLE_STRV:
@@ -336,7 +359,8 @@ static bool table_data_matches(
                 size_t maximum_width,
                 unsigned weight,
                 unsigned align_percent,
-                unsigned ellipsize_percent) {
+                unsigned ellipsize_percent,
+                bool uppercase) {
 
         size_t k, l;
         assert(d);
@@ -359,13 +383,14 @@ static bool table_data_matches(
         if (d->ellipsize_percent != ellipsize_percent)
                 return false;
 
-        /* If a color/url/uppercase flag is set, refuse to merge */
+        if (d->uppercase != uppercase)
+                return false;
+
+        /* If a color/url is set, refuse to merge */
         if (d->color || d->rgap_color)
                 return false;
         if (d->url)
                 return false;
-        if (d->uppercase)
-                return false;
 
         k = table_data_size(type, data);
         l = table_data_size(d->type, d->data);
@@ -382,7 +407,8 @@ static TableData *table_data_new(
                 size_t maximum_width,
                 unsigned weight,
                 unsigned align_percent,
-                unsigned ellipsize_percent) {
+                unsigned ellipsize_percent,
+                bool uppercase) {
 
         _cleanup_free_ TableData *d = NULL;
         size_t data_size;
@@ -400,6 +426,7 @@ static TableData *table_data_new(
         d->weight = weight;
         d->align_percent = align_percent;
         d->ellipsize_percent = ellipsize_percent;
+        d->uppercase = uppercase;
 
         if (IN_SET(type, TABLE_STRV, TABLE_STRV_WRAPPED)) {
                 d->strv = strv_copy(data);
@@ -423,6 +450,7 @@ int table_add_cell_full(
                 unsigned ellipsize_percent) {
 
         _cleanup_(table_data_unrefp) TableData *d = NULL;
+        bool uppercase;
         TableData *p;
 
         assert(t);
@@ -455,13 +483,15 @@ int table_add_cell_full(
         assert(align_percent <= 100);
         assert(ellipsize_percent <= 100);
 
+        uppercase = type == TABLE_HEADER;
+
         /* Small optimization: Pretty often adjacent cells in two subsequent lines have the same data and
          * formatting. Let's see if we can reuse the cell data and ref it once more. */
 
-        if (p && table_data_matches(p, type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent))
+        if (p && table_data_matches(p, type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent, uppercase))
                 d = table_data_ref(p);
         else {
-                d = table_data_new(type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent);
+                d = table_data_new(type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent, uppercase);
                 if (!d)
                         return -ENOMEM;
         }
@@ -477,18 +507,21 @@ int table_add_cell_full(
         return 0;
 }
 
-int table_add_cell_stringf(Table *t, TableCell **ret_cell, const char *format, ...) {
+int table_add_cell_stringf_full(Table *t, TableCell **ret_cell, TableDataType dt, const char *format, ...) {
         _cleanup_free_ char *buffer = NULL;
         va_list ap;
         int r;
 
+        assert(t);
+        assert(IN_SET(dt, TABLE_STRING, TABLE_PATH, TABLE_FIELD, TABLE_HEADER));
+
         va_start(ap, format);
         r = vasprintf(&buffer, format, ap);
         va_end(ap);
         if (r < 0)
                 return -ENOMEM;
 
-        return table_add_cell(t, ret_cell, TABLE_STRING, buffer);
+        return table_add_cell(t, ret_cell, dt, buffer);
 }
 
 int table_fill_empty(Table *t, size_t until_column) {
@@ -563,14 +596,14 @@ static int table_dedup_cell(Table *t, TableCell *cell) {
                         od->maximum_width,
                         od->weight,
                         od->align_percent,
-                        od->ellipsize_percent);
+                        od->ellipsize_percent,
+                        od->uppercase);
         if (!nd)
                 return -ENOMEM;
 
         nd->color = od->color;
         nd->rgap_color = od->rgap_color;
         nd->url = TAKE_PTR(curl);
-        nd->uppercase = od->uppercase;
 
         table_data_unref(od);
         t->data[i] = nd;
@@ -780,14 +813,14 @@ int table_update(Table *t, TableCell *cell, TableDataType type, const void *data
                         od->maximum_width,
                         od->weight,
                         od->align_percent,
-                        od->ellipsize_percent);
+                        od->ellipsize_percent,
+                        od->uppercase);
         if (!nd)
                 return -ENOMEM;
 
         nd->color = od->color;
         nd->rgap_color = od->rgap_color;
         nd->url = TAKE_PTR(curl);
-        nd->uppercase = od->uppercase;
 
         table_data_unref(od);
         t->data[i] = nd;
@@ -840,6 +873,8 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
 
                 case TABLE_STRING:
                 case TABLE_PATH:
+                case TABLE_FIELD:
+                case TABLE_HEADER:
                         data = va_arg(ap, const char *);
                         break;
 
@@ -1241,6 +1276,8 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
                 switch (a->type) {
 
                 case TABLE_STRING:
+                case TABLE_FIELD:
+                case TABLE_HEADER:
                         return strcmp(a->string, b->string);
 
                 case TABLE_PATH:
@@ -1416,15 +1453,26 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
 
         case TABLE_STRING:
         case TABLE_PATH:
+        case TABLE_FIELD:
+        case TABLE_HEADER:
                 if (d->uppercase && !avoid_uppercasing) {
-                        d->formatted = new(char, strlen(d->string) + 1);
+                        d->formatted = new(char, strlen(d->string) + (d->type == TABLE_FIELD) + 1);
                         if (!d->formatted)
                                 return NULL;
 
                         char *q = d->formatted;
-                        for (char *p = d->string; *p; p++, q++)
-                                *q = (char) toupper((unsigned char) *p);
+                        for (char *p = d->string; *p; p++)
+                                *(q++) = (char) toupper((unsigned char) *p);
+
+                        if (d->type == TABLE_FIELD)
+                                *(q++) = ':';
+
                         *q = 0;
+                        return d->formatted;
+                } else if (d->type == TABLE_FIELD) {
+                        d->formatted = strjoin(d->string, ":");
+                        if (!d->formatted)
+                                return NULL;
 
                         return d->formatted;
                 }
@@ -1982,6 +2030,11 @@ static const char* table_data_color(TableData *d) {
         if (table_data_isempty(d))
                 return ansi_grey();
 
+        if (d->type == TABLE_FIELD)
+                return ansi_bright_blue();
+        if (d->type == TABLE_HEADER)
+                return ansi_underline();
+
         return NULL;
 }
 
@@ -1991,6 +2044,9 @@ static const char* table_data_rgap_color(TableData *d) {
         if (d->rgap_color)
                 return d->rgap_color;
 
+        if (d->type == TABLE_HEADER)
+                return ansi_underline();
+
         return NULL;
 }
 
@@ -2335,7 +2391,7 @@ int table_print(Table *t, FILE *f) {
 
                                                 /* Drop trailing white spaces of last column when no cosmetics is set. */
                                                 if (j == display_columns - 1 &&
-                                                    (!colors_enabled() || (!table_data_color(d) && row != t->data)) &&
+                                                    (!colors_enabled() || !table_data_color(d)) &&
                                                     (!urlify_enabled() || !d->url))
                                                         delete_trailing_chars(aligned, NULL);
 
@@ -2355,12 +2411,8 @@ int table_print(Table *t, FILE *f) {
                                         field = buffer;
                                 }
 
-                                if (colors_enabled()) {
-                                        if (gap_color)
-                                                fputs(gap_color, f);
-                                        else if (row == t->data) /* underline header line fully, including the column separator */
-                                                fputs(ansi_underline(), f);
-                                }
+                                if (colors_enabled() && gap_color)
+                                        fputs(gap_color, f);
 
                                 if (j > 0)
                                         fputc(' ', f); /* column separator left of cell */
@@ -2369,18 +2421,16 @@ int table_print(Table *t, FILE *f) {
                                         color = table_data_color(d);
 
                                         /* Undo gap color */
-                                        if (gap_color || (color && row == t->data))
+                                        if (gap_color)
                                                 fputs(ANSI_NORMAL, f);
 
                                         if (color)
                                                 fputs(color, f);
-                                        else if (gap_color && row == t->data) /* underline header line cell */
-                                                fputs(ansi_underline(), f);
                                 }
 
                                 fputs(field, f);
 
-                                if (colors_enabled() && (color || row == t->data))
+                                if (colors_enabled() && color)
                                         fputs(ANSI_NORMAL, f);
 
                                 gap_color = table_data_rgap_color(d);
@@ -2494,6 +2544,8 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
 
         case TABLE_STRING:
         case TABLE_PATH:
+        case TABLE_FIELD:
+        case TABLE_HEADER:
                 return json_variant_new_string(ret, d->string);
 
         case TABLE_STRV:
@@ -2573,10 +2625,10 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
                 return json_variant_new_array_bytes(ret, &d->address, FAMILY_ADDRESS_SIZE(AF_INET6));
 
         case TABLE_ID128:
-                return json_variant_new_string(ret, SD_ID128_TO_STRING(d->id128));
+                return json_variant_new_id128(ret, d->id128);
 
         case TABLE_UUID:
-                return json_variant_new_string(ret, SD_ID128_TO_UUID_STRING(d->id128));
+                return json_variant_new_uuid(ret, d->id128);
 
         case TABLE_UID:
                 if (!uid_is_valid(d->uid))
@@ -2629,21 +2681,47 @@ static char* string_to_json_field_name(const char *f) {
         return c;
 }
 
-static const char *table_get_json_field_name(Table *t, size_t column) {
+static int table_make_json_field_name(Table *t, TableData *d, char **ret) {
+        _cleanup_free_ char *mangled = NULL;
+        const char *n;
+
         assert(t);
+        assert(d);
+        assert(ret);
 
-        return column < t->n_json_fields ? t->json_fields[column] : NULL;
+        if (IN_SET(d->type, TABLE_HEADER, TABLE_FIELD))
+                n = d->string;
+        else {
+                n = table_data_format(t, d, /* avoid_uppercasing= */ true, SIZE_MAX, NULL);
+                if (!n)
+                        return -ENOMEM;
+        }
+
+        mangled = string_to_json_field_name(n);
+        if (!mangled)
+                return -ENOMEM;
+
+        *ret = TAKE_PTR(mangled);
+        return 0;
 }
 
-int table_to_json(Table *t, JsonVariant **ret) {
+static const char *table_get_json_field_name(Table *t, size_t idx) {
+        assert(t);
+
+        return idx < t->n_json_fields ? t->json_fields[idx] : NULL;
+}
+
+static int table_to_json_regular(Table *t, JsonVariant **ret) {
         JsonVariant **rows = NULL, **elements = NULL;
         _cleanup_free_ size_t *sorted = NULL;
         size_t n_rows, display_columns;
         int r;
 
         assert(t);
+        assert(!t->vertical);
 
         /* Ensure we have no incomplete rows */
+        assert(t->n_columns > 0);
         assert(t->n_cells % t->n_columns == 0);
 
         n_rows = t->n_cells / t->n_columns;
@@ -2686,24 +2764,10 @@ int table_to_json(Table *t, JsonVariant **ret) {
                 /* Use explicitly set JSON field name, if we have one. Otherwise mangle the column field value. */
                 n = table_get_json_field_name(t, c);
                 if (!n) {
-                        const char *formatted;
-                        TableData *d;
-
-                        assert_se(d = t->data[c]);
-
-                        /* Field names must be strings, hence format whatever we got here as a string first */
-                        formatted = table_data_format(t, d, true, SIZE_MAX, NULL);
-                        if (!formatted) {
-                                r = -ENOMEM;
+                        r = table_make_json_field_name(t, ASSERT_PTR(t->data[c]), &mangled);
+                        if (r < 0)
                                 goto finish;
-                        }
 
-                        /* Arbitrary strings suck as field names, try to mangle them into something more suitable hence */
-                        mangled = string_to_json_field_name(formatted);
-                        if (!mangled) {
-                                r = -ENOMEM;
-                                goto finish;
-                        }
                         n = mangled;
                 }
 
@@ -2761,6 +2825,70 @@ finish:
         return r;
 }
 
+static int table_to_json_vertical(Table *t, JsonVariant **ret) {
+        JsonVariant **elements = NULL;
+        size_t n_elements = 0;
+        int r;
+
+        assert(t);
+        assert(t->vertical);
+
+        if (t->n_columns != 2)
+                return -EINVAL;
+
+        /* Ensure we have no incomplete rows */
+        assert(t->n_cells % t->n_columns == 0);
+
+        elements = new0(JsonVariant *, t->n_cells);
+        if (!elements) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        for (size_t i = t->n_columns; i < t->n_cells; i++) {
+
+                if (i % t->n_columns == 0) {
+                        _cleanup_free_ char *mangled = NULL;
+                        const char *n;
+
+                        n = table_get_json_field_name(t, i / t->n_columns - 1);
+                        if (!n) {
+                                r = table_make_json_field_name(t, ASSERT_PTR(t->data[i]), &mangled);
+                                if (r < 0)
+                                        goto finish;
+
+                                n = mangled;
+                        }
+
+                        r = json_variant_new_string(elements + n_elements, n);
+                } else
+                        r = table_data_to_json(t->data[i], elements + n_elements);
+                if (r < 0)
+                        goto finish;
+
+                n_elements++;
+        }
+
+        r = json_variant_new_object(ret, elements, n_elements);
+
+finish:
+        if (elements) {
+                json_variant_unref_many(elements, n_elements);
+                free(elements);
+        }
+
+        return r;
+}
+
+int table_to_json(Table *t, JsonVariant **ret) {
+        assert(t);
+
+        if (t->vertical)
+                return table_to_json_vertical(t, ret);
+
+        return table_to_json_regular(t, ret);
+}
+
 int table_print_json(Table *t, FILE *f, JsonFormatFlags flags) {
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         int r;
@@ -2809,7 +2937,7 @@ int table_print_with_pager(
         return 0;
 }
 
-int table_set_json_field_name(Table *t, size_t column, const char *name) {
+int table_set_json_field_name(Table *t, size_t idx, const char *name) {
         int r;
 
         assert(t);
@@ -2817,21 +2945,21 @@ int table_set_json_field_name(Table *t, size_t column, const char *name) {
         if (name) {
                 size_t m;
 
-                m = MAX(column + 1, t->n_json_fields);
+                m = MAX(idx + 1, t->n_json_fields);
                 if (!GREEDY_REALLOC0(t->json_fields, m))
                         return -ENOMEM;
 
-                r = free_and_strdup(t->json_fields + column, name);
+                r = free_and_strdup(t->json_fields + idx, name);
                 if (r < 0)
                         return r;
 
                 t->n_json_fields = m;
                 return r;
         } else {
-                if (column >= t->n_json_fields)
+                if (idx >= t->n_json_fields)
                         return 0;
 
-                t->json_fields[column] = mfree(t->json_fields[column]);
+                t->json_fields[idx] = mfree(t->json_fields[idx]);
                 return 1;
         }
 }
index c88a060dbea0ac0e18b07cafd0f73be21678c9d2..e835692c6fdb9db19ac1fcae46c460f869ebf011 100644 (file)
@@ -12,6 +12,8 @@
 typedef enum TableDataType {
         TABLE_EMPTY,
         TABLE_STRING,
+        TABLE_HEADER,              /* in regular mode: the cells in the first row, that carry the column names */
+        TABLE_FIELD,               /* in vertical mode: the cells in the first column, that carry the field names */
         TABLE_STRV,
         TABLE_STRV_WRAPPED,
         TABLE_PATH,
@@ -78,6 +80,7 @@ typedef struct TableCell TableCell;
 Table *table_new_internal(const char *first_header, ...) _sentinel_;
 #define table_new(...) table_new_internal(__VA_ARGS__, NULL)
 Table *table_new_raw(size_t n_columns);
+Table *table_new_vertical(void);
 Table *table_unref(Table *t);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Table*, table_unref);
@@ -86,7 +89,8 @@ int table_add_cell_full(Table *t, TableCell **ret_cell, TableDataType type, cons
 static inline int table_add_cell(Table *t, TableCell **ret_cell, TableDataType type, const void *data) {
         return table_add_cell_full(t, ret_cell, type, data, SIZE_MAX, SIZE_MAX, UINT_MAX, UINT_MAX, UINT_MAX);
 }
-int table_add_cell_stringf(Table *t, TableCell **ret_cell, const char *format, ...) _printf_(3, 4);
+int table_add_cell_stringf_full(Table *t, TableCell **ret_cell, TableDataType type, const char *format, ...) _printf_(4, 5);
+#define table_add_cell_stringf(t, ret_cell, format, ...) table_add_cell_stringf_full(t, ret_cell, TABLE_STRING, format, __VA_ARGS__)
 
 int table_fill_empty(Table *t, size_t until_column);
 
@@ -139,7 +143,7 @@ int table_print_json(Table *t, FILE *f, JsonFormatFlags json_flags);
 
 int table_print_with_pager(Table *t, JsonFormatFlags json_format_flags, PagerFlags pager_flags, bool show_header);
 
-int table_set_json_field_name(Table *t, size_t column, const char *name);
+int table_set_json_field_name(Table *t, size_t idx, const char *name);
 
 #define table_log_add_error(r) \
         log_error_errno(r, "Failed to add cells to table: %m")
index 3cbc6be248b3bab2f83cae32dd130820107dd4cc..164e71a150d0d9f1d2c139168987093a233714f9 100644 (file)
@@ -88,7 +88,7 @@ int fstab_filter_options(
                 char ***ret_values,
                 char **ret_filtered) {
 
-        const char *name, *namefound = NULL, *x;
+        const char *namefound = NULL, *x;
         _cleanup_strv_free_ char **stor = NULL, **values = NULL;
         _cleanup_free_ char *value = NULL, **filtered = NULL;
         int r;
@@ -127,17 +127,17 @@ int fstab_filter_options(
                                 if (!x)
                                         continue;
                                 /* Match name, but when ret_values, only when followed by assignment. */
-                                if (*x == '=' || (!ret_values && *x == '\0'))
+                                if (*x == '=' || (!ret_values && *x == '\0')) {
+                                        /* Keep the last occurrence found */
+                                        namefound = name;
                                         goto found;
+                                }
                         }
 
                         *t = *s;
                         t++;
                         continue;
                 found:
-                        /* Keep the last occurrence found */
-                        namefound = name;
-
                         if (ret_value || ret_values) {
                                 assert(IN_SET(*x, '=', '\0'));
 
index a15833b21dbf44ff7db1359b731d37efc1b03837..ce73d7f78190cba1db7d4fd57dc2e0f8bb50d06a 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "gpt.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "utf8.h"
 
 #pragma message "Please define GPT partition types for your architecture."
 #endif
 
+bool partition_designator_is_versioned(PartitionDesignator d) {
+        /* Returns true for all designators where we want to support a concept of "versioning", i.e. which
+         * likely contain software binaries (or hashes thereof) that make sense to be versioned as a
+         * whole. We use this check to automatically pick the newest version of these partitions, by version
+         * comparing the partition labels. */
+
+        return IN_SET(d,
+                      PARTITION_ROOT,
+                      PARTITION_USR,
+                      PARTITION_ROOT_VERITY,
+                      PARTITION_USR_VERITY,
+                      PARTITION_ROOT_VERITY_SIG,
+                      PARTITION_USR_VERITY_SIG);
+}
+
+PartitionDesignator partition_verity_of(PartitionDesignator p) {
+        switch (p) {
+
+        case PARTITION_ROOT:
+                return PARTITION_ROOT_VERITY;
+
+        case PARTITION_USR:
+                return PARTITION_USR_VERITY;
+
+        default:
+                return _PARTITION_DESIGNATOR_INVALID;
+        }
+}
+
+PartitionDesignator partition_verity_sig_of(PartitionDesignator p) {
+        switch (p) {
+
+        case PARTITION_ROOT:
+                return PARTITION_ROOT_VERITY_SIG;
+
+        case PARTITION_USR:
+                return PARTITION_USR_VERITY_SIG;
+
+        default:
+                return _PARTITION_DESIGNATOR_INVALID;
+        }
+}
+
+
+static const char *const partition_designator_table[] = {
+        [PARTITION_ROOT]                      = "root",
+        [PARTITION_USR]                       = "usr",
+        [PARTITION_HOME]                      = "home",
+        [PARTITION_SRV]                       = "srv",
+        [PARTITION_ESP]                       = "esp",
+        [PARTITION_XBOOTLDR]                  = "xbootldr",
+        [PARTITION_SWAP]                      = "swap",
+        [PARTITION_ROOT_VERITY]               = "root-verity",
+        [PARTITION_USR_VERITY]                = "usr-verity",
+        [PARTITION_ROOT_VERITY_SIG]           = "root-verity-sig",
+        [PARTITION_USR_VERITY_SIG]            = "usr-verity-sig",
+        [PARTITION_TMP]                       = "tmp",
+        [PARTITION_VAR]                       = "var",
+        [PARTITION_USER_HOME]                 = "user-home",
+        [PARTITION_LINUX_GENERIC]             = "linux-generic",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(partition_designator, PartitionDesignator);
+
+static const char *const partition_mountpoint_table[] = {
+        [PARTITION_ROOT]                      = "/\0",
+        [PARTITION_USR]                       = "/usr\0",
+        [PARTITION_HOME]                      = "/home\0",
+        [PARTITION_SRV]                       = "/srv\0",
+        [PARTITION_ESP]                       = "/efi\0/boot\0",
+        [PARTITION_XBOOTLDR]                  = "/boot\0",
+        [PARTITION_TMP]                       = "/var/tmp\0",
+        [PARTITION_VAR]                       = "/var\0",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(partition_mountpoint, PartitionDesignator);
+
 #define _GPT_ARCH_SEXTET(arch, name)                                   \
-        { SD_GPT_ROOT_##arch,              "root-" name,               ARCHITECTURE_##arch, .is_root = true            },  \
-        { SD_GPT_ROOT_##arch##_VERITY,     "root-" name "-verity",     ARCHITECTURE_##arch, .is_root_verity = true     },  \
-        { SD_GPT_ROOT_##arch##_VERITY_SIG, "root-" name "-verity-sig", ARCHITECTURE_##arch, .is_root_verity_sig = true },  \
-        { SD_GPT_USR_##arch,               "usr-" name,                ARCHITECTURE_##arch, .is_usr = true             },  \
-        { SD_GPT_USR_##arch##_VERITY,      "usr-" name "-verity",      ARCHITECTURE_##arch, .is_usr_verity = true      },  \
-        { SD_GPT_USR_##arch##_VERITY_SIG,  "usr-" name "-verity-sig",  ARCHITECTURE_##arch, .is_usr_verity_sig = true  }
+        { SD_GPT_ROOT_##arch,              "root-" name,               ARCHITECTURE_##arch, .designator = PARTITION_ROOT            },  \
+        { SD_GPT_ROOT_##arch##_VERITY,     "root-" name "-verity",     ARCHITECTURE_##arch, .designator = PARTITION_ROOT_VERITY     },  \
+        { SD_GPT_ROOT_##arch##_VERITY_SIG, "root-" name "-verity-sig", ARCHITECTURE_##arch, .designator = PARTITION_ROOT_VERITY_SIG },  \
+        { SD_GPT_USR_##arch,               "usr-" name,                ARCHITECTURE_##arch, .designator = PARTITION_USR             },  \
+        { SD_GPT_USR_##arch##_VERITY,      "usr-" name "-verity",      ARCHITECTURE_##arch, .designator = PARTITION_USR_VERITY      },  \
+        { SD_GPT_USR_##arch##_VERITY_SIG,  "usr-" name "-verity-sig",  ARCHITECTURE_##arch, .designator = PARTITION_USR_VERITY_SIG  }
 
 const GptPartitionType gpt_partition_type_table[] = {
         _GPT_ARCH_SEXTET(ALPHA,       "alpha"),
@@ -43,26 +121,31 @@ const GptPartitionType gpt_partition_type_table[] = {
         _GPT_ARCH_SEXTET(X86,         "x86"),
         _GPT_ARCH_SEXTET(X86_64,      "x86-64"),
 #ifdef SD_GPT_ROOT_NATIVE
-        { SD_GPT_ROOT_NATIVE,            "root",            native_architecture(), .is_root = true            },
-        { SD_GPT_ROOT_NATIVE_VERITY,     "root-verity",     native_architecture(), .is_root_verity = true     },
-        { SD_GPT_ROOT_NATIVE_VERITY_SIG, "root-verity-sig", native_architecture(), .is_root_verity_sig = true },
-        { SD_GPT_USR_NATIVE,             "usr",             native_architecture(), .is_usr = true             },
-        { SD_GPT_USR_NATIVE_VERITY,      "usr-verity",      native_architecture(), .is_usr_verity = true      },
-        { SD_GPT_USR_NATIVE_VERITY_SIG,  "usr-verity-sig",  native_architecture(), .is_usr_verity_sig = true  },
+        { SD_GPT_ROOT_NATIVE,            "root",            native_architecture(), .designator = PARTITION_ROOT            },
+        { SD_GPT_ROOT_NATIVE_VERITY,     "root-verity",     native_architecture(), .designator = PARTITION_ROOT_VERITY     },
+        { SD_GPT_ROOT_NATIVE_VERITY_SIG, "root-verity-sig", native_architecture(), .designator = PARTITION_ROOT_VERITY_SIG },
+        { SD_GPT_USR_NATIVE,             "usr",             native_architecture(), .designator = PARTITION_USR             },
+        { SD_GPT_USR_NATIVE_VERITY,      "usr-verity",      native_architecture(), .designator = PARTITION_USR_VERITY      },
+        { SD_GPT_USR_NATIVE_VERITY_SIG,  "usr-verity-sig",  native_architecture(), .designator = PARTITION_USR_VERITY_SIG  },
 #endif
 #ifdef SD_GPT_ROOT_SECONDARY
-        _GPT_ARCH_SEXTET(SECONDARY,   "secondary"),
+        { SD_GPT_ROOT_NATIVE,            "root-secondary",            native_architecture(), .designator = PARTITION_ROOT            },
+        { SD_GPT_ROOT_NATIVE_VERITY,     "root-secondary-verity",     native_architecture(), .designator = PARTITION_ROOT_VERITY     },
+        { SD_GPT_ROOT_NATIVE_VERITY_SIG, "root-secondary-verity-sig", native_architecture(), .designator = PARTITION_ROOT_VERITY_SIG },
+        { SD_GPT_USR_NATIVE,             "usr-secondary",             native_architecture(), .designator = PARTITION_USR             },
+        { SD_GPT_USR_NATIVE_VERITY,      "usr-secondary-verity",      native_architecture(), .designator = PARTITION_USR_VERITY      },
+        { SD_GPT_USR_NATIVE_VERITY_SIG,  "usr-secondary-verity-sig",  native_architecture(), .designator = PARTITION_USR_VERITY_SIG  },
 #endif
 
-        { SD_GPT_ESP,                    "esp",           _ARCHITECTURE_INVALID },
-        { SD_GPT_XBOOTLDR,               "xbootldr",      _ARCHITECTURE_INVALID },
-        { SD_GPT_SWAP,                   "swap",          _ARCHITECTURE_INVALID },
-        { SD_GPT_HOME,                   "home",          _ARCHITECTURE_INVALID },
-        { SD_GPT_SRV,                    "srv",           _ARCHITECTURE_INVALID },
-        { SD_GPT_VAR,                    "var",           _ARCHITECTURE_INVALID },
-        { SD_GPT_TMP,                    "tmp",           _ARCHITECTURE_INVALID },
-        { SD_GPT_USER_HOME,              "user-home",     _ARCHITECTURE_INVALID },
-        { SD_GPT_LINUX_GENERIC,          "linux-generic", _ARCHITECTURE_INVALID },
+        { SD_GPT_ESP,                    "esp",           _ARCHITECTURE_INVALID, .designator = PARTITION_ESP },
+        { SD_GPT_XBOOTLDR,               "xbootldr",      _ARCHITECTURE_INVALID, .designator = PARTITION_XBOOTLDR },
+        { SD_GPT_SWAP,                   "swap",          _ARCHITECTURE_INVALID, .designator = PARTITION_SWAP },
+        { SD_GPT_HOME,                   "home",          _ARCHITECTURE_INVALID, .designator = PARTITION_HOME },
+        { SD_GPT_SRV,                    "srv",           _ARCHITECTURE_INVALID, .designator = PARTITION_SRV },
+        { SD_GPT_VAR,                    "var",           _ARCHITECTURE_INVALID, .designator = PARTITION_VAR },
+        { SD_GPT_TMP,                    "tmp",           _ARCHITECTURE_INVALID, .designator = PARTITION_TMP },
+        { SD_GPT_USER_HOME,              "user-home",     _ARCHITECTURE_INVALID, .designator = PARTITION_USER_HOME },
+        { SD_GPT_LINUX_GENERIC,          "linux-generic", _ARCHITECTURE_INVALID, .designator = PARTITION_LINUX_GENERIC },
         {}
 };
 
@@ -100,17 +183,27 @@ const char *gpt_partition_type_uuid_to_string_harder(
         return sd_id128_to_uuid_string(id, buffer);
 }
 
-int gpt_partition_type_uuid_from_string(const char *s, sd_id128_t *ret) {
+int gpt_partition_type_from_string(const char *s, GptPartitionType *ret) {
+        sd_id128_t id;
+        int r;
+
         assert(s);
 
         for (size_t i = 0; i < ELEMENTSOF(gpt_partition_type_table) - 1; i++)
                 if (streq(s, gpt_partition_type_table[i].name)) {
                         if (ret)
-                                *ret = gpt_partition_type_table[i].uuid;
+                                *ret = gpt_partition_type_table[i];
                         return 0;
                 }
 
-        return sd_id128_from_string(s, ret);
+        r = sd_id128_from_string(s, &id);
+        if (r < 0)
+                return r;
+
+        if (ret)
+                *ret = gpt_partition_type_from_uuid(id);
+
+        return 0;
 }
 
 Architecture gpt_partition_type_uuid_to_arch(sd_id128_t id) {
@@ -133,74 +226,59 @@ int gpt_partition_label_valid(const char *s) {
         return char16_strlen(recoded) <= GPT_LABEL_MAX;
 }
 
-static GptPartitionType gpt_partition_type_from_uuid(sd_id128_t id) {
+GptPartitionType gpt_partition_type_from_uuid(sd_id128_t id) {
         const GptPartitionType *pt;
 
         pt = gpt_partition_type_find_by_uuid(id);
         if (pt)
                 return *pt;
 
-        return (GptPartitionType) { .uuid = id, .arch = _ARCHITECTURE_INVALID };
-}
-
-bool gpt_partition_type_is_root(sd_id128_t id) {
-        return gpt_partition_type_from_uuid(id).is_root;
-}
-
-bool gpt_partition_type_is_root_verity(sd_id128_t id) {
-        return gpt_partition_type_from_uuid(id).is_root_verity;
-}
-
-bool gpt_partition_type_is_root_verity_sig(sd_id128_t id) {
-        return gpt_partition_type_from_uuid(id).is_root_verity_sig;
-}
-
-bool gpt_partition_type_is_usr(sd_id128_t id) {
-        return gpt_partition_type_from_uuid(id).is_usr;
-}
-
-bool gpt_partition_type_is_usr_verity(sd_id128_t id) {
-        return gpt_partition_type_from_uuid(id).is_usr_verity;
+        return (GptPartitionType) {
+                .uuid = id,
+                .arch = _ARCHITECTURE_INVALID,
+                .designator = _PARTITION_DESIGNATOR_INVALID,
+        };
 }
 
-bool gpt_partition_type_is_usr_verity_sig(sd_id128_t id) {
-        return gpt_partition_type_from_uuid(id).is_usr_verity_sig;
+const char *gpt_partition_type_mountpoint_nulstr(GptPartitionType type) {
+        return partition_mountpoint_to_string(type.designator);
 }
 
-bool gpt_partition_type_knows_read_only(sd_id128_t id) {
-        return gpt_partition_type_is_root(id) ||
-                gpt_partition_type_is_usr(id) ||
-                sd_id128_in_set(id,
-                                SD_GPT_HOME,
-                                SD_GPT_SRV,
-                                SD_GPT_VAR,
-                                SD_GPT_TMP,
-                                SD_GPT_XBOOTLDR) ||
-                gpt_partition_type_is_root_verity(id) || /* pretty much implied, but let's set the bit to make things really clear */
-                gpt_partition_type_is_usr_verity(id);    /* ditto */
+bool gpt_partition_type_knows_read_only(GptPartitionType type) {
+        return IN_SET(type.designator,
+                      PARTITION_ROOT,
+                      PARTITION_USR,
+                      /* pretty much implied, but let's set the bit to make things really clear */
+                      PARTITION_ROOT_VERITY,
+                      PARTITION_USR_VERITY,
+                      PARTITION_HOME,
+                      PARTITION_SRV,
+                      PARTITION_VAR,
+                      PARTITION_TMP,
+                      PARTITION_XBOOTLDR);
 }
 
-bool gpt_partition_type_knows_growfs(sd_id128_t id) {
-        return gpt_partition_type_is_root(id) ||
-                gpt_partition_type_is_usr(id) ||
-                sd_id128_in_set(id,
-                                SD_GPT_HOME,
-                                SD_GPT_SRV,
-                                SD_GPT_VAR,
-                                SD_GPT_TMP,
-                                SD_GPT_XBOOTLDR);
+bool gpt_partition_type_knows_growfs(GptPartitionType type) {
+        return IN_SET(type.designator,
+                      PARTITION_ROOT,
+                      PARTITION_USR,
+                      PARTITION_HOME,
+                      PARTITION_SRV,
+                      PARTITION_VAR,
+                      PARTITION_TMP,
+                      PARTITION_XBOOTLDR);
 }
 
-bool gpt_partition_type_knows_no_auto(sd_id128_t id) {
-        return gpt_partition_type_is_root(id) ||
-                gpt_partition_type_is_root_verity(id) ||
-                gpt_partition_type_is_usr(id) ||
-                gpt_partition_type_is_usr_verity(id) ||
-                sd_id128_in_set(id,
-                                SD_GPT_HOME,
-                                SD_GPT_SRV,
-                                SD_GPT_VAR,
-                                SD_GPT_TMP,
-                                SD_GPT_XBOOTLDR,
-                                SD_GPT_SWAP);
+bool gpt_partition_type_knows_no_auto(GptPartitionType type) {
+        return IN_SET(type.designator,
+                      PARTITION_ROOT,
+                      PARTITION_ROOT_VERITY,
+                      PARTITION_USR,
+                      PARTITION_USR_VERITY,
+                      PARTITION_HOME,
+                      PARTITION_SRV,
+                      PARTITION_VAR,
+                      PARTITION_TMP,
+                      PARTITION_XBOOTLDR,
+                      PARTITION_SWAP);
 }
index f673194d4af9f9df5cfdcf7c2c53d0791afd0f1f..967e20e0af59ce961355cea03c2d9bc4b617355d 100644 (file)
 /* maximum length of gpt label */
 #define GPT_LABEL_MAX 36
 
+typedef enum PartitionDesignator {
+        PARTITION_ROOT, /* Primary architecture */
+        PARTITION_USR,
+        PARTITION_HOME,
+        PARTITION_SRV,
+        PARTITION_ESP,
+        PARTITION_XBOOTLDR,
+        PARTITION_SWAP,
+        PARTITION_ROOT_VERITY, /* verity data for the PARTITION_ROOT partition */
+        PARTITION_USR_VERITY,
+        PARTITION_ROOT_VERITY_SIG, /* PKCS#7 signature for root hash for the PARTITION_ROOT partition */
+        PARTITION_USR_VERITY_SIG,
+        PARTITION_TMP,
+        PARTITION_VAR,
+        PARTITION_USER_HOME,
+        PARTITION_LINUX_GENERIC,
+        _PARTITION_DESIGNATOR_MAX,
+        _PARTITION_DESIGNATOR_INVALID = -EINVAL,
+} PartitionDesignator;
+
+bool partition_designator_is_versioned(PartitionDesignator d);
+
+PartitionDesignator partition_verity_of(PartitionDesignator p);
+PartitionDesignator partition_verity_sig_of(PartitionDesignator p);
+
+const char* partition_designator_to_string(PartitionDesignator d) _const_;
+PartitionDesignator partition_designator_from_string(const char *name) _pure_;
+
 const char *gpt_partition_type_uuid_to_string(sd_id128_t id);
 const char *gpt_partition_type_uuid_to_string_harder(
                 sd_id128_t id,
                 char buffer[static SD_ID128_UUID_STRING_MAX]);
-int gpt_partition_type_uuid_from_string(const char *s, sd_id128_t *ret);
 
 #define GPT_PARTITION_TYPE_UUID_TO_STRING_HARDER(id) \
         gpt_partition_type_uuid_to_string_harder((id), (char[SD_ID128_UUID_STRING_MAX]) {})
@@ -25,26 +52,18 @@ typedef struct GptPartitionType {
         sd_id128_t uuid;
         const char *name;
         Architecture arch;
-
-        bool is_root:1;
-        bool is_root_verity:1;
-        bool is_root_verity_sig:1;
-        bool is_usr:1;
-        bool is_usr_verity:1;
-        bool is_usr_verity_sig:1;
+        PartitionDesignator designator;
 } GptPartitionType;
 
 extern const GptPartitionType gpt_partition_type_table[];
 
 int gpt_partition_label_valid(const char *s);
 
-bool gpt_partition_type_is_root(sd_id128_t id);
-bool gpt_partition_type_is_root_verity(sd_id128_t id);
-bool gpt_partition_type_is_root_verity_sig(sd_id128_t id);
-bool gpt_partition_type_is_usr(sd_id128_t id);
-bool gpt_partition_type_is_usr_verity(sd_id128_t id);
-bool gpt_partition_type_is_usr_verity_sig(sd_id128_t id);
+GptPartitionType gpt_partition_type_from_uuid(sd_id128_t id);
+int gpt_partition_type_from_string(const char *s, GptPartitionType *ret);
+
+const char *gpt_partition_type_mountpoint_nulstr(GptPartitionType type);
 
-bool gpt_partition_type_knows_read_only(sd_id128_t id);
-bool gpt_partition_type_knows_growfs(sd_id128_t id);
-bool gpt_partition_type_knows_no_auto(sd_id128_t id);
+bool gpt_partition_type_knows_read_only(GptPartitionType type);
+bool gpt_partition_type_knows_growfs(GptPartitionType type);
+bool gpt_partition_type_knows_no_auto(GptPartitionType type);
index 6a680fb3be1b7747a5a86ee577a8134f10439e68..0ff686d24b65a478f00bcb37d1d33e8e33e3536e 100644 (file)
@@ -652,7 +652,7 @@ int hwdb_update(const char *root, const char *hwdb_bin_dir, bool strict, bool co
 
 int hwdb_query(const char *modalias, const char *root) {
         _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
-        const char *key, *value, *p;
+        const char *key, *value;
         int r;
 
         assert(modalias);
@@ -682,7 +682,6 @@ int hwdb_query(const char *modalias, const char *root) {
 
 bool hwdb_should_reload(sd_hwdb *hwdb) {
         bool found = false;
-        const char* p;
         struct stat st;
 
         if (!hwdb)
index 97d39ee04777bc6dcf1760895ecfeba039e2b573..ec8906450611491777ff81ca6175c35152746bdd 100644 (file)
@@ -179,7 +179,6 @@ int raw_strip_suffixes(const char *p, char **ret) {
                 return -ENOMEM;
 
         for (;;) {
-                const char *sfx;
                 bool changed = false;
 
                 NULSTR_FOREACH(sfx, suffixes) {
index 9bb412ba06842031377d903d6bde981c92416f2f..0abc73897e8b7233b3bba43a2bdc3c15f963cb73 100644 (file)
@@ -197,7 +197,7 @@ int unit_file_exists(LookupScope scope, const LookupPaths *paths, const char *na
 int unit_file_get_list(LookupScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns);
 Hashmap* unit_file_list_free(Hashmap *h);
 
-InstallChangeType install_changes_add(InstallChange **changes, size_t *n_changes, int type, const char *path, const char *source);
+InstallChangeType install_changes_add(InstallChange **changes, size_t *n_changes, InstallChangeType type, const char *path, const char *source);
 void install_changes_free(InstallChange *changes, size_t n_changes);
 void install_changes_dump(int r, const char *verb, const InstallChange *changes, size_t n_changes, bool quiet);
 
@@ -224,7 +224,7 @@ UnitFileState unit_file_state_from_string(const char *s) _pure_;
 /* from_string conversion is unreliable because of the overlap between -EPERM and -1 for error. */
 
 const char *install_change_type_to_string(InstallChangeType t) _const_;
-int install_change_type_from_string(const char *s) _pure_;
+InstallChangeType install_change_type_from_string(const char *s) _pure_;
 
 const char *unit_file_preset_mode_to_string(UnitFilePresetMode m) _const_;
 UnitFilePresetMode unit_file_preset_mode_from_string(const char *s) _pure_;
index eda7bb19563e560b074fe138b54fb7b70bcb83fd..94d7b3155705cf29e22b5f7921b879a14697b1e8 100644 (file)
@@ -481,6 +481,10 @@ int json_variant_new_id128(JsonVariant **ret, sd_id128_t id) {
         return json_variant_new_string(ret, SD_ID128_TO_STRING(id));
 }
 
+int json_variant_new_uuid(JsonVariant **ret, sd_id128_t id) {
+        return json_variant_new_string(ret, SD_ID128_TO_UUID_STRING(id));
+}
+
 static void json_variant_set(JsonVariant *a, JsonVariant *b) {
         assert(a);
 
@@ -3609,7 +3613,8 @@ int json_buildv(JsonVariant **ret, va_list ap) {
                         break;
                 }
 
-                case _JSON_BUILD_ID128: {
+                case _JSON_BUILD_ID128:
+                case _JSON_BUILD_UUID: {
                         const sd_id128_t *id;
 
                         if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
@@ -3620,7 +3625,9 @@ int json_buildv(JsonVariant **ret, va_list ap) {
                         assert_se(id = va_arg(ap, sd_id128_t*));
 
                         if (current->n_suppress == 0) {
-                                r = json_variant_new_id128(&add, *id);
+                                r = command == _JSON_BUILD_ID128 ?
+                                        json_variant_new_id128(&add, *id) :
+                                        json_variant_new_uuid(&add, *id);
                                 if (r < 0)
                                         goto finish;
                         }
index 5993e05299c6e8c03961647c99d01ccce8f56154..c5f052a9d569bbcafc83c3afca3a61c619611ae7 100644 (file)
@@ -75,6 +75,7 @@ int json_variant_new_array_strv(JsonVariant **ret, char **l);
 int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n);
 int json_variant_new_null(JsonVariant **ret);
 int json_variant_new_id128(JsonVariant **ret, sd_id128_t id);
+int json_variant_new_uuid(JsonVariant **ret, sd_id128_t id);
 
 static inline int json_variant_new_string(JsonVariant **ret, const char *s) {
         return json_variant_new_stringn(ret, s, SIZE_MAX);
@@ -251,6 +252,7 @@ enum {
         _JSON_BUILD_HEX,
         _JSON_BUILD_OCTESCAPE,
         _JSON_BUILD_ID128,
+        _JSON_BUILD_UUID,
         _JSON_BUILD_BYTE_ARRAY,
         _JSON_BUILD_HW_ADDR,
         _JSON_BUILD_PAIR_UNSIGNED_NON_ZERO,
@@ -288,6 +290,7 @@ enum {
 #define JSON_BUILD_HEX(p, n) _JSON_BUILD_HEX, (const void*) { p }, (size_t) { n }
 #define JSON_BUILD_OCTESCAPE(p, n) _JSON_BUILD_OCTESCAPE, (const void*) { p }, (size_t) { n }
 #define JSON_BUILD_ID128(id) _JSON_BUILD_ID128, (const sd_id128_t*) { &(id) }
+#define JSON_BUILD_UUID(id) _JSON_BUILD_UUID, (const sd_id128_t*) { &(id) }
 #define JSON_BUILD_BYTE_ARRAY(v, n) _JSON_BUILD_BYTE_ARRAY, (const void*) { v }, (size_t) { n }
 #define JSON_BUILD_CONST_STRING(s) _JSON_BUILD_VARIANT, JSON_VARIANT_STRING_CONST(s)
 #define JSON_BUILD_IN4_ADDR(v) JSON_BUILD_BYTE_ARRAY((const struct in_addr*) { v }, sizeof(struct in_addr))
@@ -313,6 +316,7 @@ enum {
 #define JSON_BUILD_PAIR_BASE64(name, p, n) JSON_BUILD_PAIR(name, JSON_BUILD_BASE64(p, n))
 #define JSON_BUILD_PAIR_HEX(name, p, n) JSON_BUILD_PAIR(name, JSON_BUILD_HEX(p, n))
 #define JSON_BUILD_PAIR_ID128(name, id) JSON_BUILD_PAIR(name, JSON_BUILD_ID128(id))
+#define JSON_BUILD_PAIR_UUID(name, id) JSON_BUILD_PAIR(name, JSON_BUILD_UUID(id))
 #define JSON_BUILD_PAIR_BYTE_ARRAY(name, v, n) JSON_BUILD_PAIR(name, JSON_BUILD_BYTE_ARRAY(v, n))
 #define JSON_BUILD_PAIR_IN4_ADDR(name, v) JSON_BUILD_PAIR(name, JSON_BUILD_IN4_ADDR(v))
 #define JSON_BUILD_PAIR_IN6_ADDR(name, v) JSON_BUILD_PAIR(name, JSON_BUILD_IN6_ADDR(v))
index 842ff3066b4151a2fa89c2d3bae51e0ded9ff6c3..6222ef4fc81c4d50864833e4ec76e7dfc72e0227 100644 (file)
@@ -71,7 +71,6 @@ int get_keymaps(char ***ret) {
         if (!keymaps)
                 return -ENOMEM;
 
-        const char *dir;
         NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
                 r = recurse_dir_at(
                                 AT_FDCWD,
@@ -135,7 +134,6 @@ int keymap_exists(const char *name) {
         if (!keymap_is_valid(name))
                 return -EINVAL;
 
-        const char *dir;
         NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
                 r = recurse_dir_at(
                                 AT_FDCWD,
index 525849ee19e77f4e6057644d75c2c3de79e0eccd..a2bf14ec487299fc450491422fb3b2277f13b94b 100644 (file)
@@ -194,6 +194,157 @@ static int verify_features(
         return 0;
 }
 
+static int fido2_assert_set_basic_properties(
+                fido_assert_t *a,
+                const char *rp_id,
+                const void *cid,
+                size_t cid_size) {
+        int r;
+
+        assert(a);
+        assert(rp_id);
+        assert(cid);
+        assert(cid_size > 0);
+
+        r = sym_fido_assert_set_rp(a, rp_id);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r));
+
+        r = sym_fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r));
+
+        r = sym_fido_assert_allow_cred(a, cid, cid_size);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
+
+        return 0;
+}
+
+static int fido2_common_assert_error_handle(int r) {
+        switch (r) {
+        case FIDO_OK:
+                return 0;
+        case FIDO_ERR_NO_CREDENTIALS:
+                return log_error_errno(SYNTHETIC_ERRNO(EBADSLT),
+                                       "Wrong security token; needed credentials not present on token.");
+        case FIDO_ERR_PIN_REQUIRED:
+                return log_error_errno(SYNTHETIC_ERRNO(ENOANO),
+                                       "Security token requires PIN.");
+        case FIDO_ERR_PIN_AUTH_BLOCKED:
+                return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
+                                       "PIN of security token is blocked, please remove/reinsert token.");
+#ifdef FIDO_ERR_UV_BLOCKED
+        case FIDO_ERR_UV_BLOCKED:
+                return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
+                                       "Verification of security token is blocked, please remove/reinsert token.");
+#endif
+        case FIDO_ERR_PIN_INVALID:
+                return log_error_errno(SYNTHETIC_ERRNO(ENOLCK),
+                                       "PIN of security token incorrect.");
+        case FIDO_ERR_UP_REQUIRED:
+                return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE),
+                                       "User presence required.");
+        case FIDO_ERR_ACTION_TIMEOUT:
+                return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
+                                       "Token action timeout. (User didn't interact with token quickly enough.)");
+        default:
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to ask token for assertion: %s", sym_fido_strerr(r));
+        }
+}
+
+static int fido2_is_cred_in_specific_token(
+                const char *path,
+                const char *rp_id,
+                const void *cid,
+                size_t cid_size,
+                char **pins,
+                Fido2EnrollFlags flags) {
+
+        assert(path);
+        assert(rp_id);
+        assert(cid);
+        assert(cid_size);
+
+        _cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
+        _cleanup_(fido_assert_free_wrapper) fido_assert_t *a = NULL;
+        bool has_up = false, has_uv = false;
+        int r;
+
+        d = sym_fido_dev_new();
+        if (!d)
+                return log_oom();
+
+        r = sym_fido_dev_open(d, path);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to open FIDO2 device %s: %s", path, sym_fido_strerr(r));
+
+        r = verify_features(d, path, LOG_ERR, NULL, NULL, &has_up, &has_uv);
+        if (r < 0)
+                return r;
+
+        a = sym_fido_assert_new();
+        if (!a)
+                return log_oom();
+
+        r = fido2_assert_set_basic_properties(a, rp_id, cid, cid_size);
+        if (r < 0)
+                return r;
+
+        /* According to CTAP 2.1 specification, to do pre-flight we need to set up option to false
+         * with optionally pinUvAuthParam in assertion[1]. But for authenticator that doesn't support
+         * user presence, once up option is present, the authenticator may return CTAP2_ERR_UNSUPPORTED_OPTION[2].
+         * So we simplely omit the option in that case.
+         * Reference:
+         * 1: https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#pre-flight
+         * 2: https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorGetAssertion (in step 5)
+         */
+        if (has_up)
+                r = sym_fido_assert_set_up(a, FIDO_OPT_FALSE);
+        else
+                r = sym_fido_assert_set_up(a, FIDO_OPT_OMIT);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set assertion user presence: %s", sym_fido_strerr(r));
+
+
+        /* According to CTAP 2.1 specification, if the credential is marked as userVerificationRequired,
+         * we may pass pinUvAuthParam parameter or set uv option to true in order to check whether the
+         * credential is in the token. Here we choose to use PIN (pinUvAuthParam) to achieve this.
+         * If we don't do that, the authenticator will remove the credential from the applicable
+         * credentials list, hence CTAP2_ERR_NO_CREDENTIALS error will be returned.
+         * Please see
+         * https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#sctn-getAssert-authnr-alg
+         * step 7.4 for detailed information.
+         */
+        if (FLAGS_SET(flags, FIDO2ENROLL_UV) && has_uv) {
+                if (strv_isempty(pins))
+                        r = FIDO_ERR_PIN_REQUIRED;
+                else
+                        STRV_FOREACH(i, pins) {
+                                r = sym_fido_dev_get_assert(d, a, *i);
+                                if (r != FIDO_ERR_PIN_INVALID)
+                                        break;
+                        }
+
+        } else
+                r = sym_fido_dev_get_assert(d, a, NULL);
+
+        switch (r) {
+                case FIDO_OK:
+                        return true;
+                case FIDO_ERR_NO_CREDENTIALS:
+                        return false;
+                default:
+                        return fido2_common_assert_error_handle(r);
+        }
+}
+
 static int fido2_use_hmac_hash_specific_token(
                 const char *path,
                 const char *rp_id,
@@ -263,20 +414,9 @@ static int fido2_use_hmac_hash_specific_token(
                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
                                        "Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r));
 
-        r = sym_fido_assert_set_rp(a, rp_id);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r));
-
-        r = sym_fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r));
-
-        r = sym_fido_assert_allow_cred(a, cid, cid_size);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
+        r = fido2_assert_set_basic_properties(a, rp_id, cid, cid_size);
+        if (r < 0)
+                return r;
 
         log_info("Asking FIDO2 token for authentication.");
 
@@ -403,36 +543,9 @@ static int fido2_use_hmac_hash_specific_token(
                         required |= FIDO2ENROLL_PIN;
         }
 
-        switch (r) {
-        case FIDO_OK:
-                break;
-        case FIDO_ERR_NO_CREDENTIALS:
-                return log_error_errno(SYNTHETIC_ERRNO(EBADSLT),
-                                       "Wrong security token; needed credentials not present on token.");
-        case FIDO_ERR_PIN_REQUIRED:
-                return log_error_errno(SYNTHETIC_ERRNO(ENOANO),
-                                       "Security token requires PIN.");
-        case FIDO_ERR_PIN_AUTH_BLOCKED:
-                return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
-                                       "PIN of security token is blocked, please remove/reinsert token.");
-#ifdef FIDO_ERR_UV_BLOCKED
-        case FIDO_ERR_UV_BLOCKED:
-                return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
-                                       "Verification of security token is blocked, please remove/reinsert token.");
-#endif
-        case FIDO_ERR_PIN_INVALID:
-                return log_error_errno(SYNTHETIC_ERRNO(ENOLCK),
-                                       "PIN of security token incorrect.");
-        case FIDO_ERR_UP_REQUIRED:
-                return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE),
-                                       "User presence required.");
-        case FIDO_ERR_ACTION_TIMEOUT:
-                return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
-                                       "Token action timeout. (User didn't interact with token quickly enough.)");
-        default:
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to ask token for assertion: %s", sym_fido_strerr(r));
-        }
+        r = fido2_common_assert_error_handle(r);
+        if (r < 0)
+                return r;
 
         hmac = sym_fido_assert_hmac_secret_ptr(a, 0);
         if (!hmac)
@@ -519,6 +632,30 @@ int fido2_use_hmac_hash(
                         goto finish;
                 }
 
+                r = fido2_is_cred_in_specific_token(path, rp_id, cid, cid_size, pins, required);
+                /* We handle PIN related errors here since we have used PIN in the check.
+                 * If PIN error happens, we can avoid pin retries decreased the second time. */
+                if (IN_SET(r, -ENOANO,        /* → pin required */
+                              -ENOLCK,        /* → pin incorrect */
+                              -EOWNERDEAD)) { /* → uv blocked */
+                        /* If it's not the last device, try next one */
+                        if (i < found - 1)
+                                continue;
+
+                        /* -EOWNERDEAD is returned when uv is blocked. Map it to EAGAIN so users can reinsert
+                         * the token and try again. */
+                        if (r == -EOWNERDEAD)
+                                r = -EAGAIN;
+                        goto finish;
+                } else if (r == -ENODEV) /* not a FIDO2 device or lacking HMAC-SECRET extension */
+                        continue;
+                else if (r < 0)
+                        log_error_errno(r, "Failed to determine whether the credential is in the token, trying anyway: %m");
+                else if (r == 0) {
+                        log_debug("The credential is not in the token %s, skipping.", path);
+                        continue;
+                }
+
                 r = fido2_use_hmac_hash_specific_token(path, rp_id, salt, salt_size, cid, cid_size, pins, required, ret_hmac, ret_hmac_size);
                 if (!IN_SET(r,
                             -EBADSLT, /* device doesn't understand our credential hash */
@@ -762,20 +899,9 @@ int fido2_generate_hmac_hash(
                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
                                        "Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r));
 
-        r = sym_fido_assert_set_rp(a, rp_id);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r));
-
-        r = sym_fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r));
-
-        r = sym_fido_assert_allow_cred(a, cid, cid_size);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
+        r = fido2_assert_set_basic_properties(a, rp_id, cid, cid_size);
+        if (r < 0)
+                return r;
 
         log_info("Generating secret key on FIDO2 security token.");
 
index 8161dbf825fb8b52e81249b12f8e6f75b2001ced..0e135a9ac35eb7fe90377cedae09bcace24f069d 100644 (file)
@@ -2,13 +2,20 @@
 
 #include <unistd.h>
 
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
 #include "id128-util.h"
 #include "mkfs-util.h"
 #include "mountpoint-util.h"
 #include "path-util.h"
 #include "process-util.h"
+#include "recurse-dir.h"
+#include "stat-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
+#include "tmpfile-util.h"
 #include "utf8.h"
 
 int mkfs_exists(const char *fstype) {
@@ -33,6 +40,10 @@ int mkfs_exists(const char *fstype) {
         return true;
 }
 
+int mkfs_supports_root_option(const char *fstype) {
+        return fstype_is_ro(fstype) || STR_IN_SET(fstype, "ext2", "ext3", "ext4", "btrfs", "vfat", "xfs");
+}
+
 static int mangle_linux_fs_label(const char *s, size_t max_len, char **ret) {
         /* Not more than max_len bytes (12 or 16) */
 
@@ -87,6 +98,185 @@ static int mangle_fat_label(const char *s, char **ret) {
         return 0;
 }
 
+static int setup_userns(uid_t uid, gid_t gid) {
+        int r;
+
+       /* mkfs programs tend to keep ownership intact when bootstrapping themselves from a root directory.
+        * However, we'd like for the files to be owned by root instead, so we fork off a user namespace and
+        * inside of it, map the uid/gid of the root directory to root in the user namespace. mkfs programs
+        * will pick up on this and the files will be owned by root in the generated filesystem. */
+
+        r = write_string_filef("/proc/self/uid_map", WRITE_STRING_FILE_DISABLE_BUFFER,
+                                UID_FMT " " UID_FMT " " UID_FMT, 0u, uid, 1u);
+        if (r < 0)
+                return log_error_errno(r,
+                                       "Failed to write mapping for "UID_FMT" to /proc/self/uid_map: %m",
+                                       uid);
+
+        r = write_string_file("/proc/self/setgroups", "deny", WRITE_STRING_FILE_DISABLE_BUFFER);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write 'deny' to /proc/self/setgroups: %m");
+
+        r = write_string_filef("/proc/self/gid_map", WRITE_STRING_FILE_DISABLE_BUFFER,
+                                UID_FMT " " UID_FMT " " UID_FMT, 0u, gid, 1u);
+        if (r < 0)
+                return log_error_errno(r,
+                                       "Failed to write mapping for "UID_FMT" to /proc/self/gid_map: %m",
+                                       gid);
+
+        return 0;
+}
+
+static int do_mcopy(const char *node, const char *root) {
+        _cleanup_strv_free_ char **argv = NULL;
+        _cleanup_closedir_ DIR *rootdir = NULL;
+        struct stat st;
+        int r;
+
+        assert(node);
+        assert(root);
+
+        /* Return early if there's nothing to copy. */
+        if (dir_is_empty(root, /*ignore_hidden_or_backup=*/ false))
+                return 0;
+
+        argv = strv_new("mcopy", "-b", "-s", "-p", "-Q", "-n", "-m", "-i", node);
+        if (!argv)
+                return log_oom();
+
+        /* mcopy copies the top level directory instead of everything in it so we have to pass all
+         * the subdirectories to mcopy instead to end up with the correct directory structure. */
+
+        rootdir = opendir(root);
+        if (!rootdir)
+                return log_error_errno(errno, "Failed to open directory '%s'", root);
+
+        FOREACH_DIRENT(de, rootdir, return -errno) {
+                char *p = path_join(root, de->d_name);
+                if (!p)
+                        return log_oom();
+
+                r = strv_consume(&argv, TAKE_PTR(p));
+                if (r < 0)
+                        return log_oom();
+        }
+
+        r = strv_extend(&argv, "::");
+        if (r < 0)
+                return log_oom();
+
+        if (stat(root, &st) < 0)
+                return log_error_errno(errno, "Failed to stat '%s': %m", root);
+
+        r = safe_fork("(mcopy)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|FORK_NEW_USERNS, NULL);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                r = setup_userns(st.st_uid, st.st_gid);
+                if (r < 0)
+                        _exit(EXIT_FAILURE);
+
+                /* Avoid failures caused by mismatch in expectations between mkfs.vfat and mcopy by disabling
+                 * the stricter mcopy checks using MTOOLS_SKIP_CHECK. */
+                execvpe("mcopy", argv, STRV_MAKE("MTOOLS_SKIP_CHECK=1"));
+
+                log_error_errno(errno, "Failed to execute mcopy: %m");
+
+                _exit(EXIT_FAILURE);
+        }
+
+        return 0;
+}
+
+static int protofile_print_item(
+                RecurseDirEvent event,
+                const char *path,
+                int dir_fd,
+                int inode_fd,
+                const struct dirent *de,
+                const struct statx *sx,
+                void *userdata) {
+
+        FILE *f = ASSERT_PTR(userdata);
+        int r;
+
+        if (event == RECURSE_DIR_LEAVE) {
+                fputs("$\n", f);
+                return 0;
+        }
+
+        if (!IN_SET(event, RECURSE_DIR_ENTER, RECURSE_DIR_ENTRY))
+                return RECURSE_DIR_CONTINUE;
+
+        char type = S_ISDIR(sx->stx_mode)  ? 'd' :
+                    S_ISREG(sx->stx_mode)  ? '-' :
+                    S_ISLNK(sx->stx_mode)  ? 'l' :
+                    S_ISFIFO(sx->stx_mode) ? 'p' :
+                    S_ISBLK(sx->stx_mode)  ? 'b' :
+                    S_ISCHR(sx->stx_mode)  ? 'c' : 0;
+        if (type == 0)
+                return RECURSE_DIR_CONTINUE;
+
+        fprintf(f, "%s %c%c%c%03o 0 0 ",
+                de->d_name,
+                type,
+                sx->stx_mode & S_ISUID ? 'u' : '-',
+                sx->stx_mode & S_ISGID ? 'g' : '-',
+                (unsigned) (sx->stx_mode & 0777));
+
+        if (S_ISREG(sx->stx_mode))
+                fputs(path, f);
+        else if (S_ISLNK(sx->stx_mode)) {
+                _cleanup_free_ char *p = NULL;
+
+                r = readlinkat_malloc(dir_fd, de->d_name, &p);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to read symlink %s: %m", path);
+
+                fputs(p, f);
+        } else if (S_ISBLK(sx->stx_mode) || S_ISCHR(sx->stx_mode))
+                fprintf(f, "%" PRIu32 " %" PRIu32, sx->stx_rdev_major, sx->stx_rdev_minor);
+
+        fputc('\n', f);
+
+        return RECURSE_DIR_CONTINUE;
+}
+
+static int make_protofile(const char *root, char **ret) {
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_(unlink_and_freep) char *p = NULL;
+        const char *vt;
+        int r;
+
+        assert(ret);
+
+        r = var_tmp_dir(&vt);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get persistent temporary directory: %m");
+
+        r = fopen_temporary_child(vt, &f, &p);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open temporary file: %m");
+
+        fputs("/\n"
+              "0 0\n"
+              "d--755 0 0\n", f);
+
+        r = recurse_dir_at(AT_FDCWD, root, STATX_TYPE|STATX_MODE, UINT_MAX, 0, protofile_print_item, f);
+        if (r < 0)
+                return log_error_errno(r, "Failed to recurse through %s: %m", root);
+
+        fputs("$\n", f);
+
+        r = fflush_and_check(f);
+        if (r < 0)
+                return log_error_errno(r, "Failed to flush %s: %m", p);
+
+        *ret = TAKE_PTR(p);
+
+        return 0;
+}
+
 int make_filesystem(
                 const char *node,
                 const char *fstype,
@@ -96,7 +286,10 @@ int make_filesystem(
                 bool discard) {
 
         _cleanup_free_ char *mkfs = NULL, *mangled_label = NULL;
+        _cleanup_strv_free_ char **argv = NULL;
+        _cleanup_(unlink_and_freep) char *protofile = NULL;
         char vol_id[CONST_MAX(SD_ID128_UUID_STRING_MAX, 8U + 1U)] = {};
+        struct stat st;
         int r;
 
         assert(node);
@@ -128,9 +321,9 @@ int make_filesystem(
                                                        "Don't know how to create read-only file system '%s', refusing.",
                                                        fstype);
         } else {
-                if (root)
+                if (root && !mkfs_supports_root_option(fstype))
                         return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
-                                               "Populating with source tree is only supported for read-only filesystems");
+                                               "Populating with source tree is not supported for %s", fstype);
                 r = mkfs_exists(fstype);
                 if (r < 0)
                         return log_error_errno(r, "Failed to determine whether mkfs binary for %s exists: %m", fstype);
@@ -169,101 +362,161 @@ int make_filesystem(
         if (isempty(vol_id))
                 assert_se(sd_id128_to_uuid_string(uuid, vol_id));
 
-        r = safe_fork("(mkfs)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR, NULL);
+        /* When changing this conditional, also adjust the log statement below. */
+        if (streq(fstype, "ext2")) {
+                argv = strv_new(mkfs,
+                                "-q",
+                                "-L", label,
+                                "-U", vol_id,
+                                "-I", "256",
+                                "-m", "0",
+                                "-E", discard ? "discard,lazy_itable_init=1" : "nodiscard,lazy_itable_init=1",
+                                node);
+                if (!argv)
+                        return log_oom();
+
+                if (root) {
+                        r = strv_extend_strv(&argv, STRV_MAKE("-d", root), false);
+                        if (r < 0)
+                                return log_oom();
+                }
+
+        } else if (STR_IN_SET(fstype, "ext3", "ext4")) {
+                argv = strv_new(mkfs,
+                                "-q",
+                                "-L", label,
+                                "-U", vol_id,
+                                "-I", "256",
+                                "-O", "has_journal",
+                                "-m", "0",
+                                "-E", discard ? "discard,lazy_itable_init=1" : "nodiscard,lazy_itable_init=1",
+                                node);
+
+                if (root) {
+                        r = strv_extend_strv(&argv, STRV_MAKE("-d", root), false);
+                        if (r < 0)
+                                return log_oom();
+                }
+
+        } else if (streq(fstype, "btrfs")) {
+                argv = strv_new(mkfs,
+                                "-q",
+                                "-L", label,
+                                "-U", vol_id,
+                                node);
+                if (!argv)
+                        return log_oom();
+
+                if (!discard) {
+                        r = strv_extend(&argv, "--nodiscard");
+                        if (r < 0)
+                                return log_oom();
+                }
+
+                if (root) {
+                        r = strv_extend_strv(&argv, STRV_MAKE("-r", root), false);
+                        if (r < 0)
+                                return log_oom();
+                }
+
+        } else if (streq(fstype, "f2fs")) {
+                argv = strv_new(mkfs,
+                                "-q",
+                                "-g",  /* "default options" */
+                                "-f",  /* force override, without this it doesn't seem to want to write to an empty partition */
+                                "-l", label,
+                                "-U", vol_id,
+                                "-t", one_zero(discard),
+                                node);
+
+        } else if (streq(fstype, "xfs")) {
+                const char *j;
+
+                j = strjoina("uuid=", vol_id);
+
+                argv = strv_new(mkfs,
+                                "-q",
+                                "-L", label,
+                                "-m", j,
+                                "-m", "reflink=1",
+                                node);
+                if (!argv)
+                        return log_oom();
+
+                if (!discard) {
+                        r = strv_extend(&argv, "-K");
+                        if (r < 0)
+                                return log_oom();
+                }
+
+                if (root) {
+                        r = make_protofile(root, &protofile);
+                        if (r < 0)
+                                return r;
+
+                        r = strv_extend_strv(&argv, STRV_MAKE("-p", protofile), false);
+                        if (r < 0)
+                                return log_oom();
+                }
+
+        } else if (streq(fstype, "vfat"))
+
+                argv = strv_new(mkfs,
+                                "-i", vol_id,
+                                "-n", label,
+                                "-F", "32",  /* yes, we force FAT32 here */
+                                node);
+
+        else if (streq(fstype, "swap"))
+                /* TODO: add --quiet here if
+                 * https://github.com/util-linux/util-linux/issues/1499 resolved. */
+
+                argv = strv_new(mkfs,
+                                "-L", label,
+                                "-U", vol_id,
+                                node);
+
+        else if (streq(fstype, "squashfs"))
+
+                argv = strv_new(mkfs,
+                                root, node,
+                                "-quiet",
+                                "-noappend");
+        else
+                /* Generic fallback for all other file systems */
+                argv = strv_new(mkfs, node);
+
+        if (!argv)
+                return log_oom();
+
+        if (root && stat(root, &st) < 0)
+                return log_error_errno(errno, "Failed to stat %s: %m", root);
+
+        r = safe_fork("(mkfs)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|(root ? FORK_NEW_USERNS : 0), NULL);
         if (r < 0)
                 return r;
         if (r == 0) {
                 /* Child */
 
-                /* When changing this conditional, also adjust the log statement below. */
-                if (streq(fstype, "ext2"))
-                        (void) execlp(mkfs, mkfs,
-                                      "-q",
-                                      "-L", label,
-                                      "-U", vol_id,
-                                      "-I", "256",
-                                      "-m", "0",
-                                      "-E", discard ? "discard,lazy_itable_init=1" : "nodiscard,lazy_itable_init=1",
-                                      node, NULL);
-
-                else if (STR_IN_SET(fstype, "ext3", "ext4"))
-                        (void) execlp(mkfs, mkfs,
-                                      "-q",
-                                      "-L", label,
-                                      "-U", vol_id,
-                                      "-I", "256",
-                                      "-O", "has_journal",
-                                      "-m", "0",
-                                      "-E", discard ? "discard,lazy_itable_init=1" : "nodiscard,lazy_itable_init=1",
-                                      node, NULL);
-
-                else if (streq(fstype, "btrfs")) {
-                        (void) execlp(mkfs, mkfs,
-                                      "-q",
-                                      "-L", label,
-                                      "-U", vol_id,
-                                      node,
-                                      discard ? NULL : "--nodiscard",
-                                      NULL);
-
-                } else if (streq(fstype, "f2fs")) {
-                        (void) execlp(mkfs, mkfs,
-                                      "-q",
-                                      "-g",  /* "default options" */
-                                      "-f",  /* force override, without this it doesn't seem to want to write to an empty partition */
-                                      "-l", label,
-                                      "-U", vol_id,
-                                      "-t", one_zero(discard),
-                                      node,
-                                      NULL);
-
-                } else if (streq(fstype, "xfs")) {
-                        const char *j;
-
-                        j = strjoina("uuid=", vol_id);
-
-                        (void) execlp(mkfs, mkfs,
-                                      "-q",
-                                      "-L", label,
-                                      "-m", j,
-                                      "-m", "reflink=1",
-                                      node,
-                                      discard ? NULL : "-K",
-                                      NULL);
-
-                } else if (streq(fstype, "vfat"))
-
-                        (void) execlp(mkfs, mkfs,
-                                      "-i", vol_id,
-                                      "-n", label,
-                                      "-F", "32",  /* yes, we force FAT32 here */
-                                      node, NULL);
-
-                else if (streq(fstype, "swap"))
-                        /* TODO: add --quiet here if
-                         * https://github.com/util-linux/util-linux/issues/1499 resolved. */
-
-                        (void) execlp(mkfs, mkfs,
-                                      "-L", label,
-                                      "-U", vol_id,
-                                      node, NULL);
-
-                else if (streq(fstype, "squashfs"))
-
-                        (void) execlp(mkfs, mkfs,
-                                      root, node,
-                                      "-quiet",
-                                      "-noappend",
-                                      NULL);
-                else
-                        /* Generic fallback for all other file systems */
-                        (void) execlp(mkfs, mkfs, node, NULL);
+                if (root) {
+                        r = setup_userns(st.st_uid, st.st_gid);
+                        if (r < 0)
+                                _exit(EXIT_FAILURE);
+                }
+
+                execvp(mkfs, argv);
 
                 log_error_errno(errno, "Failed to execute %s: %m", mkfs);
 
                 _exit(EXIT_FAILURE);
         }
 
+        if (root && streq(fstype, "vfat")) {
+                r = do_mcopy(node, root);
+                if (r < 0)
+                        return r;
+        }
+
         if (STR_IN_SET(fstype, "ext2", "ext3", "ext4", "btrfs", "f2fs", "xfs", "vfat", "swap"))
                 log_info("%s successfully formatted as %s (label \"%s\", uuid %s)",
                          node, fstype, label, vol_id);
index 3125ef176d6babd4cfbdaac7753a9db79200625b..62061c6647d2684bc48ad4aa23d993e0cdcaf7ca 100644 (file)
@@ -9,4 +9,6 @@
 
 int mkfs_exists(const char *fstype);
 
+int mkfs_supports_root_option(const char *fstype);
+
 int make_filesystem(const char *node, const char *fstype, const char *label, const char *root, sd_id128_t uuid, bool discard);
index 1526f59b0aa028fc1ea8b6e51d7e610a699dbc56..951701d497ef40320d634c238873bdc22f328563 100644 (file)
@@ -3,11 +3,47 @@
 #include <errno.h>
 
 #include "module-util.h"
+#include "proc-cmdline.h"
+#include "strv.h"
+
+static int denylist_modules(const char *p, char ***denylist) {
+        _cleanup_strv_free_ char **k = NULL;
+
+        assert(p);
+        assert(denylist);
+
+        k = strv_split(p, ",");
+        if (!k)
+                return -ENOMEM;
+
+        if (strv_extend_strv(denylist, k, true) < 0)
+                return -ENOMEM;
+
+        return 0;
+}
+
+static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
+        int r;
+
+        if (proc_cmdline_key_streq(key, "module_blacklist")) {
+
+                if (proc_cmdline_value_missing(key, value))
+                        return 0;
+
+                r = denylist_modules(value, data);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
 
 int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose) {
         const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST;
         struct kmod_list *itr;
         _cleanup_(kmod_module_unref_listp) struct kmod_list *modlist = NULL;
+        _cleanup_strv_free_ char **denylist = NULL;
+        bool denylist_parsed = false;
         int r;
 
         /* verbose==true means we should log at non-debug level if we
@@ -50,10 +86,27 @@ int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose)
                                          "Inserted module '%s'", kmod_module_get_name(mod));
                         else if (err == KMOD_PROBE_APPLY_BLACKLIST)
                                 log_full(verbose ? LOG_INFO : LOG_DEBUG,
-                                         "Module '%s' is deny-listed", kmod_module_get_name(mod));
+                                         "Module '%s' is deny-listed (by kmod)", kmod_module_get_name(mod));
                         else {
                                 assert(err < 0);
 
+                                if (err == -EPERM) {
+                                        if (!denylist_parsed) {
+                                                r = proc_cmdline_parse(parse_proc_cmdline_item, &denylist, 0);
+                                                if (r < 0)
+                                                        log_full_errno(!verbose ? LOG_DEBUG : LOG_WARNING,
+                                                                       r,
+                                                                       "Failed to parse kernel command line, ignoring: %m");
+
+                                                denylist_parsed = true;
+                                        }
+                                        if (strv_contains(denylist, kmod_module_get_name(mod))) {
+                                                log_full(verbose ? LOG_INFO : LOG_DEBUG,
+                                                         "Module '%s' is deny-listed (by kernel)", kmod_module_get_name(mod));
+                                                continue;
+                                        }
+                                }
+
                                 log_full_errno(!verbose ? LOG_DEBUG :
                                                err == -ENODEV ? LOG_NOTICE :
                                                err == -ENOENT ? LOG_WARNING :
index 312005f7e21f7e1587c38fe8237447ba57aec336..ac185d180b0b638a724240781b782b7821315433 100644 (file)
@@ -8,7 +8,7 @@
 int resize_fs(int fd, uint64_t sz, uint64_t *ret_size);
 
 #define BTRFS_MINIMAL_SIZE (256U*1024U*1024U)
-#define XFS_MINIMAL_SIZE (14U*1024U*1024U)
+#define XFS_MINIMAL_SIZE (16U*1024U*1024U)
 #define EXT4_MINIMAL_SIZE (1024U*1024U)
 
 uint64_t minimal_size_by_fs_magic(statfs_f_type_t magic);
index d9ab387301d1ca5f704e52ec6d8d840e60f8b370..e58173d864dad8976d2b4d4e66b6f6fded29b381 100644 (file)
@@ -25,10 +25,11 @@ typedef enum ResolveSupport ResolveSupport;
 typedef enum DnssecMode DnssecMode;
 typedef enum DnsOverTlsMode DnsOverTlsMode;
 
+/* Do not change the order, see link_get_llmnr_support() or link_get_mdns_support(). */
 enum ResolveSupport {
         RESOLVE_SUPPORT_NO,
-        RESOLVE_SUPPORT_YES,
         RESOLVE_SUPPORT_RESOLVE,
+        RESOLVE_SUPPORT_YES,
         _RESOLVE_SUPPORT_MAX,
         _RESOLVE_SUPPORT_INVALID = -EINVAL,
 };
index 86d3e57b0e543cbaf5a078a120f69c25272f8be5..24fd9a2aa2d2650984ac81cde2f4be13604ba658 100644 (file)
@@ -5,7 +5,6 @@
 
 #include "alloc-util.h"
 #include "errno-util.h"
-#include "fd-util.h"
 
 typedef enum RemoveFlags {
         REMOVE_ONLY_DIRECTORIES = 1 << 0, /* Only remove empty directories, no files */
@@ -52,19 +51,3 @@ static inline char *rm_rf_subvolume_and_free(char *p) {
         return mfree(p);
 }
 DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_subvolume_and_free);
-
-static inline int rm_rf_physical_and_close(int fd) {
-        _cleanup_free_ char *p = NULL;
-
-        if (fd < 0)
-                return -1;
-
-        if (fd_get_path(fd, &p) < 0)
-                return safe_close(fd);
-
-        safe_close(fd);
-        (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_MISSING_OK|REMOVE_CHMOD);
-
-        return -1;
-}
-DEFINE_TRIVIAL_CLEANUP_FUNC(int, rm_rf_physical_and_close);
index dfac4da7cc364c057be18c882dc7c9ebaac5198d..8ece7f163f1be53370957aff2e2cfc61bce90814 100644 (file)
@@ -1039,7 +1039,6 @@ static int add_syscall_filter_set(
                 bool log_missing,
                 char ***added) {
 
-        const char *sys;
         int r;
 
         /* Any syscalls that are handled are added to the *added strv. It needs to be initialized. */
@@ -1169,7 +1168,6 @@ int seccomp_parse_syscall_filter(
 
         if (name[0] == '@') {
                 const SyscallFilterSet *set;
-                const char *i;
 
                 set = syscall_filter_set_find(name);
                 if (!set) {
@@ -1909,7 +1907,6 @@ int parse_syscall_archs(char **l, Set **ret_archs) {
 }
 
 int seccomp_filter_set_add(Hashmap *filter, bool add, const SyscallFilterSet *set) {
-        const char *i;
         int r;
 
         assert(set);
@@ -2308,7 +2305,6 @@ int seccomp_suppress_sync(void) {
 
         SECCOMP_FOREACH_LOCAL_ARCH(arch) {
                 _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL;
-                const char *c;
 
                 r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW);
                 if (r < 0)
index d37c0b08456972fb097ef6a517f760467ef61e48..17c5038b51b77a4fc9f07b1f2c1bc508f2379801 100644 (file)
@@ -14,6 +14,8 @@ int fopen_temporary_label(
 
         int r;
 
+        assert(path);
+
         r = mac_selinux_create_file_prepare(target, S_IFREG);
         if (r < 0)
                 return r;
index ba8a23e18cad7efc96df885555c670662d50e13d..327caa439f0b3a71052ca5d93056cb0ef25c6bed 100644 (file)
@@ -152,8 +152,19 @@ int tpm2_context_init(const char *device, struct tpm2_context *ret) {
         if (r < 0)
                 return log_error_errno(r, "TPM2 support not installed: %m");
 
-        if (!device)
+        if (!device) {
                 device = secure_getenv("SYSTEMD_TPM2_DEVICE");
+                if (device)
+                        /* Setting the env var to an empty string forces tpm2-tss' own device picking
+                         * logic to be used. */
+                        device = empty_to_null(device);
+                else
+                        /* If nothing was specified explicitly, we'll use a hardcoded default: the "device" tcti
+                         * driver and the "/dev/tpmrm0" device. We do this since on some distributions the tpm2-abrmd
+                         * might be used and we really don't want that, since it is a system service and that creates
+                         * various ordering issues/deadlocks during early boot. */
+                        device = "device:/dev/tpmrm0";
+        }
 
         if (device) {
                 const char *param, *driver, *fn;
@@ -163,15 +174,27 @@ int tpm2_context_init(const char *device, struct tpm2_context *ret) {
 
                 param = strchr(device, ':');
                 if (param) {
+                        /* Syntax #1: Pair of driver string and arbitrary parameter */
                         driver = strndupa_safe(device, param - device);
+                        if (isempty(driver))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 driver name is empty, refusing.");
+
                         param++;
-                } else {
+                } else if (path_is_absolute(device) && path_is_valid(device)) {
+                        /* Syntax #2: TPM device node */
                         driver = "device";
                         param = device;
-                }
+                } else
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid TPM2 driver string, refusing.");
+
+                log_debug("Using TPM2 TCTI driver '%s' with device '%s'.", driver, param);
 
                 fn = strjoina("libtss2-tcti-", driver, ".so.0");
 
+                /* Better safe than sorry, let's refuse strings that cannot possibly be valid driver early, before going to disk. */
+                if (!filename_is_valid(fn))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 driver name '%s' not valid, refusing.", driver);
+
                 dl = dlopen(fn, RTLD_NOW);
                 if (!dl)
                         return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to load %s: %s", fn, dlerror());
@@ -1094,7 +1117,13 @@ static int tpm2_make_policy_session(
                                 ESYS_TR_NONE,
                                 NULL,
                                 &pubkey_tpm2,
+#if HAVE_TSS2_ESYS3
+                                /* tpm2-tss >= 3.0.0 requires a ESYS_TR_RH_* constant specifying the requested
+                                 * hierarchy, older versions need TPM2_RH_* instead. */
+                                ESYS_TR_RH_OWNER,
+#else
                                 TPM2_RH_OWNER,
+#endif
                                 &pubkey_handle);
                 if (rc != TSS2_RC_SUCCESS) {
                         r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
index aac02cd61bcaed4eebb0f92b46058e69e33867e9..7d953534520b9b7e04e9b96aa927d948f1252355 100644 (file)
@@ -642,9 +642,46 @@ static int device_is_power_sink(sd_device *device) {
         return found_sink || !found_source;
 }
 
+static bool battery_is_discharging(sd_device *d) {
+        const char *val;
+        int r;
+
+        assert(d);
+
+        r = sd_device_get_sysattr_value(d, "scope", &val);
+        if (r < 0) {
+                if (r != -ENOENT)
+                        log_device_debug_errno(d, r, "Failed to read 'scope' sysfs attribute, ignoring: %m");
+        } else if (streq(val, "Device")) {
+                log_device_debug(d, "The power supply is a device battery, ignoring device.");
+                return false;
+        }
+
+        r = device_get_sysattr_bool(d, "present");
+        if (r < 0)
+                log_device_debug_errno(d, r, "Failed to read 'present' sysfs attribute, assuming the battery is present: %m");
+        else if (r == 0) {
+                log_device_debug(d, "The battery is not present, ignoring the power supply.");
+                return false;
+        }
+
+        /* Possible values: "Unknown", "Charging", "Discharging", "Not charging", "Full" */
+        r = sd_device_get_sysattr_value(d, "status", &val);
+        if (r < 0) {
+                log_device_debug_errno(d, r, "Failed to read 'status' sysfs attribute, assuming the battery is discharging: %m");
+                return true;
+        }
+        if (!streq(val, "Discharging")) {
+                log_device_debug(d, "The battery status is '%s', assuming the battery is not used as a power source of this machine.", val);
+                return false;
+        }
+
+        return true;
+}
+
 int on_ac_power(void) {
         _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
-        bool found_ac_online = false, found_battery = false;
+        bool found_ac_online = false, found_discharging_battery = false;
         sd_device *d;
         int r;
 
@@ -686,17 +723,10 @@ int on_ac_power(void) {
                 }
 
                 if (streq(val, "Battery")) {
-                        r = sd_device_get_sysattr_value(d, "scope", &val);
-                        if (r < 0) {
-                                if (r != -ENOENT)
-                                        log_device_debug_errno(d, r, "Failed to read 'scope' sysfs attribute, ignoring: %m");
-                        } else if (streq(val, "Device")) {
-                                log_device_debug(d, "The power supply is a device battery, ignoring device.");
-                                continue;
+                        if (battery_is_discharging(d)) {
+                                found_discharging_battery = true;
+                                log_device_debug(d, "The power supply is a battery and currently discharging.");
                         }
-
-                        found_battery = true;
-                        log_device_debug(d, "The power supply is battery.");
                         continue;
                 }
 
@@ -713,11 +743,11 @@ int on_ac_power(void) {
         if (found_ac_online) {
                 log_debug("Found at least one online non-battery power supply, system is running on AC.");
                 return true;
-        } else if (found_battery) {
-                log_debug("Found battery and no online power sources, assuming system is running from battery.");
+        } else if (found_discharging_battery) {
+                log_debug("Found at least one discharging battery and no online power sources, assuming system is running from battery.");
                 return false;
         } else {
-                log_debug("No power supply reported online and no battery, assuming system is running on AC.");
+                log_debug("No power supply reported online and no discharging battery found, assuming system is running on AC.");
                 return true;
         }
 }
index 588c8b56c5cebc518e0e45a229e94452d92a694d..92f6ecaa8d044145b87ae87f91ec71c66e48f7d8 100644 (file)
@@ -6,6 +6,7 @@
 #include "env-util.h"
 #include "fd-util.h"
 #include "fuzz.h"
+#include "nulstr-util.h"
 #include "selinux-util.h"
 #include "static-destruct.h"
 #include "stdio-util.h"
index 8d3db98c0a5966e4659f0f77b86597b0d22d4acb..77dd075eb3074e3bdb3d32bc4d01edbded3347e3 100644 (file)
@@ -250,6 +250,7 @@ typedef struct UnitStatusInfo {
         uint64_t memory_high;
         uint64_t memory_max;
         uint64_t memory_swap_max;
+        uint64_t memory_zswap_max;
         uint64_t memory_limit;
         uint64_t memory_available;
         uint64_t cpu_usage_nsec;
@@ -700,6 +701,7 @@ static void print_status_info(
                 if (i->memory_min > 0 || i->memory_low > 0 ||
                     i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX ||
                     i->memory_swap_max != CGROUP_LIMIT_MAX ||
+                    i->memory_zswap_max != CGROUP_LIMIT_MAX ||
                     i->memory_available != CGROUP_LIMIT_MAX ||
                     i->memory_limit != CGROUP_LIMIT_MAX) {
                         const char *prefix = "";
@@ -725,6 +727,10 @@ static void print_status_info(
                                 printf("%sswap max: %s", prefix, FORMAT_BYTES(i->memory_swap_max));
                                 prefix = " ";
                         }
+                        if (i->memory_zswap_max != CGROUP_LIMIT_MAX) {
+                                printf("%szswap max: %s", prefix, FORMAT_BYTES(i->memory_zswap_max));
+                                prefix = " ";
+                        }
                         if (i->memory_limit != CGROUP_LIMIT_MAX) {
                                 printf("%slimit: %s", prefix, FORMAT_BYTES(i->memory_limit));
                                 prefix = " ";
@@ -1935,6 +1941,7 @@ static int show_one(
                 { "MemoryHigh",                     "t",               NULL,           offsetof(UnitStatusInfo, memory_high)                       },
                 { "MemoryMax",                      "t",               NULL,           offsetof(UnitStatusInfo, memory_max)                        },
                 { "MemorySwapMax",                  "t",               NULL,           offsetof(UnitStatusInfo, memory_swap_max)                   },
+                { "MemoryZSwapMax",                 "t",               NULL,           offsetof(UnitStatusInfo, memory_zswap_max)                  },
                 { "MemoryLimit",                    "t",               NULL,           offsetof(UnitStatusInfo, memory_limit)                      },
                 { "CPUUsageNSec",                   "t",               NULL,           offsetof(UnitStatusInfo, cpu_usage_nsec)                    },
                 { "TasksCurrent",                   "t",               NULL,           offsetof(UnitStatusInfo, tasks_current)                     },
@@ -1969,6 +1976,7 @@ static int show_one(
                 .memory_high = CGROUP_LIMIT_MAX,
                 .memory_max = CGROUP_LIMIT_MAX,
                 .memory_swap_max = CGROUP_LIMIT_MAX,
+                .memory_zswap_max = CGROUP_LIMIT_MAX,
                 .memory_limit = UINT64_MAX,
                 .memory_available = CGROUP_LIMIT_MAX,
                 .cpu_usage_nsec = UINT64_MAX,
@@ -2207,9 +2215,10 @@ int verb_show(int argc, char *argv[], void *userdata) {
 
                 if (!arg_states && !arg_types) {
                         if (show_mode == SYSTEMCTL_SHOW_PROPERTIES)
-                                r = show_one(bus, "/org/freedesktop/systemd1", NULL, show_mode, &new_line, &ellipsized);
-                        else
-                                r = show_system_status(bus);
+                                /* systemctl show --all → show properties of the manager */
+                                return show_one(bus, "/org/freedesktop/systemd1", NULL, show_mode, &new_line, &ellipsized);
+
+                        r = show_system_status(bus);
                         if (r < 0)
                                 return r;
 
index fa46574fd6bbeec1d5ecfd5e459d3a98939575b3..33d0e584ba0706c1f8bf3d1e3daa20a427adf726 100644 (file)
@@ -111,6 +111,7 @@ int read_partition_info(
         struct fdisk_parttype *pt;
         uint64_t start, size, flags;
         sd_id128_t ptid, id;
+        GptPartitionType type;
         size_t partno;
         int r;
 
@@ -178,6 +179,8 @@ int read_partition_info(
         if (!label_copy)
                 return log_oom();
 
+        type = gpt_partition_type_from_uuid(ptid);
+
         *ret = (PartitionInfo) {
                 .partno = partno,
                 .start = start,
@@ -187,9 +190,9 @@ int read_partition_info(
                 .uuid = id,
                 .label = TAKE_PTR(label_copy),
                 .device = TAKE_PTR(device),
-                .no_auto = FLAGS_SET(flags, SD_GPT_FLAG_NO_AUTO) && gpt_partition_type_knows_no_auto(ptid),
-                .read_only = FLAGS_SET(flags, SD_GPT_FLAG_READ_ONLY) && gpt_partition_type_knows_read_only(ptid),
-                .growfs = FLAGS_SET(flags, SD_GPT_FLAG_GROWFS) && gpt_partition_type_knows_growfs(ptid),
+                .no_auto = FLAGS_SET(flags, SD_GPT_FLAG_NO_AUTO) && gpt_partition_type_knows_no_auto(type),
+                .read_only = FLAGS_SET(flags, SD_GPT_FLAG_READ_ONLY) && gpt_partition_type_knows_read_only(type),
+                .growfs = FLAGS_SET(flags, SD_GPT_FLAG_GROWFS) && gpt_partition_type_knows_growfs(type),
         };
 
         return 1; /* found! */
@@ -269,6 +272,7 @@ int patch_partition(
         _cleanup_(fdisk_unref_partitionp) struct fdisk_partition *pa = NULL;
         _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
         bool tweak_no_auto, tweak_read_only, tweak_growfs;
+        GptPartitionType type;
         int r, fd;
 
         assert(device);
@@ -313,16 +317,18 @@ int patch_partition(
                         return log_error_errno(r, "Failed to update partition UUID: %m");
         }
 
+        type = gpt_partition_type_from_uuid(info->type);
+
         /* Tweak the read-only flag, but only if supported by the partition type */
         tweak_no_auto =
                 FLAGS_SET(change, PARTITION_NO_AUTO) &&
-                gpt_partition_type_knows_no_auto(info->type);
+                gpt_partition_type_knows_no_auto(type);
         tweak_read_only =
                 FLAGS_SET(change, PARTITION_READ_ONLY) &&
-                gpt_partition_type_knows_read_only(info->type);
+                gpt_partition_type_knows_read_only(type);
         tweak_growfs =
                 FLAGS_SET(change, PARTITION_GROWFS) &&
-                gpt_partition_type_knows_growfs(info->type);
+                gpt_partition_type_knows_growfs(type);
 
         if (change & PARTITION_FLAGS) {
                 uint64_t flags;
index 8104e9c82e97fdc118f0d7aca7af030b76af0a7b..2814fdb6fa307a77eb52c602f3e3cb2f76bbc041 100644 (file)
@@ -8,6 +8,7 @@
 #include "blockdev-util.h"
 #include "chase-symlinks.h"
 #include "device-util.h"
+#include "devnum-util.h"
 #include "dirent-util.h"
 #include "env-util.h"
 #include "fd-util.h"
@@ -194,7 +195,7 @@ static int resource_load_from_blockdev(Resource *rr) {
                         continue;
 
                 /* Check if partition type matches */
-                if (rr->partition_type_set && !sd_id128_equal(pinfo.type, rr->partition_type))
+                if (rr->partition_type_set && !sd_id128_equal(pinfo.type, rr->partition_type.uuid))
                         continue;
 
                 /* A label of "_empty" means "not used so far" for us */
@@ -525,10 +526,14 @@ int resource_resolve_path(
         assert(rr);
 
         if (rr->path_auto) {
+                struct stat orig_root_stats;
 
-                /* NB: we don't actually check the backing device of the root fs "/", but of "/usr", in order
-                 * to support environments where the root fs is a tmpfs, and the OS itself placed exclusively
-                 * in /usr/. */
+                /* NB: If the root mount has been replaced by some form of volatile file system (overlayfs),
+                 * the original root block device node is symlinked in /run/systemd/volatile-root. Let's
+                 * follow that link here. If that doesn't exist, we check the backing device of "/usr". We
+                 * don't actually check the backing device of the root fs "/", in order to support
+                 * environments where the root fs is a tmpfs, and the OS itself placed exclusively in
+                 * /usr/. */
 
                 if (rr->type != RESOURCE_PARTITION)
                         return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
@@ -546,7 +551,16 @@ int resource_resolve_path(
                         return log_error_errno(SYNTHETIC_ERRNO(EPERM),
                                                "Block device is not allowed when using --root= mode.");
 
-                r = get_block_device_harder("/usr/", &d);
+                r = stat("/run/systemd/volatile-root", &orig_root_stats);
+                if (r < 0) {
+                        if (errno == -ENOENT) /* volatile-root not found */
+                                r = get_block_device_harder("/usr/", &d);
+                        else
+                                return log_error_errno(r, "Failed to stat /run/systemd/volatile-root: %m");
+                } else if (!S_ISBLK(orig_root_stats.st_mode)) /* symlink was present but not block device */
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "/run/systemd/volatile-root is not linked to a block device.");
+                else /* symlink was present and a block device */
+                        d = orig_root_stats.st_rdev;
 
         } else if (rr->type == RESOURCE_PARTITION) {
                 _cleanup_close_ int fd = -1, real_fd = -1;
index 86be0d338936eff59628a61e2d39abb8fac64438..3209988c24fcd801e2f88d0b2f877a416f39dd9c 100644 (file)
@@ -5,8 +5,7 @@
 #include <stdbool.h>
 #include <sys/types.h>
 
-#include "sd-id128.h"
-
+#include "gpt.h"
 #include "hashmap.h"
 #include "macro.h"
 
@@ -74,7 +73,7 @@ struct Resource {
         char *path;
         bool path_auto; /* automatically find root path (only available if target resource, not source resource) */
         char **patterns;
-        sd_id128_t partition_type;
+        GptPartitionType partition_type;
         bool partition_type_set;
 
         /* All instances of this resource we found */
index d6705cd12ec4316c0adc71579e3c2bb32c23a208..0c3d65a00dcbe9fd826bdf6db07fe6a9626a30bf 100644 (file)
@@ -344,7 +344,7 @@ static int config_parse_resource_ptype(
 
         assert(rvalue);
 
-        r = gpt_partition_type_uuid_from_string(rvalue, &rr->partition_type);
+        r = gpt_partition_type_from_string(rvalue, &rr->partition_type);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed parse partition type, ignoring: %s", rvalue);
@@ -654,18 +654,18 @@ int transfer_vacuum(
                 if (t->target.n_empty + t->target.n_instances < 2)
                         return log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
                                                "Partition table has less than two partition slots of the right type " SD_ID128_UUID_FORMAT_STR " (%s), refusing.",
-                                               SD_ID128_FORMAT_VAL(t->target.partition_type),
-                                               gpt_partition_type_uuid_to_string(t->target.partition_type));
+                                               SD_ID128_FORMAT_VAL(t->target.partition_type.uuid),
+                                               gpt_partition_type_uuid_to_string(t->target.partition_type.uuid));
                 if (space > t->target.n_empty + t->target.n_instances)
                         return log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
                                                "Partition table does not have enough partition slots of right type " SD_ID128_UUID_FORMAT_STR " (%s) for operation.",
-                                               SD_ID128_FORMAT_VAL(t->target.partition_type),
-                                               gpt_partition_type_uuid_to_string(t->target.partition_type));
+                                               SD_ID128_FORMAT_VAL(t->target.partition_type.uuid),
+                                               gpt_partition_type_uuid_to_string(t->target.partition_type.uuid));
                 if (space == t->target.n_empty + t->target.n_instances)
                         return log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
                                                "Asked to empty all partition table slots of the right type " SD_ID128_UUID_FORMAT_STR " (%s), can't allow that. One instance must always remain.",
-                                               SD_ID128_FORMAT_VAL(t->target.partition_type),
-                                               gpt_partition_type_uuid_to_string(t->target.partition_type));
+                                               SD_ID128_FORMAT_VAL(t->target.partition_type.uuid),
+                                               gpt_partition_type_uuid_to_string(t->target.partition_type.uuid));
 
                 rm = LESS_BY(space, t->target.n_empty);
                 remain = LESS_BY(t->target.n_instances, rm);
@@ -858,7 +858,7 @@ int transfer_acquire_instance(Transfer *t, Instance *i) {
                 r = find_suitable_partition(
                                 t->target.path,
                                 i->metadata.size,
-                                t->target.partition_type_set ? &t->target.partition_type : NULL,
+                                t->target.partition_type_set ? &t->target.partition_type.uuid : NULL,
                                 &t->partition_info);
                 if (r < 0)
                         return r;
index 9d193651edb88c9dd6cfc4a29e9eb2dc55c100f8..4f72e00f57da25a3e64810cf0e3ed4d0acdce04a 100644 (file)
@@ -557,6 +557,8 @@ tests += [
 
         [files('test-strv.c')],
 
+        [files('test-nulstr-util.c')],
+
         [files('test-path-util.c')],
 
         [files('test-rm-rf.c')],
index 3f8225cbcc1a48832e081d91adc15eee4dd6ea08..da0f5e137a75ea669301101d080ae27fc9985ad1 100644 (file)
@@ -158,7 +158,6 @@ int main(int argc, char *argv[]) {
         else
                 arg_start = getpid_cached();
 
-        const char *i;
         NULSTR_FOREACH(i, "zeros\0simple\0random\0") {
 #if HAVE_XZ
                 test_compress_decompress("XZ", i, compress_blob_xz, decompress_blob_xz);
index 4cd23d8e2135bba4ec65f986a66894bd5aa9f9ab..5144eb4687298a55433ef60ae202120e84edd955 100644 (file)
@@ -800,7 +800,6 @@ TEST(print_securities) {
 
 TEST(condition_test_virtualization) {
         Condition *condition;
-        const char *virt;
         int r;
 
         condition = condition_new(CONDITION_VIRTUALIZATION, "garbage oifdsjfoidsjoj", false, false);
index 00a38b18f6a690d53fe012e1f09541f90f3bb428..8177f6e33b87e50dca66e806b4db73b69ac80d5b 100644 (file)
@@ -67,21 +67,22 @@ TEST(copy_tree_replace_file) {
 
         /* The file exists- now overwrite original contents, and test the COPY_REPLACE flag. */
 
-        assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK) == -EEXIST);
+        assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK, NULL) == -EEXIST);
 
         assert_se(read_file_at_and_streq(AT_FDCWD, dst, "foo foo foo\n"));
 
-        assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE) == 0);
+        assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE, NULL) == 0);
 
         assert_se(read_file_at_and_streq(AT_FDCWD, dst, "bar bar\n"));
 }
 
 TEST(copy_tree_replace_dirs) {
-        _cleanup_(rm_rf_physical_and_closep) int src = -1, dst = -1;
+        _cleanup_(rm_rf_physical_and_freep) char *srcp = NULL, *dstp = NULL;
+        _cleanup_close_ int src = -1, dst = -1;
 
         /* Create the random source/destination directories */
-        assert_se((src = mkdtemp_open(NULL, 0, NULL)) >= 0);
-        assert_se((dst = mkdtemp_open(NULL, 0, NULL)) >= 0);
+        assert_se((src = mkdtemp_open(NULL, 0, &srcp)) >= 0);
+        assert_se((dst = mkdtemp_open(NULL, 0, &dstp)) >= 0);
 
         /* Populate some data to differentiate the files. */
         assert_se(write_string_file_at(src, "foo", "src file 1", WRITE_STRING_FILE_CREATE) >= 0);
@@ -91,14 +92,14 @@ TEST(copy_tree_replace_dirs) {
         assert_se(write_string_file_at(dst, "bar", "dest file 2", WRITE_STRING_FILE_CREATE) == 0);
 
         /* Copying without COPY_REPLACE should fail because the destination file already exists. */
-        assert_se(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK) == -EEXIST);
+        assert_se(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK, NULL) == -EEXIST);
 
         assert_se(read_file_at_and_streq(src, "foo", "src file 1\n"));
         assert_se(read_file_at_and_streq(src, "bar", "src file 2\n"));
         assert_se(read_file_at_and_streq(dst, "foo", "dest file 1\n"));
         assert_se(read_file_at_and_streq(dst, "bar", "dest file 2\n"));
 
-        assert_se(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE) == 0);
+        assert_se(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE, NULL) == 0);
 
         assert_se(read_file_at_and_streq(src, "foo", "src file 1\n"));
         assert_se(read_file_at_and_streq(src, "bar", "src file 2\n"));
@@ -131,6 +132,8 @@ TEST(copy_file_fd) {
 }
 
 TEST(copy_tree) {
+        _cleanup_set_free_ Set *denylist = NULL;
+        _cleanup_free_ char *cp = NULL;
         char original_dir[] = "/tmp/test-copy_tree/";
         char copy_dir[] = "/tmp/test-copy_tree-copy/";
         char **files = STRV_MAKE("file", "dir1/file", "dir1/dir2/file", "dir1/dir2/dir3/dir4/dir5/file");
@@ -138,7 +141,7 @@ TEST(copy_tree) {
                                     "link2", "dir1/file");
         char **hardlinks = STRV_MAKE("hlink", "file",
                                      "hlink2", "dir1/file");
-        const char *unixsockp;
+        const char *unixsockp, *ignorep;
         struct stat st;
         int xattr_worked = -1; /* xattr support is optional in temporary directories, hence use it if we can,
                                 * but don't fail if we can't */
@@ -184,7 +187,14 @@ TEST(copy_tree) {
         unixsockp = strjoina(original_dir, "unixsock");
         assert_se(mknod(unixsockp, S_IFSOCK|0644, 0) >= 0);
 
-        assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS) == 0);
+        ignorep = strjoina(original_dir, "ignore/file");
+        assert_se(write_string_file(ignorep, "ignore", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) == 0);
+        assert_se(RET_NERRNO(stat(ignorep, &st)) >= 0);
+        assert_se(cp = memdup(&st, sizeof(st)));
+        assert_se(set_ensure_put(&denylist, &inode_hash_ops, cp) >= 0);
+        TAKE_PTR(cp);
+
+        assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS, denylist) == 0);
 
         STRV_FOREACH(p, files) {
                 _cleanup_free_ char *buf, *f, *c = NULL;
@@ -236,8 +246,11 @@ TEST(copy_tree) {
         assert_se(stat(unixsockp, &st) >= 0);
         assert_se(S_ISSOCK(st.st_mode));
 
-        assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0);
-        assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0);
+        assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK, denylist) < 0);
+        assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK, denylist) < 0);
+
+        ignorep = strjoina(copy_dir, "ignore/file");
+        assert_se(RET_NERRNO(access(ignorep, F_OK)) == -ENOENT);
 
         (void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
         (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
index 60d5fee71fc2b452c2917ca79809ca3ff8d344ea..14341b97b4ea3960f43692efa2132863303025d8 100644 (file)
@@ -534,6 +534,37 @@ TEST(table) {
                                 "5min              5min              \n"));
 }
 
+TEST(vertical) {
+        _cleanup_(table_unrefp) Table *t = NULL;
+        _cleanup_free_ char *formatted = NULL;
+
+        assert_se(t = table_new_vertical());
+
+        assert_se(table_add_many(t,
+                                 TABLE_FIELD, "pfft aa", TABLE_STRING, "foo",
+                                 TABLE_FIELD, "uuu o", TABLE_SIZE, UINT64_C(1024),
+                                 TABLE_FIELD, "lllllllllllo", TABLE_STRING, "jjjjjjjjjjjjjjjjj") >= 0);
+
+        assert_se(table_set_json_field_name(t, 1, "dimpfelmoser") >= 0);
+
+        assert_se(table_format(t, &formatted) >= 0);
+
+        assert_se(streq(formatted,
+                        "     pfft aa: foo\n"
+                        "       uuu o: 1.0K\n"
+                        "lllllllllllo: jjjjjjjjjjjjjjjjj\n"));
+
+        _cleanup_(json_variant_unrefp) JsonVariant *a = NULL, *b = NULL;
+        assert_se(table_to_json(t, &a) >= 0);
+
+        assert_se(json_build(&b, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR("pfft_aa", JSON_BUILD_STRING("foo")),
+                                             JSON_BUILD_PAIR("dimpfelmoser", JSON_BUILD_UNSIGNED(1024)),
+                                             JSON_BUILD_PAIR("lllllllllllo", JSON_BUILD_STRING("jjjjjjjjjjjjjjjjj")))) >= 0);
+
+        assert_se(json_variant_equal(a, b));
+}
+
 static int intro(void) {
         assert_se(setenv("SYSTEMD_COLORS", "0", 1) >= 0);
         assert_se(setenv("COLUMNS", "40", 1) >= 0);
index d0259843b6723a43d57e4380be5299855b83c028..4bf0a5daf8765c85d4fcf8b9389addf166cf8a8a 100644 (file)
@@ -386,6 +386,15 @@ TEST(chase_symlinks) {
         assert_se(path_equal(path_startswith(result, p), "usr"));
         result = mfree(result);
 
+        /* Test CHASE_PROHIBIT_SYMLINKS */
+
+        assert_se(chase_symlinks("top/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
+        assert_se(chase_symlinks("top/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
+        assert_se(chase_symlinks("top/dotdot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
+        assert_se(chase_symlinks("top/dotdot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
+        assert_se(chase_symlinks("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
+        assert_se(chase_symlinks("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
+
  cleanup:
         assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
 }
index 8c313c66cc894e4070dce7cb8d1c361f6b345cdd..377b79f1556d36cad898eca2e69187df27c0b87e 100644 (file)
@@ -19,13 +19,13 @@ TEST(gpt_types_against_architectures) {
                 for (Architecture a = 0; a < _ARCHITECTURE_MAX; a++)
                         FOREACH_STRING(suffix, "", "-verity", "-verity-sig") {
                                 _cleanup_free_ char *joined = NULL;
-                                sd_id128_t id;
+                                GptPartitionType type;
 
                                 joined = strjoin(prefix, architecture_to_string(a), suffix);
                                 if (!joined)
                                         return (void) log_oom();
 
-                                r = gpt_partition_type_uuid_from_string(joined, &id);
+                                r = gpt_partition_type_from_string(joined, &type);
                                 if (r < 0) {
                                         printf("%s %s\n", RED_CROSS_MARK(), joined);
                                         continue;
@@ -34,15 +34,15 @@ TEST(gpt_types_against_architectures) {
                                 printf("%s %s\n", GREEN_CHECK_MARK(), joined);
 
                                 if (streq(prefix, "root-") && streq(suffix, ""))
-                                        assert_se(gpt_partition_type_is_root(id));
+                                        assert_se(type.designator == PARTITION_ROOT);
                                 if (streq(prefix, "root-") && streq(suffix, "-verity"))
-                                        assert_se(gpt_partition_type_is_root_verity(id));
+                                        assert_se(type.designator == PARTITION_ROOT_VERITY);
                                 if (streq(prefix, "usr-") && streq(suffix, ""))
-                                        assert_se(gpt_partition_type_is_usr(id));
+                                        assert_se(type.designator == PARTITION_USR);
                                 if (streq(prefix, "usr-") && streq(suffix, "-verity"))
-                                        assert_se(gpt_partition_type_is_usr_verity(id));
+                                        assert_se(type.designator == PARTITION_USR_VERITY);
 
-                                assert_se(gpt_partition_type_uuid_to_arch(id) == a);
+                                assert_se(type.arch == a);
                         }
 }
 
index 36a775012b3c742971e2d56628ba997f3937bbda..fea20d82a6783245155b94252aadfd57361438ca 100644 (file)
@@ -452,8 +452,8 @@ TEST(hashmap_foreach_key) {
 
         m = hashmap_new(&string_hash_ops);
 
-        NULSTR_FOREACH(key, key_table)
-                hashmap_put(m, key, (void*) (const char*) "my dummy val");
+        NULSTR_FOREACH(k, key_table)
+                hashmap_put(m, k, (void*) (const char*) "my dummy val");
 
         HASHMAP_FOREACH_KEY(s, key, m) {
                 assert_se(s);
diff --git a/src/test/test-nulstr-util.c b/src/test/test-nulstr-util.c
new file mode 100644 (file)
index 0000000..a068e5f
--- /dev/null
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "nulstr-util.h"
+#include "strv.h"
+#include "tests.h"
+
+TEST(strv_split_nulstr) {
+        _cleanup_strv_free_ char **l = NULL;
+        const char nulstr[] = "str0\0str1\0str2\0str3\0";
+
+        l = strv_split_nulstr(nulstr);
+        assert_se(l);
+
+        assert_se(streq(l[0], "str0"));
+        assert_se(streq(l[1], "str1"));
+        assert_se(streq(l[2], "str2"));
+        assert_se(streq(l[3], "str3"));
+}
+
+TEST(strv_parse_nulstr) {
+        _cleanup_strv_free_ char **l = NULL;
+        const char nulstr[] = "hoge\0hoge2\0hoge3\0\0hoge5\0\0xxx";
+
+        l = strv_parse_nulstr(nulstr, sizeof(nulstr)-1);
+        assert_se(l);
+        puts("Parse nulstr:");
+        strv_print(l);
+
+        assert_se(streq(l[0], "hoge"));
+        assert_se(streq(l[1], "hoge2"));
+        assert_se(streq(l[2], "hoge3"));
+        assert_se(streq(l[3], ""));
+        assert_se(streq(l[4], "hoge5"));
+        assert_se(streq(l[5], ""));
+        assert_se(streq(l[6], "xxx"));
+        strv_free(l);
+
+        l = strv_parse_nulstr((const char[0]) {}, 0);
+        assert_se(l);
+        assert_se(strv_isempty(l));
+        strv_free(l);
+
+        l = strv_parse_nulstr((const char[1]) { 0 }, 1);
+        assert_se(l);
+        assert_se(strv_equal(l, STRV_MAKE("")));
+        strv_free(l);
+
+        l = strv_parse_nulstr((const char[1]) { 'x' }, 1);
+        assert_se(l);
+        assert_se(strv_equal(l, STRV_MAKE("x")));
+        strv_free(l);
+
+        l = strv_parse_nulstr((const char[2]) { 0, 0 }, 2);
+        assert_se(l);
+        assert_se(strv_equal(l, STRV_MAKE("", "")));
+        strv_free(l);
+
+        l = strv_parse_nulstr((const char[2]) { 'x', 0 }, 2);
+        assert_se(l);
+        assert_se(strv_equal(l, STRV_MAKE("x")));
+        strv_free(l);
+
+        l = strv_parse_nulstr((const char[3]) { 0, 0, 0 }, 3);
+        assert_se(l);
+        assert_se(strv_equal(l, STRV_MAKE("", "", "")));
+        strv_free(l);
+
+        l = strv_parse_nulstr((const char[3]) { 'x', 0, 0 }, 3);
+        assert_se(l);
+        assert_se(strv_equal(l, STRV_MAKE("x", "")));
+        strv_free(l);
+
+        l = strv_parse_nulstr((const char[3]) { 0, 'x', 0 }, 3);
+        assert_se(l);
+        assert_se(strv_equal(l, STRV_MAKE("", "x")));
+        strv_free(l);
+
+        l = strv_parse_nulstr((const char[3]) { 0, 0, 'x' }, 3);
+        assert_se(l);
+        assert_se(strv_equal(l, STRV_MAKE("", "", "x")));
+        strv_free(l);
+
+        l = strv_parse_nulstr((const char[3]) { 'x', 'x', 0 }, 3);
+        assert_se(l);
+        assert_se(strv_equal(l, STRV_MAKE("xx")));
+        strv_free(l);
+
+        l = strv_parse_nulstr((const char[3]) { 0, 'x', 'x' }, 3);
+        assert_se(l);
+        assert_se(strv_equal(l, STRV_MAKE("", "xx")));
+        strv_free(l);
+
+        l = strv_parse_nulstr((const char[3]) { 'x', 0, 'x' }, 3);
+        assert_se(l);
+        assert_se(strv_equal(l, STRV_MAKE("x", "x")));
+        strv_free(l);
+
+        l = strv_parse_nulstr((const char[3]) { 'x', 'x', 'x' }, 3);
+        assert_se(l);
+        assert_se(strv_equal(l, STRV_MAKE("xxx")));
+}
+
+static void test_strv_make_nulstr_one(char **l) {
+        _cleanup_free_ char *b = NULL, *c = NULL;
+        _cleanup_strv_free_ char **q = NULL;
+        size_t n, m;
+        unsigned i = 0;
+
+        log_info("/* %s */", __func__);
+
+        assert_se(strv_make_nulstr(l, &b, &n) >= 0);
+        assert_se(q = strv_parse_nulstr(b, n));
+        assert_se(strv_equal(l, q));
+
+        assert_se(strv_make_nulstr(q, &c, &m) >= 0);
+        assert_se(memcmp_nn(b, n, c, m) == 0);
+
+        NULSTR_FOREACH(s, b)
+                assert_se(streq(s, l[i++]));
+        assert_se(i == strv_length(l));
+}
+
+TEST(strv_make_nulstr) {
+        test_strv_make_nulstr_one(NULL);
+        test_strv_make_nulstr_one(STRV_MAKE(NULL));
+        test_strv_make_nulstr_one(STRV_MAKE("foo"));
+        test_strv_make_nulstr_one(STRV_MAKE("foo", "bar"));
+        test_strv_make_nulstr_one(STRV_MAKE("foo", "bar", "quuux"));
+}
+
+static void test_strv_make_nulstr_binary_one(char **l, const char *b, size_t n) {
+        _cleanup_strv_free_ char **z = NULL;
+        _cleanup_free_ char *a = NULL;
+        size_t m;
+
+        assert_se(strv_make_nulstr(l, &a, &m) >= 0);
+        assert_se(memcmp_nn(a, m, b, n) == 0);
+        assert_se(z = strv_parse_nulstr(a, m));
+        assert_se(strv_equal(l, z));
+}
+
+TEST(strv_make_nulstr_binary) {
+        test_strv_make_nulstr_binary_one(NULL, (const char[0]) {}, 0);
+        test_strv_make_nulstr_binary_one(STRV_MAKE(NULL), (const char[0]) {}, 0);
+        test_strv_make_nulstr_binary_one(STRV_MAKE(""), (const char[1]) { 0 }, 1);
+        test_strv_make_nulstr_binary_one(STRV_MAKE("", ""), (const char[2]) { 0, 0 }, 2);
+        test_strv_make_nulstr_binary_one(STRV_MAKE("x", ""), (const char[3]) { 'x', 0, 0 }, 3);
+        test_strv_make_nulstr_binary_one(STRV_MAKE("", "x"), (const char[3]) { 0, 'x', 0 }, 3);
+        test_strv_make_nulstr_binary_one(STRV_MAKE("", "", ""), (const char[3]) { 0, 0, 0 }, 3);
+        test_strv_make_nulstr_binary_one(STRV_MAKE("x", "", ""), (const char[4]) { 'x', 0, 0, 0 }, 4);
+        test_strv_make_nulstr_binary_one(STRV_MAKE("", "x", ""), (const char[4]) { 0, 'x', 0, 0 }, 4);
+        test_strv_make_nulstr_binary_one(STRV_MAKE("", "", "x"), (const char[4]) { 0, 0, 'x', 0 }, 4);
+        test_strv_make_nulstr_binary_one(STRV_MAKE("x", "x", ""), (const char[5]) { 'x', 0, 'x', 0, 0 }, 5);
+        test_strv_make_nulstr_binary_one(STRV_MAKE("", "x", "x"), (const char[5]) { 0, 'x', 0, 'x', 0 }, 5);
+        test_strv_make_nulstr_binary_one(STRV_MAKE("x", "", "x"), (const char[5]) { 'x', 0, 0, 'x', 0 }, 5);
+        test_strv_make_nulstr_binary_one(STRV_MAKE("x", "x", "x"), (const char[6]) { 'x', 0, 'x', 0, 'x', 0 }, 6);
+}
+
+DEFINE_TEST_MAIN(LOG_INFO);
index f18634cc34e32fc2251f2f2cd7db7f4d406056ba..ecb6118bcaf678f5531972b628bb734fb559731f 100644 (file)
@@ -56,7 +56,6 @@ TEST(basic_enumerate) {
 
 TEST(sd_hwdb_new_from_path) {
         _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
-        const char *hwdb_bin_path = NULL;
         int r;
 
         assert_se(sd_hwdb_new_from_path(NULL, &hwdb) == -EINVAL);
index 4c704badbd71ca0320b392f3e0b409a5459ab261..ae311c56868295d88aa44799008e7bae63ff5963 100644 (file)
@@ -117,7 +117,7 @@ TEST(seccomp_arch_to_string) {
 }
 
 TEST(architecture_table) {
-        const char *n, *n2;
+        const char *n2;
 
         NULSTR_FOREACH(n,
                        "native\0"
@@ -234,7 +234,7 @@ TEST(filter_sets_ordered) {
         assert_se(streq(syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].name, "@known"));
 
         for (size_t i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
-                const char *k, *p = NULL;
+                const char *p = NULL;
 
                 /* Make sure each group has a description */
                 assert_se(!isempty(syscall_filter_sets[0].help));
index 5c12a0597ad29321793e734761356371b315a813..f69e80ca5a84911b65342722c408b589c24fb0e3 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <stdlib.h>
 
+#include "nulstr-util.h"
 #include "strbuf.h"
 #include "string-util.h"
 #include "strv.h"
index b892396f05bf8c92a75802d0a964fa4952570b31..9208faafa47d22c482090a71d0be038a97e01955 100644 (file)
@@ -2,7 +2,6 @@
 
 #include "alloc-util.h"
 #include "escape.h"
-#include "nulstr-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "tests.h"
@@ -487,37 +486,6 @@ TEST(strv_split_newlines_full) {
         assert_se(strv_equal(l, (char**) input_table_retain_escape));
 }
 
-TEST(strv_split_nulstr) {
-        _cleanup_strv_free_ char **l = NULL;
-        const char nulstr[] = "str0\0str1\0str2\0str3\0";
-
-        l = strv_split_nulstr (nulstr);
-        assert_se(l);
-
-        assert_se(streq(l[0], "str0"));
-        assert_se(streq(l[1], "str1"));
-        assert_se(streq(l[2], "str2"));
-        assert_se(streq(l[3], "str3"));
-}
-
-TEST(strv_parse_nulstr) {
-        _cleanup_strv_free_ char **l = NULL;
-        const char nulstr[] = "hoge\0hoge2\0hoge3\0\0hoge5\0\0xxx";
-
-        l = strv_parse_nulstr(nulstr, sizeof(nulstr)-1);
-        assert_se(l);
-        puts("Parse nulstr:");
-        strv_print(l);
-
-        assert_se(streq(l[0], "hoge"));
-        assert_se(streq(l[1], "hoge2"));
-        assert_se(streq(l[2], "hoge3"));
-        assert_se(streq(l[3], ""));
-        assert_se(streq(l[4], "hoge5"));
-        assert_se(streq(l[5], ""));
-        assert_se(streq(l[6], "xxx"));
-}
-
 TEST(strv_overlap) {
         const char * const input_table[] = {
                 "one",
@@ -945,36 +913,6 @@ TEST(strv_extend_n) {
         assert_se(v[1] == NULL);
 }
 
-static void test_strv_make_nulstr_one(char **l) {
-        _cleanup_free_ char *b = NULL, *c = NULL;
-        _cleanup_strv_free_ char **q = NULL;
-        const char *s = NULL;
-        size_t n, m;
-        unsigned i = 0;
-
-        log_info("/* %s */", __func__);
-
-        assert_se(strv_make_nulstr(l, &b, &n) >= 0);
-        assert_se(q = strv_parse_nulstr(b, n));
-        assert_se(strv_equal(l, q));
-
-        assert_se(strv_make_nulstr(q, &c, &m) >= 0);
-        assert_se(m == n);
-        assert_se(memcmp(b, c, m) == 0);
-
-        NULSTR_FOREACH(s, b)
-                assert_se(streq(s, l[i++]));
-        assert_se(i == strv_length(l));
-}
-
-TEST(strv_make_nulstr) {
-        test_strv_make_nulstr_one(NULL);
-        test_strv_make_nulstr_one(STRV_MAKE(NULL));
-        test_strv_make_nulstr_one(STRV_MAKE("foo"));
-        test_strv_make_nulstr_one(STRV_MAKE("foo", "bar"));
-        test_strv_make_nulstr_one(STRV_MAKE("foo", "bar", "quuux"));
-}
-
 TEST(foreach_string) {
         const char * const t[] = {
                 "foo",
index c75957317391195db6391459dfe8a23668c1a700..186f6ee29c8d8e7b78efec123c1b0139754fba2b 100644 (file)
@@ -114,7 +114,7 @@ TEST(load_userns) {
                 assert_se(uid_range_covers(p, 0, UINT32_MAX));
         }
 
-        assert_se(fopen_temporary(NULL, &f, &fn) >= 0);
+        assert_se(fopen_temporary_child(NULL, &f, &fn) >= 0);
         fputs("0 0 20\n"
               "100 0 20\n", f);
         assert_se(fflush_and_check(f) >= 0);
index e3c722610a2ca9081ed9f831bf2b9c5463d2daa4..5d1be1150940a1cc4dee7589e781cab13154bf47 100644 (file)
@@ -61,15 +61,12 @@ static int print_status_info(const StatusInfo *i) {
 
         assert(i);
 
-        table = table_new("key", "value");
+        table = table_new_vertical();
         if (!table)
                 return log_oom();
 
-        table_set_header(table, false);
-
         assert_se(cell = table_get_cell(table, 0, 0));
         (void) table_set_ellipsize_percent(table, cell, 100);
-        (void) table_set_align_percent(table, cell, 100);
 
         assert_se(cell = table_get_cell(table, 0, 1));
         (void) table_set_ellipsize_percent(table, cell, 100);
@@ -97,14 +94,14 @@ static int print_status_info(const StatusInfo *i) {
 
         n = have_time ? strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) : 0;
         r = table_add_many(table,
-                           TABLE_STRING, "Local time:",
+                           TABLE_FIELD, "Local time",
                            TABLE_STRING, n > 0 ? a : "n/a");
         if (r < 0)
                 return table_log_add_error(r);
 
         n = have_time ? strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)) : 0;
         r = table_add_many(table,
-                           TABLE_STRING, "Universal time:",
+                           TABLE_FIELD, "Universal time",
                            TABLE_STRING, n > 0 ? a : "n/a");
         if (r < 0)
                 return table_log_add_error(r);
@@ -117,12 +114,12 @@ static int print_status_info(const StatusInfo *i) {
         } else
                 n = 0;
         r = table_add_many(table,
-                           TABLE_STRING, "RTC time:",
+                           TABLE_FIELD, "RTC time",
                            TABLE_STRING, n > 0 ? a : "n/a");
         if (r < 0)
                 return table_log_add_error(r);
 
-        r = table_add_cell(table, NULL, TABLE_STRING, "Time zone:");
+        r = table_add_cell(table, NULL, TABLE_FIELD, "Time zone");
         if (r < 0)
                 return table_log_add_error(r);
 
@@ -139,11 +136,11 @@ static int print_status_info(const StatusInfo *i) {
                 tzset();
 
         r = table_add_many(table,
-                           TABLE_STRING, "System clock synchronized:",
+                           TABLE_FIELD, "System clock synchronized",
                            TABLE_BOOLEAN, i->ntp_synced,
-                           TABLE_STRING, "NTP service:",
+                           TABLE_FIELD, "NTP service",
                            TABLE_STRING, i->ntp_capable ? (i->ntp_active ? "active" : "inactive") : "n/a",
-                           TABLE_STRING, "RTC in local TZ:",
+                           TABLE_FIELD, "RTC in local TZ",
                            TABLE_BOOLEAN, i->rtc_local);
         if (r < 0)
                 return table_log_add_error(r);
@@ -363,15 +360,12 @@ static int print_ntp_status_info(NTPStatusInfo *i) {
 
         assert(i);
 
-        table = table_new("key", "value");
+        table = table_new_vertical();
         if (!table)
                 return log_oom();
 
-        table_set_header(table, false);
-
         assert_se(cell = table_get_cell(table, 0, 0));
         (void) table_set_ellipsize_percent(table, cell, 100);
-        (void) table_set_align_percent(table, cell, 100);
 
         assert_se(cell = table_get_cell(table, 0, 1));
         (void) table_set_ellipsize_percent(table, cell, 100);
@@ -388,7 +382,7 @@ static int print_ntp_status_info(NTPStatusInfo *i) {
          *  d = (T4 - T1) - (T3 - T2)     t = ((T2 - T1) + (T3 - T4)) / 2"
          */
 
-        r = table_add_cell(table, NULL, TABLE_STRING, "Server:");
+        r = table_add_cell(table, NULL, TABLE_FIELD, "Server");
         if (r < 0)
                 return table_log_add_error(r);
 
@@ -396,7 +390,7 @@ static int print_ntp_status_info(NTPStatusInfo *i) {
         if (r < 0)
                 return table_log_add_error(r);
 
-        r = table_add_cell(table, NULL, TABLE_STRING, "Poll interval:");
+        r = table_add_cell(table, NULL, TABLE_FIELD, "Poll interval");
         if (r < 0)
                 return table_log_add_error(r);
 
@@ -409,7 +403,7 @@ static int print_ntp_status_info(NTPStatusInfo *i) {
 
         if (i->packet_count == 0) {
                 r = table_add_many(table,
-                                   TABLE_STRING, "Packet count:",
+                                   TABLE_FIELD, "Packet count",
                                    TABLE_STRING, "0");
                 if (r < 0)
                         return table_log_add_error(r);
@@ -440,13 +434,13 @@ static int print_ntp_status_info(NTPStatusInfo *i) {
         root_distance = i->root_delay / 2 + i->root_dispersion;
 
         r = table_add_many(table,
-                           TABLE_STRING, "Leap:",
+                           TABLE_FIELD, "Leap",
                            TABLE_STRING, ntp_leap_to_string(i->leap),
-                           TABLE_STRING, "Version:",
+                           TABLE_FIELD, "Version",
                            TABLE_UINT32, i->version,
-                           TABLE_STRING, "Stratum:",
+                           TABLE_FIELD, "Stratum",
                            TABLE_UINT32, i->stratum,
-                           TABLE_STRING, "Reference:");
+                           TABLE_FIELD, "Reference");
         if (r < 0)
                 return table_log_add_error(r);
 
@@ -457,7 +451,7 @@ static int print_ntp_status_info(NTPStatusInfo *i) {
         if (r < 0)
                 return table_log_add_error(r);
 
-        r = table_add_cell(table, NULL, TABLE_STRING, "Precision:");
+        r = table_add_cell(table, NULL, TABLE_FIELD, "Precision");
         if (r < 0)
                 return table_log_add_error(r);
 
@@ -467,7 +461,7 @@ static int print_ntp_status_info(NTPStatusInfo *i) {
         if (r < 0)
                 return table_log_add_error(r);
 
-        r = table_add_cell(table, NULL, TABLE_STRING, "Root distance:");
+        r = table_add_cell(table, NULL, TABLE_FIELD, "Root distance");
         if (r < 0)
                 return table_log_add_error(r);
 
@@ -477,7 +471,7 @@ static int print_ntp_status_info(NTPStatusInfo *i) {
         if (r < 0)
                 return table_log_add_error(r);
 
-        r = table_add_cell(table, NULL, TABLE_STRING, "Offset:");
+        r = table_add_cell(table, NULL, TABLE_FIELD, "Offset");
         if (r < 0)
                 return table_log_add_error(r);
 
@@ -488,17 +482,17 @@ static int print_ntp_status_info(NTPStatusInfo *i) {
                 return table_log_add_error(r);
 
         r = table_add_many(table,
-                           TABLE_STRING, "Delay:",
+                           TABLE_FIELD, "Delay",
                            TABLE_STRING, FORMAT_TIMESPAN(delay, 0),
-                           TABLE_STRING, "Jitter:",
+                           TABLE_FIELD, "Jitter",
                            TABLE_STRING, FORMAT_TIMESPAN(i->jitter, 0),
-                           TABLE_STRING, "Packet count:",
+                           TABLE_FIELD, "Packet count",
                            TABLE_UINT64, i->packet_count);
         if (r < 0)
                 return table_log_add_error(r);
 
         if (!i->spike) {
-                r = table_add_cell(table, NULL, TABLE_STRING, "Frequency:");
+                r = table_add_cell(table, NULL, TABLE_FIELD, "Frequency");
                 if (r < 0)
                         return table_log_add_error(r);
 
index 1fec6d48319fcf4d69c14fe135f32cfa1e5bbf75..bf5192c56fecc716a21585c06f4158dc6455402f 100644 (file)
@@ -50,6 +50,7 @@
 #include "mkdir-label.h"
 #include "mount-util.h"
 #include "mountpoint-util.h"
+#include "nulstr-util.h"
 #include "offline-passwd.h"
 #include "pager.h"
 #include "parse-argument.h"
@@ -962,22 +963,34 @@ shortcut:
         return label_fix_full(fd, /* inode_path= */ NULL, /* label_path= */ path, 0);
 }
 
-static int path_open_parent_safe(const char *path) {
+static int path_open_parent_safe(const char *path, bool allow_failure) {
         _cleanup_free_ char *dn = NULL;
         int r, fd;
 
         if (!path_is_normalized(path))
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to open parent of '%s': path not normalized.", path);
+                return log_full_errno(allow_failure ? LOG_INFO : LOG_ERR,
+                                      SYNTHETIC_ERRNO(EINVAL),
+                                      "Failed to open parent of '%s': path not normalized%s.",
+                                      path,
+                                      allow_failure ? ", ignoring" : "");
 
         r = path_extract_directory(path, &dn);
         if (r < 0)
-                return log_error_errno(r, "Unable to determine parent directory of '%s': %m", path);
+                return log_full_errno(allow_failure ? LOG_INFO : LOG_ERR,
+                                      r,
+                                      "Unable to determine parent directory of '%s'%s: %m",
+                                      path,
+                                      allow_failure ? ", ignoring" : "");
 
-        r = chase_symlinks(dn, arg_root, CHASE_SAFE|CHASE_WARN, NULL, &fd);
+        r = chase_symlinks(dn, arg_root, allow_failure ? CHASE_SAFE : CHASE_SAFE|CHASE_WARN, NULL, &fd);
         if (r == -ENOLINK) /* Unsafe symlink: already covered by CHASE_WARN */
                 return r;
         if (r < 0)
-                return log_error_errno(r, "Failed to open path '%s': %m", dn);
+                return log_full_errno(allow_failure ? LOG_INFO : LOG_ERR,
+                                      r,
+                                      "Failed to open path '%s'%s: %m",
+                                      dn,
+                                      allow_failure ? ", ignoring" : "");
 
         return fd;
 }
@@ -1432,7 +1445,7 @@ static int write_one_file(Item *i, const char *path, CreationMode creation) {
 
         /* Validate the path and keep the fd on the directory for opening the file so we're sure that it
          * can't be changed behind our back. */
-        dir_fd = path_open_parent_safe(path);
+        dir_fd = path_open_parent_safe(path, i->allow_failure);
         if (dir_fd < 0)
                 return dir_fd;
 
@@ -1482,7 +1495,7 @@ static int create_file(Item *i, const char *path) {
 
         /* Validate the path and keep the fd on the directory for opening the file so we're sure that it
          * can't be changed behind our back. */
-        dir_fd = path_open_parent_safe(path);
+        dir_fd = path_open_parent_safe(path, i->allow_failure);
         if (dir_fd < 0)
                 return dir_fd;
 
@@ -1550,7 +1563,7 @@ static int truncate_file(Item *i, const char *path) {
 
         /* Validate the path and keep the fd on the directory for opening the file so we're sure that it
          * can't be changed behind our back. */
-        dir_fd = path_open_parent_safe(path);
+        dir_fd = path_open_parent_safe(path, i->allow_failure);
         if (dir_fd < 0)
                 return dir_fd;
 
@@ -1629,7 +1642,7 @@ static int copy_files(Item *i) {
 
         /* Validate the path and use the returned directory fd for copying the target so we're sure that the
          * path can't be changed behind our back. */
-        dfd = path_open_parent_safe(i->path);
+        dfd = path_open_parent_safe(i->path, i->allow_failure);
         if (dfd < 0)
                 return dfd;
 
@@ -1637,7 +1650,8 @@ static int copy_files(Item *i) {
                          dfd, bn,
                          i->uid_set ? i->uid : UID_INVALID,
                          i->gid_set ? i->gid : GID_INVALID,
-                         COPY_REFLINK | COPY_MERGE_EMPTY | COPY_MAC_CREATE | COPY_HARDLINKS);
+                         COPY_REFLINK | COPY_MERGE_EMPTY | COPY_MAC_CREATE | COPY_HARDLINKS,
+                         NULL);
 
         fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
         if (fd < 0) {
@@ -1665,6 +1679,7 @@ static int create_directory_or_subvolume(
                 const char *path,
                 mode_t mode,
                 bool subvol,
+                bool allow_failure,
                 struct stat *ret_st,
                 CreationMode *ret_creation) {
 
@@ -1680,7 +1695,7 @@ static int create_directory_or_subvolume(
         if (r < 0)
                 return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
 
-        pfd = path_open_parent_safe(path);
+        pfd = path_open_parent_safe(path, allow_failure);
         if (pfd < 0)
                 return pfd;
 
@@ -1721,7 +1736,11 @@ static int create_directory_or_subvolume(
 
                 /* Then look at the original error */
                 if (r < 0)
-                        return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", path);
+                        return log_full_errno(allow_failure ? LOG_INFO : LOG_ERR,
+                                              r,
+                                              "Failed to create directory or subvolume \"%s\"%s: %m",
+                                              path,
+                                              allow_failure ? ", ignoring" : "");
 
                 return log_error_errno(errno, "Failed to open directory/subvolume we just created '%s': %m", path);
         }
@@ -1749,7 +1768,7 @@ static int create_directory(Item *i, const char *path) {
         assert(i);
         assert(IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY));
 
-        fd = create_directory_or_subvolume(path, i->mode, /* subvol= */ false, &st, &creation);
+        fd = create_directory_or_subvolume(path, i->mode, /* subvol= */ false, i->allow_failure, &st, &creation);
         if (fd == -EEXIST)
                 return 0;
         if (fd < 0)
@@ -1767,7 +1786,7 @@ static int create_subvolume(Item *i, const char *path) {
         assert(i);
         assert(IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA));
 
-        fd = create_directory_or_subvolume(path, i->mode, /* subvol = */ true, &st, &creation);
+        fd = create_directory_or_subvolume(path, i->mode, /* subvol = */ true, i->allow_failure, &st, &creation);
         if (fd == -EEXIST)
                 return 0;
         if (fd < 0)
@@ -1846,7 +1865,7 @@ static int create_device(Item *i, mode_t file_type) {
 
         /* Validate the path and use the returned directory fd for copying the target so we're sure that the
          * path can't be changed behind our back. */
-        dfd = path_open_parent_safe(i->path);
+        dfd = path_open_parent_safe(i->path, i->allow_failure);
         if (dfd < 0)
                 return dfd;
 
@@ -1948,7 +1967,7 @@ static int create_fifo(Item *i) {
         if (r == O_DIRECTORY)
                 return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Cannot open path '%s' for creating FIFO, is a directory.", i->path);
 
-        pfd = path_open_parent_safe(i->path);
+        pfd = path_open_parent_safe(i->path, i->allow_failure);
         if (pfd < 0)
                 return pfd;
 
@@ -2033,7 +2052,7 @@ static int create_symlink(Item *i) {
         if (r == O_DIRECTORY)
                 return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Cannot open path '%s' for creating FIFO, is a directory.", i->path);
 
-        pfd = path_open_parent_safe(i->path);
+        pfd = path_open_parent_safe(i->path, i->allow_failure);
         if (pfd < 0)
                 return pfd;
 
index 5f837f4c31e649c7bd023a84dcb55022bb231135..3b0cdda74e0bfdc106faed9a0b37f15722360e73 100644 (file)
@@ -1341,7 +1341,7 @@ bool udev_rules_should_reload(UdevRules *rules) {
 }
 
 static bool token_match_string(UdevRuleToken *token, const char *str) {
-        const char *i, *value;
+        const char *value;
         bool match = false;
 
         assert(token);
index 31c5e67e6a048a45d039e63690b01b46469c47f7..0d513cf85bf02c1d890865905ed444bacb1b6f62 100755 (executable)
@@ -3,6 +3,8 @@
 set -e
 
 TEST_DESCRIPTION="test systemd-repart"
+IMAGE_NAME="repart"
+TEST_FORCE_NEWIMAGE=1
 
 # shellcheck source=test/test-functions
 . "$TEST_BASE_DIR/test-functions"
@@ -10,11 +12,14 @@ TEST_DESCRIPTION="test systemd-repart"
 test_append_files() {
     if ! get_bool "${TEST_NO_QEMU:=}"; then
         install_dmevent
-        if command -v openssl >/dev/null 2>&1; then
-            inst_binary openssl
-        fi
         instmods dm_verity =md
         generate_module_dependencies
+        image_install -o /sbin/mksquashfs
+    fi
+
+    inst_binary mcopy
+    if command -v openssl >/dev/null 2>&1; then
+        inst_binary openssl
     fi
 }
 
index 621fb1cf1b684f7464bb647a2bac15fb76209af1..b4cfca2814fd1aede9283a1c2698cad12ce6c008 100644 (file)
@@ -160,6 +160,7 @@ MemoryLimit=
 MemoryLow=
 MemoryMax=
 MemorySwapMax=
+MemoryZSwapMax=
 MessageQueueMaxMessages=
 MessageQueueMessageSize=
 MountAPIVFS=
index a35ba0e79d60e63abb55f9ea0ecdbf3016b25dff..28331c241221e395bb99104a5a8c071a68c6c613 100644 (file)
@@ -1320,6 +1320,11 @@ install_missing_libraries() {
     inst_simple "${path}/engines-3/capi.so" || true
     inst_simple "${path}/engines-3/loader_attic.so" || true
     inst_simple "${path}/engines-3/padlock.so" || true
+
+    # Binaries from mtools depend on the gconv modules to translate between codepages. Because there's no
+    # pkg-config file for these, we copy every gconv/ directory we can find in /usr/lib and /usr/lib64.
+    # shellcheck disable=SC2046
+    inst_recursive $(find /usr/lib* -name gconv 2>/dev/null)
 }
 
 cleanup_loopdev() {
@@ -1356,6 +1361,9 @@ create_empty_image() {
         root_size=$((4 * root_size))
         data_size=$((2 * data_size))
     fi
+    if [ "$IMAGE_NAME" = "repart" ]; then
+        root_size=$((root_size+=1000))
+    fi
 
     echo "Setting up ${IMAGE_PUBLIC:?} (${root_size} MB)"
     rm -f "${IMAGE_PRIVATE:?}" "$IMAGE_PUBLIC"
index f41069ee04f5b552900d5a18f02e727ce526dde7..a225ac8beec83f22ef436812c52f12ade387ae18 100755 (executable)
@@ -3,6 +3,13 @@
 set -eux
 set -o pipefail
 
+runas() {
+    declare userid=$1
+    shift
+    # shellcheck disable=SC2016
+    su "$userid" -s /bin/sh -c 'XDG_RUNTIME_DIR=/run/user/$UID exec "$@"' -- sh "$@"
+}
+
 if ! command -v systemd-repart &>/dev/null; then
     echo "no systemd-repart" >/skipped
     exit 0
@@ -14,6 +21,9 @@ fi
 export SYSTEMD_LOG_LEVEL=debug
 export PAGER=cat
 
+# Disable use of special glyphs such as →
+export SYSTEMD_UTF8=0
+
 seed=750b6cd5c4ae4012a15e7be3c29e6a47
 
 if ! systemd-detect-virt --quiet --container; then
@@ -86,17 +96,17 @@ test_basic() {
     local defs imgs output
     local loop volume
 
-    defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
+    defs="$(runas testuser mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
+    imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
 
     # 1. create an empty image
 
-    systemd-repart --empty=create \
-                   --size=1G \
-                   --seed="$seed" \
-                   "$imgs/zzz"
+    runas testuser systemd-repart --empty=create \
+                                  --size=1G \
+                                  --seed="$seed" \
+                                  "$imgs/zzz"
 
     output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
 
@@ -130,10 +140,44 @@ SizeMaxBytes=64M
 PaddingMinBytes=92M
 EOF
 
-    systemd-repart --definitions="$defs" \
-                   --dry-run=no \
-                   --seed="$seed" \
-                   "$imgs/zzz"
+    runas testuser systemd-repart --definitions="$defs" \
+                                  --dry-run=no \
+                                  --seed="$seed" \
+                                  --include-partitions=home,swap \
+                                  "$imgs/zzz"
+
+    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+    assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+$imgs/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
+
+    runas testuser systemd-repart --definitions="$defs" \
+                                  --dry-run=no \
+                                  --seed="$seed" \
+                                  --exclude-partitions=root \
+                                  "$imgs/zzz"
+
+    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+    assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+$imgs/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
+
+    runas testuser systemd-repart --definitions="$defs" \
+                                  --dry-run=no \
+                                  --seed="$seed" \
+                                  "$imgs/zzz"
 
     output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
 
@@ -166,10 +210,10 @@ EOF
     echo "Label=ignored_label" >>"$defs/home.conf"
     echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >>"$defs/home.conf"
 
-    systemd-repart --definitions="$defs" \
-                   --dry-run=no \
-                   --seed="$seed" \
-                   "$imgs/zzz"
+    runas testuser systemd-repart --definitions="$defs" \
+                                  --dry-run=no \
+                                  --seed="$seed" \
+                                  "$imgs/zzz"
 
     output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
 
@@ -187,11 +231,11 @@ $imgs/zzz5 : start=     1908696, size=      188416, type=0FC63DAF-8483-4772-8E79
 
     # 4. Resizing to 2G
 
-    systemd-repart --definitions="$defs" \
-                   --size=2G \
-                   --dry-run=no \
-                   --seed="$seed" \
-                   "$imgs/zzz"
+    runas testuser systemd-repart --definitions="$defs" \
+                                  --size=2G \
+                                  --dry-run=no \
+                                  --seed="$seed" \
+                                  "$imgs/zzz"
 
     output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
 
@@ -219,11 +263,11 @@ UUID=2a1d97e1d0a346cca26eadc643926617
 CopyBlocks=$imgs/block-copy
 EOF
 
-    systemd-repart --definitions="$defs" \
-                   --size=3G \
-                   --dry-run=no \
-                   --seed="$seed" \
-                   "$imgs/zzz"
+    runas testuser systemd-repart --definitions="$defs" \
+                                  --size=3G \
+                                  --dry-run=no \
+                                  --seed="$seed" \
+                                  "$imgs/zzz"
 
     output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
 
@@ -242,11 +286,6 @@ $imgs/zzz6 : start=     4194264, size=     2097152, type=0FC63DAF-8483-4772-8E79
 
     cmp --bytes=$((4096*10240)) --ignore-initial=0:$((512*4194264)) "$imgs/block-copy" "$imgs/zzz"
 
-    if systemd-detect-virt --quiet --container; then
-        echo "Skipping encrypt tests in container."
-        return
-    fi
-
     # 6. Testing Format=/Encrypt=/CopyFiles=
 
     cat >"$defs/extra3.conf" <<EOF
@@ -260,11 +299,11 @@ CopyFiles=$defs:/def
 SizeMinBytes=48M
 EOF
 
-    systemd-repart --definitions="$defs" \
-                   --size=auto \
-                   --dry-run=no \
-                   --seed="$seed" \
-                   "$imgs/zzz"
+    runas testuser systemd-repart --definitions="$defs" \
+                                  --size=auto \
+                                  --dry-run=no \
+                                  --seed="$seed" \
+                                  "$imgs/zzz"
 
     output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
 
@@ -282,6 +321,11 @@ $imgs/zzz5 : start=     1908696, size=     2285568, type=0FC63DAF-8483-4772-8E79
 $imgs/zzz6 : start=     4194264, size=     2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name=\"block-copy\"
 $imgs/zzz7 : start=     6291416, size=       98304, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=7B93D1F2-595D-4CE3-B0B9-837FBD9E63B0, name=\"luks-format-copy\""
 
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping encrypt mount tests in container."
+        return
+    fi
+
     loop="$(losetup -P --show --find "$imgs/zzz")"
     udevadm wait --timeout 60 --settle "${loop:?}"
 
@@ -301,8 +345,8 @@ $imgs/zzz7 : start=     6291416, size=       98304, type=0FC63DAF-8483-4772-8E79
 test_dropin() {
     local defs imgs output
 
-    defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
+    defs="$(runas testuser mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
+    imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
 
@@ -325,9 +369,13 @@ EOF
 Label=label2
 EOF
 
-    output=$(systemd-repart --definitions="$defs" --empty=create --size=100M --json=pretty "$imgs/zzz")
+    output=$(runas testuser systemd-repart --definitions="$defs" \
+                                           --empty=create \
+                                           --size=100M \
+                                           --json=pretty \
+                                           "$imgs/zzz")
 
-    diff <(echo "$output") - <<EOF
+    diff -u <(echo "$output") - <<EOF
 [
        {
                "type" : "swap",
@@ -338,10 +386,10 @@ EOF
                "offset" : 1048576,
                "old_size" : 0,
                "raw_size" : 33554432,
-               "size" : " 32.0M",
+               "size" : "-> 32.0M",
                "old_padding" : 0,
                "raw_padding" : 0,
-               "padding" : " 0B",
+               "padding" : "-> 0B",
                "activity" : "create",
                "drop-in_files" : [
                        "$defs/root.conf.d/override1.conf",
@@ -355,8 +403,8 @@ EOF
 test_multiple_definitions() {
     local defs imgs output
 
-    defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
+    defs="$(runas testuser mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
+    imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
 
@@ -380,9 +428,14 @@ UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3
 Label=label2
 EOF
 
-    output=$(systemd-repart --definitions="$defs/1" --definitions="$defs/2" --empty=create --size=100M --json=pretty "$imgs/zzz")
+    output=$(runas testuser systemd-repart --definitions="$defs/1" \
+                                           --definitions="$defs/2" \
+                                           --empty=create \
+                                           --size=100M \
+                                           --json=pretty \
+                                           "$imgs/zzz")
 
-    diff <(echo "$output") - <<EOF
+    diff -u <(echo "$output") - <<EOF
 [
        {
                "type" : "swap",
@@ -393,10 +446,10 @@ EOF
                "offset" : 1048576,
                "old_size" : 0,
                "raw_size" : 33554432,
-               "size" : " 32.0M",
+               "size" : "-> 32.0M",
                "old_padding" : 0,
                "raw_padding" : 0,
-               "padding" : " 0B",
+               "padding" : "-> 0B",
                "activity" : "create"
        },
        {
@@ -408,10 +461,10 @@ EOF
                "offset" : 34603008,
                "old_size" : 0,
                "raw_size" : 33554432,
-               "size" : " 32.0M",
+               "size" : "-> 32.0M",
                "old_padding" : 0,
                "raw_padding" : 0,
-               "padding" : " 0B",
+               "padding" : "-> 0B",
                "activity" : "create"
        }
 ]
@@ -421,13 +474,8 @@ EOF
 test_copy_blocks() {
     local defs imgs output
 
-    if systemd-detect-virt --quiet --container; then
-        echo "Skipping copy blocks tests in container."
-        return
-    fi
-
-    defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
+    defs="$(runas testuser mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
+    imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
 
@@ -456,11 +504,11 @@ Format=ext4
 MakeDirectories=/usr /efi
 EOF
 
-    systemd-repart --definitions="$defs" \
-                   --empty=create \
-                   --size=auto \
-                   --seed="$seed" \
-                   "$imgs/zzz"
+    runas testuser systemd-repart --definitions="$defs" \
+                                  --empty=create \
+                                  --size=auto \
+                                  --seed="$seed" \
+                                  "$imgs/zzz"
 
     output=$(sfdisk --dump "$imgs/zzz")
 
@@ -468,6 +516,11 @@ EOF
     assert_in "$imgs/zzz2 : start=       22528, size=       20480, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output"
     assert_in "$imgs/zzz3 : start=       43008, size=       20480, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:60\"" "$output"
 
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping second part of copy blocks tests in container."
+        return
+    fi
+
     # Then, create another image with CopyBlocks=auto
 
     cat >"$defs/esp.conf" <<EOF
@@ -489,6 +542,7 @@ Type=root-${architecture}
 CopyBlocks=auto
 EOF
 
+    # --image needs root privileges so skip runas testuser here.
     systemd-repart --definitions="$defs" \
                    --empty=create \
                    --size=auto \
@@ -502,8 +556,8 @@ EOF
 test_unaligned_partition() {
     local defs imgs output
 
-    defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
+    defs="$(runas testuser mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
+    imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
 
@@ -514,7 +568,7 @@ test_unaligned_partition() {
 Type=root-${architecture}
 EOF
 
-    truncate -s 10g "$imgs/unaligned"
+    runas testuser truncate -s 10g "$imgs/unaligned"
     sfdisk "$imgs/unaligned" <<EOF
 label: gpt
 
@@ -522,10 +576,10 @@ start=2048, size=69044
 start=71092, size=3591848
 EOF
 
-    systemd-repart --definitions="$defs" \
-                   --seed="$seed" \
-                   --dry-run=no \
-                   "$imgs/unaligned"
+    runas testuser systemd-repart --definitions="$defs" \
+                                  --seed="$seed" \
+                                  --dry-run=no \
+                                  "$imgs/unaligned"
 
     output=$(sfdisk --dump "$imgs/unaligned")
 
@@ -539,8 +593,8 @@ test_issue_21817() {
 
     # testcase for #21817
 
-    defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
+    defs="$(runas testuser mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
+    imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
 
@@ -549,7 +603,7 @@ test_issue_21817() {
 Type=root
 EOF
 
-    truncate -s 100m "$imgs/21817.img"
+    runas testuser truncate -s 100m "$imgs/21817.img"
     sfdisk "$imgs/21817.img" <<EOF
 label: gpt
 
@@ -557,11 +611,11 @@ size=50M, type=${root_guid}
 ,
 EOF
 
-    systemd-repart --pretty=yes \
-                   --definitions "$imgs" \
-                   --seed="$seed" \
-                   --dry-run=no \
-                   "$imgs/21817.img"
+    runas testuser systemd-repart --pretty=yes \
+                                  --definitions "$imgs" \
+                                  --seed="$seed" \
+                                  --dry-run=no \
+                                  "$imgs/21817.img"
 
     output=$(sfdisk --dump "$imgs/21817.img")
 
@@ -575,8 +629,8 @@ test_issue_24553() {
 
     # testcase for #24553
 
-    defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
+    defs="$(runas testuser mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
+    imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
 
@@ -598,28 +652,28 @@ start=524328, size=14848000, type=${root_guid}, uuid=${root_uuid}, name="root-${
 EOF
 
     # 1. Operate on a small image compared with SizeMinBytes=.
-    truncate -s 8g "$imgs/zzz"
+    runas testuser truncate -s 8g "$imgs/zzz"
     sfdisk "$imgs/zzz" <"$imgs/partscript"
 
     # This should fail, but not trigger assertions.
-    assert_rc 1 systemd-repart --definitions="$defs" \
-                               --seed="$seed" \
-                               --dry-run=no \
-                               "$imgs/zzz"
+    assert_rc 1 runas testuser systemd-repart --definitions="$defs" \
+                                              --seed="$seed" \
+                                              --dry-run=no \
+                                              "$imgs/zzz"
 
     output=$(sfdisk --dump "$imgs/zzz")
     assert_in "$imgs/zzz2 : start=      524328, size=    14848000, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
 
     # 2. Operate on an larger image compared with SizeMinBytes=.
     rm -f "$imgs/zzz"
-    truncate -s 12g "$imgs/zzz"
+    runas testuser truncate -s 12g "$imgs/zzz"
     sfdisk "$imgs/zzz" <"$imgs/partscript"
 
     # This should succeed.
-    systemd-repart --definitions="$defs" \
-                   --seed="$seed" \
-                   --dry-run=no \
-                   "$imgs/zzz"
+    runas testuser systemd-repart --definitions="$defs" \
+                                  --seed="$seed" \
+                                  --dry-run=no \
+                                  "$imgs/zzz"
 
     output=$(sfdisk --dump "$imgs/zzz")
     assert_in "$imgs/zzz2 : start=      524328, size=    24641456, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
@@ -641,14 +695,14 @@ Priority=10
 EOF
 
     rm -f "$imgs/zzz"
-    truncate -s 8g "$imgs/zzz"
+    runas testuser truncate -s 8g "$imgs/zzz"
     sfdisk "$imgs/zzz" <"$imgs/partscript"
 
     # This should also succeed, but root is not extended.
-    systemd-repart --definitions="$defs" \
-                   --seed="$seed" \
-                   --dry-run=no \
-                   "$imgs/zzz"
+    runas testuser systemd-repart --definitions="$defs" \
+                                  --seed="$seed" \
+                                  --dry-run=no \
+                                  "$imgs/zzz"
 
     output=$(sfdisk --dump "$imgs/zzz")
     assert_in "$imgs/zzz2 : start=      524328, size=    14848000, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
@@ -656,14 +710,14 @@ EOF
 
     # 4. Multiple partitions with Priority= (large disk)
     rm -f "$imgs/zzz"
-    truncate -s 12g "$imgs/zzz"
+    runas testuser truncate -s 12g "$imgs/zzz"
     sfdisk "$imgs/zzz" <"$imgs/partscript"
 
     # This should also succeed, and root is extended.
-    systemd-repart --definitions="$defs" \
-                   --seed="$seed" \
-                   --dry-run=no \
-                   "$imgs/zzz"
+    runas testuser systemd-repart --definitions="$defs" \
+                                  --seed="$seed" \
+                                  --dry-run=no \
+                                  "$imgs/zzz"
 
     output=$(sfdisk --dump "$imgs/zzz")
     assert_in "$imgs/zzz2 : start=      524328, size=    20971520, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
@@ -673,8 +727,8 @@ EOF
 test_zero_uuid() {
     local defs imgs output
 
-    defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
+    defs="$(runas testuser mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
+    imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
 
@@ -686,12 +740,12 @@ Type=root-${architecture}
 UUID=null
 EOF
 
-    systemd-repart --definitions="$defs" \
-                   --seed="$seed" \
-                   --dry-run=no \
-                   --empty=create \
-                   --size=auto \
-                   "$imgs/zero"
+    runas testuser systemd-repart --definitions="$defs" \
+                                  --seed="$seed" \
+                                  --dry-run=no \
+                                  --empty=create \
+                                  --size=auto \
+                                  "$imgs/zero"
 
     output=$(sfdisk --dump "$imgs/zero")
 
@@ -701,13 +755,8 @@ EOF
 test_verity() {
     local defs imgs output
 
-    if systemd-detect-virt --quiet --container; then
-        echo "Skipping verity test in container."
-        return
-    fi
-
-    defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
+    defs="$(runas testuser mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
+    imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
 
@@ -749,27 +798,145 @@ CN = Common Name
 emailAddress = test@email.com
 EOF
 
-    openssl req -config "$defs/verity.openssl.cnf" -new -x509 -newkey rsa:1024 -keyout "$defs/verity.key" -out "$defs/verity.crt" -days 365 -nodes
+    runas testuser openssl req -config "$defs/verity.openssl.cnf" \
+                               -new -x509 \
+                               -newkey rsa:1024 \
+                               -keyout "$defs/verity.key" \
+                               -out "$defs/verity.crt" \
+                               -days 365 \
+                               -nodes
 
     mkdir -p /run/verity.d
     ln -s "$defs/verity.crt" /run/verity.d/ok.crt
 
+    output=$(runas testuser systemd-repart --definitions="$defs" \
+                                           --seed="$seed" \
+                                           --dry-run=no \
+                                           --empty=create \
+                                           --size=auto \
+                                           --json=pretty \
+                                           --private-key="$defs/verity.key" \
+                                           --certificate="$defs/verity.crt" \
+                                           "$imgs/verity")
+
+    roothash=$(jq -r ".[] | select(.type == \"root-${architecture}-verity\") | .roothash" <<< "$output")
+
+    # Check that we can dissect, mount and unmount a repart verity image. (and that the image UUID is deterministic)
+
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping verity test dissect part in container."
+        return
+    fi
+
+    systemd-dissect "$imgs/verity" --root-hash "$roothash"
+    systemd-dissect "$imgs/verity" --root-hash "$roothash" --json=short | grep -q '"imageUuid":"1d2ce291-7cce-4f7d-bc83-fdb49ad74ebd"'
+    systemd-dissect "$imgs/verity" --root-hash "$roothash" -M "$imgs/mnt"
+    systemd-dissect -U "$imgs/mnt"
+}
+
+test_issue_24786() {
+    local defs imgs root output
+
+    defs="$(runas testuser mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
+    imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
+    root="$(runas testuser mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs' '$root'" RETURN
+
+    touch "$root/abc"
+    mkdir "$root/usr"
+    touch "$root/usr/def"
+
+    cat >"$defs/00-root.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+CopyFiles=/
+EOF
+
+    cat >"$defs/10-usr.conf" <<EOF
+[Partition]
+Type=usr-${architecture}
+CopyFiles=/usr:/
+EOF
+
+    output=$(runas testuser systemd-repart --definitions="$defs" \
+                                           --seed="$seed" \
+                                           --dry-run=no \
+                                           --empty=create \
+                                           --size=auto \
+                                           --json=pretty \
+                                           --root="$root" \
+                                           "$imgs/zzz")
+
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping issue 24786 test loop/mount parts in container."
+        return
+    fi
+
+    loop=$(losetup -P --show -f "$imgs/zzz")
+    udevadm wait --timeout 60 --settle "${loop:?}"
+
+    mkdir "$imgs/mnt"
+    mount -t ext4 "${loop}p1" "$imgs/mnt"
+    assert_rc 0 ls "$imgs/mnt/abc"
+    assert_rc 2 ls "$imgs/mnt/usr"
+    mkdir "$imgs/mnt/usr"
+    mount -t ext4 "${loop}p2" "$imgs/mnt/usr"
+    assert_rc 0 ls "$imgs/mnt/usr/def"
+
+    umount -R "$imgs/mnt"
+    losetup -d "$loop"
+}
+
+test_minimize() {
+    local defs imgs output
+
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping minimize test in container."
+        return
+    fi
+
+    defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+
+    for format in ext4 vfat; do
+        if ! command -v "mkfs.$format" >/dev/null; then
+            continue
+        fi
+
+        cat >"$defs/root-$format.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+Format=${format}
+CopyFiles=${defs}
+Minimize=yes
+EOF
+    done
+
+    if ! command -v mksquashfs >/dev/null; then
+        cat >"$defs/root-squashfs.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+Format=squashfs
+CopyFiles=${defs}
+Minimize=yes
+EOF
+    fi
+
     output=$(systemd-repart --definitions="$defs" \
                             --seed="$seed" \
                             --dry-run=no \
                             --empty=create \
                             --size=auto \
                             --json=pretty \
-                            --private-key="$defs/verity.key" \
-                            --certificate="$defs/verity.crt" \
-                            "$imgs/verity")
-
-    roothash=$(jq -r ".[] | select(.type == \"root-${architecture}-verity\") | .roothash" <<< "$output")
+                            "$imgs/zzz")
 
-    # Check that we can dissect, mount and unmount a repart verity image.
+    # Check that we can dissect, mount and unmount a minimized image.
 
-    systemd-dissect "$imgs/verity" --root-hash "$roothash"
-    systemd-dissect "$imgs/verity" --root-hash "$roothash" -M "$imgs/mnt"
+    systemd-dissect "$imgs/zzz"
+    systemd-dissect "$imgs/zzz" -M "$imgs/mnt"
     systemd-dissect -U "$imgs/mnt"
 }
 
@@ -809,6 +976,8 @@ EOF
     truncate -s 100m "$imgs/$sector.img"
     loop=$(losetup -b "$sector" -P --show -f "$imgs/$sector.img" )
     udevadm wait --timeout 60 --settle "${loop:?}"
+    # This operates on a loop device which we don't support doing without root privileges so we skip runas
+    # here.
     systemd-repart --pretty=yes \
                    --definitions="$defs" \
                    --seed="$seed" \
@@ -841,6 +1010,8 @@ test_issue_21817
 test_issue_24553
 test_zero_uuid
 test_verity
+test_issue_24786
+test_minimize
 
 # Valid block sizes on the Linux block layer are >= 512 and <= PAGE_SIZE, and
 # must be powers of 2. Which leaves exactly four different ones to test on
index 04a8b6e9cc8378a18d3da5f5e33aad0545482f57..1a656fcdc1a09eb9455db4b0b1e7e52321939a04 100755 (executable)
@@ -55,6 +55,79 @@ echo nameserver 10.0.3.1 10.0.3.2 | "$RESOLVCONF" -a hoge.inet.ipsec.192.168.35
 echo nameserver 10.0.3.3 10.0.3.4 | "$RESOLVCONF" -a hoge.foo.dhcp
 assert_in '10.0.3.1 10.0.3.2' "$(resolvectl dns hoge)"
 assert_in '10.0.3.3 10.0.3.4' "$(resolvectl dns hoge.foo)"
+
+# Tests for mDNS and LLMNR settings
+mkdir -p /run/systemd/resolved.conf.d
+{
+    echo "[Resolve]"
+    echo "MulticastDNS=yes"
+    echo "LLMNR=yes"
+} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
+systemctl restart systemd-resolved.service
+systemctl service-log-level systemd-resolved.service debug
+# make sure networkd is not running.
+systemctl stop systemd-networkd.service
+# defaults to yes (both the global and per-link settings are yes)
+assert_in 'yes' "$(resolvectl mdns hoge)"
+assert_in 'yes' "$(resolvectl llmnr hoge)"
+# set per-link setting
+resolvectl mdns hoge yes
+resolvectl llmnr hoge yes
+assert_in 'yes' "$(resolvectl mdns hoge)"
+assert_in 'yes' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge resolve
+resolvectl llmnr hoge resolve
+assert_in 'resolve' "$(resolvectl mdns hoge)"
+assert_in 'resolve' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge no
+resolvectl llmnr hoge no
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+# downgrade global setting to resolve
+{
+    echo "[Resolve]"
+    echo "MulticastDNS=resolve"
+    echo "LLMNR=resolve"
+} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
+systemctl restart systemd-resolved.service
+systemctl service-log-level systemd-resolved.service debug
+# set per-link setting
+resolvectl mdns hoge yes
+resolvectl llmnr hoge yes
+assert_in 'resolve' "$(resolvectl mdns hoge)"
+assert_in 'resolve' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge resolve
+resolvectl llmnr hoge resolve
+assert_in 'resolve' "$(resolvectl mdns hoge)"
+assert_in 'resolve' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge no
+resolvectl llmnr hoge no
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+# downgrade global setting to no
+{
+    echo "[Resolve]"
+    echo "MulticastDNS=no"
+    echo "LLMNR=no"
+} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
+systemctl restart systemd-resolved.service
+systemctl service-log-level systemd-resolved.service debug
+# set per-link setting
+resolvectl mdns hoge yes
+resolvectl llmnr hoge yes
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge resolve
+resolvectl llmnr hoge resolve
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge no
+resolvectl llmnr hoge no
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+
+# Cleanup
+rm -f /run/systemd/resolved.conf.d/mdns-llmnr.conf
 ip link del hoge
 ip link del hoge.foo
 
@@ -79,11 +152,13 @@ DNSSEC=allow-downgrade
 DNS=10.0.0.1
 EOF
 
+mkdir -p /run/systemd/resolved.conf.d
 {
+    echo "[Resolve]"
     echo "FallbackDNS="
     echo "DNSSEC=allow-downgrade"
     echo "DNSOverTLS=opportunistic"
-} >>/etc/systemd/resolved.conf
+} >/run/systemd/resolved.conf.d/test.conf
 ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
 # Override the default NTA list, which turns off DNSSEC validation for (among
 # others) the test. domain
index 662a1fda04ba4fcf7c77acc99da5852181f737c6..5a56d7c3317e33c5f2705bd9d014687b13d14e90 100644 (file)
@@ -26,9 +26,6 @@ ConditionPathExists=/sys/firmware/efi/efivars/LoaderFeatures-4a67b082-0a4c-41cf-
 # Only run this if there is no system token defined yet, or …
 ConditionPathExists=|!/sys/firmware/efi/efivars/LoaderSystemToken-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
 
-# … if the boot loader didn't pass the OS a random seed (and thus probably was missing the random seed file)
-ConditionPathExists=|!/sys/firmware/efi/efivars/LoaderRandomSeed-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
-
 [Service]
 Type=oneshot
 RemainAfterExit=yes