main/postmarketos-duranium: new aport

Duranium is an immutable variant of postmarketOS that uses systemd's
image-based OS model: dm-verity verified /usr, A/B slots managed by
systemd-sysupdate, LUKS2-encrypted root, and systemd-boot with UKIs.

This package ships systemd-repart, systemd-sysusers, tmpfiles, and
first-boot service configuration needed at runtime in both the initramfs
and the booted system, as well as sysupdate transfer definitions for
finding and upgrading images. Build-time repart and sysupdate config
lives in the mkosi config repo[1].

The pmOS initramfs is not used. Rather than re-implement partition
setup, verity, factory reset, and switch_root in shell script and
risk regressions for mutable pmOS installs, an initramfs is built by
mkosi containing systemd. This lets the standard systemd initrd
machinery handle everything, with no modifications to the pmOS
initramfs required.

On Android devices, subpartition logic from the pmOS initramfs is
essentially copied into a new systemd unit that runs in the initramfs
and sets up subpartitions so that they are available to tooling that
runs later in the boot process. In the future, this functionality should
be moved upstream into systemd.

Boot flows:

"Normal boot": the verified /usr partition is mounted and the LUKS
root partition is unlocked (empty passphrase by default, with fallback
to a graphical unlock prompt for user-set passphrases). switch_root
hands off to the real rootfs.

"First boot": systemd-repart creates the root partition, always as a
LUKS volume with an empty passphrase. After switch_root, a first-boot
app runs before the display manager to collect a username and password,
create the user account, and optionally set a LUKS passphrase. The
passphrase can also be changed later via systemd-cryptenroll or
cryptsetup.

"Factory reset": detected in the initramfs by systemd, which uses
systemd-repart to wipe and recreate the root partition. Boot then
continues with the first boot flow above. Factory reset can be triggered
while booting in the rootfs by starting a systemd unit that configures
an EFI variable to signal to the initramfs on next boot.

Signed-off-by: Clayton Craft <craftyguy@postmarketos.org>
Part-of: <https://gitlab.postmarketos.org/postmarketOS/pmaports/-/merge_requests/7059>
This commit is contained in:
Clayton Craft 2025-07-26 14:05:57 -07:00 committed by The Friendly Meow (merge) Bot
parent 97fcae383a
commit dceffe8a46
No known key found for this signature in database
38 changed files with 649 additions and 0 deletions

View file

@ -0,0 +1,165 @@
maintainer="Clayton Craft <clayton@craftyguy.net>"
pkgname=postmarketos-duranium
pkgver=1
pkgrel=0
pkgdesc="Configuration files for building and booting immutable postmarketOS images"
url="https://wiki.postmarketos.org/wiki/Duranium_(Immutable_postmarketOS)"
options="!check" # no tests
depends="
f0rmz
shadow
tar
"
# x86: f0rmz
arch="noarch !x86"
license="GPL-3.0-or-later"
replaces="systemd" # For overwriting tmpfiles.d/{opt,var}.conf
subpackages="$pkgname-initramfs"
_source644="
etc/crypttab
usr/lib/modprobe.d/no-firmware.conf
usr/lib/repart.d/00-esp.conf
usr/lib/repart.d/10-usr-verity-sig.conf
usr/lib/repart.d/11-usr-verity.conf
usr/lib/repart.d/12-usr.conf
usr/lib/repart.d/20-usr-verity-sig.conf
usr/lib/repart.d/21-usr-verity.conf
usr/lib/repart.d/22-usr.conf
usr/lib/repart.d/30-root.conf
usr/lib/systemd/initrd-preset/80-duranium.preset
usr/lib/systemd/system-preset/80-duranium.preset
usr/lib/systemd/system/duranium-firstboot.service
usr/lib/systemd/system/getty@.service.d/wait-firstboot.conf
usr/lib/systemd/system/initramfs-buffyboard.service
usr/lib/systemd/system/initramfs-telnetd.service
usr/lib/systemd/system/initramfs-unudhcpd.service
usr/lib/systemd/system/initramfs-usb-gadget.service
usr/lib/systemd/system/mount-subpartitions.service
usr/lib/systemd/system/systemd-cryptsetup@.service.d/factory-reset-ordering.conf
usr/lib/systemd/system/unl0kr-agent.path.d/plymouth-handoff.conf
usr/lib/systemd/system/unl0kr-agent.service.d/conflicts-console.conf
usr/lib/systemd/system/unl0kr-agent.service.d/plymouth-handoff.conf
usr/lib/sysupdate.d/10-usr-verity-sig.transfer
usr/lib/sysupdate.d/11-usr-verity.transfer
usr/lib/sysupdate.d/12-usr.transfer
usr/lib/sysupdate.d/20-uki.transfer
usr/lib/sysusers.d/10-pmos-user.conf
usr/lib/tmpfiles.d/opt.conf
usr/lib/tmpfiles.d/var.conf
usr/lib/udev/rules.d/90-udisks-ignore-verity-hack.rules
usr/share/duranium/f0rmz-firstboot.conf
"
_source755="
etc/profile.d/99-local-bin.sh
usr/libexec/duranium/first-boot
usr/libexec/duranium/mount-subpartitions
usr/libexec/duranium/usb-gadget-setup
"
# Avoid filename based checksum conflicts by including the whole path:
# https://gitlab.alpinelinux.org/alpine/abuild/-/issues/10013
flatpath() {
local i
for i in $@; do
echo "rootfs-$i" | sed s./.-.g
done
}
source="
$(flatpath $_source440 $_source600 $_source644 $_source755)
dev-gpt-auto-root-no-timeout.conf
"
package() {
for i in $_source644; do
install -Dm644 "$srcdir/$(flatpath "$i")" "$pkgdir/$i"
done
for i in $_source755; do
install -Dm755 "$srcdir/$(flatpath "$i")" "$pkgdir/$i"
done
}
initramfs() {
# musl-utils: for ldconfig.service
# scanelf: for ldconfig.service
# util-linux-login: for sulogin in emergency/debug shell
depends="
buffyboard
busybox-extras
device-mapper
device-mapper-udev
e2fsprogs
musl-utils
postmarketos-bootsplash
scanelf
unl0kr-agent
unudhcpd
util-linux-login
"
amove etc/crypttab
amove usr/lib/modprobe.d/no-firmware.conf
amove usr/lib/systemd/initrd-preset/80-duranium.preset
amove usr/lib/systemd/system/initramfs-buffyboard.service
amove usr/lib/systemd/system/initramfs-telnetd.service
amove usr/lib/systemd/system/initramfs-unudhcpd.service
amove usr/lib/systemd/system/initramfs-usb-gadget.service
amove usr/lib/systemd/system/mount-subpartitions.service
amove usr/lib/systemd/system/systemd-cryptsetup@.service.d/factory-reset-ordering.conf
amove usr/lib/systemd/system/unl0kr-agent.path.d/plymouth-handoff.conf
amove usr/lib/systemd/system/unl0kr-agent.service.d/conflicts-console.conf
amove usr/lib/systemd/system/unl0kr-agent.service.d/plymouth-handoff.conf
amove usr/libexec/duranium/mount-subpartitions
amove usr/libexec/duranium/usb-gadget-setup
# mask plymouth password agent, since we use unl0kr for prompting
mkdir -p "$subpkgdir/etc/systemd/system"
ln -s /dev/null "$subpkgdir/etc/systemd/system/systemd-ask-password-plymouth.path"
# normally this would be just another flatpath file but trying to get abuild
# to work with a file that has \ in the name is torture.
install -Dm644 "$srcdir"/dev-gpt-auto-root-no-timeout.conf \
"$subpkgdir"/usr/lib/systemd/system/dev-gpt\\x2dauto\\x2droot.device.d/no-timeout.conf
}
sha512sums="
ac441c8a690b89a92c2d7db37145e54d918bb9838a45cf76d20de82d46848a80477a8035d9cbaa39fb6ea4747c2762789f71736aa11a78e3adcca0c339dc5677 rootfs-etc-crypttab
ce3f39aa5ed95065306a42bbab6b405bcb90446e206c25509bda64ed2866d80d5168ac225baeb2a50a66e17cbb9b32f26b4a5ec1e042b5f0bf40f6907f4e4d82 rootfs-usr-lib-modprobe.d-no-firmware.conf
b0f320f2ea81c29fa9030abac998e725e098c74bb2ffb76ee2711da4c88615ad46a0e5359b98198648879551781580a0128b57a2ef1ca6a3e03448b01f533065 rootfs-usr-lib-repart.d-00-esp.conf
be7f90bd96fcad6ccf2b434a4b9ce9bc620b63558df0eddd088d86bcbeb778ff097b6d7db0a021d4322bb2ff908eb65f41840e10b11a479e15986a0b02331bf0 rootfs-usr-lib-repart.d-10-usr-verity-sig.conf
8dab35b6c5b61936a8a5f7c6872efe77550f70603a90286c874e9b3c12b25214823c89dd32517b0733110a2f92d0908d0adb6a7bff68f83163d2882e4167b4f6 rootfs-usr-lib-repart.d-11-usr-verity.conf
a2ebcf35620a8c2dcd06dff50b6d17b02e8b688f98c6f7e1bb9727733df20742669cb3926a003ccdb1091bc8a337fdc39cc774de509f1b155f3bd3b45a86f8ef rootfs-usr-lib-repart.d-12-usr.conf
f5688dbd32ae55ea19f5cafff614f2cfc775c1c7078191d4081b254ed25dad58ac5bed0084e5e07aae192591f3b166aae6e5d1ae4eb585f6b1773f03104eee88 rootfs-usr-lib-repart.d-20-usr-verity-sig.conf
730aa98894ec4586d5a97b2faed5ec30ae6679b9ebf4e6dd7e1d4f039279691040232ba30d0f88b4a2696131618b17753d0d8262b88d4ff6f1f120cbd0d22acd rootfs-usr-lib-repart.d-21-usr-verity.conf
d88e87fb22b8efdc2617e8a3d52102032ce2b0dbde0e7d40a8d7679f507acc9cea271faa7004f96611631dfa0351cd5175dc1574f907176c9e4ebc5561b9a6d6 rootfs-usr-lib-repart.d-22-usr.conf
84036b008042766e89bc02ecb7d77b52f80973a385a21faa1fbb2348ff0c216c302e29bd52c6cb40e5e547ad58aa955e06a9e94bd2ca0c512903a200f44c9a63 rootfs-usr-lib-repart.d-30-root.conf
65437794433fea0842a5ac7a528f3416d06e21bb85e796279ef6280f225e97b8836685b5a034fd0cd01db0134a74f23131bcc32bb8c386cc8c5fd80febaefaa1 rootfs-usr-lib-systemd-initrd-preset-80-duranium.preset
ee8a7a857bb409c7abe55bfaae1bc1cfb7e08f80b1ae22c804f6c012e643c090bfb2a11692e8bb633bd1685f3fd8a0a4789cb67d286fd9d6420032a2285854cd rootfs-usr-lib-systemd-system-preset-80-duranium.preset
09dd75ce4d6790af5156fadea0b948b13a5f36ab1162c45b4a58a5c6b4061d6871610cc3384252422b28bfd7836b95cd7ca857277113abacc0083ecdd6cfff1f rootfs-usr-lib-systemd-system-duranium-firstboot.service
16fc787401e1fbbd1421257781270d2be6cb879ab9cc99412044df25af72c5525567f7b7f1066f022c0b7e03dd33c62f2d871fed9e9121ed309564c207795c38 rootfs-usr-lib-systemd-system-getty@.service.d-wait-firstboot.conf
786a057e3f1eab37773b5255a5abde060b6bc6f42fd2cdaa82ce678263db6177ca30b49decb795293a4a0ccaf02346684acd0469cc8222898c43768376f2218d rootfs-usr-lib-systemd-system-initramfs-buffyboard.service
4f303c5d7cb7054e8bb6e109aa3f4337098b19a6b5a8c6b0b1e1033889d8b1f85a87a0086b67f6f9c62fc93aeca0c59e50def12fff33b79fb7819657b5b820ec rootfs-usr-lib-systemd-system-initramfs-telnetd.service
7620e15cebb92c06a662bea095122793b8928b37c8b73fb060f2c882ac9c40419cc3e9eacafab2d04f8089a6fd32c1aa46f9247c54ee60adca87a796262bc37a rootfs-usr-lib-systemd-system-initramfs-unudhcpd.service
9c0eb915c09fc3d8de3a1a486f3e2202be05b11ba924d13349a70d93355ca0c72fe2417504e4b7e0c74aa1bb7f5ae77e3497f09dd67e48c0b8f2432f382f2f38 rootfs-usr-lib-systemd-system-initramfs-usb-gadget.service
221844a2fe5af2c9c675d7d7c118f3db82afbf8b28db77ea049db74813b62881fe40ada30630f5fd777c6503e2936e22251be75e278635274fe6ac08eba8e56b rootfs-usr-lib-systemd-system-mount-subpartitions.service
101b1af9f26fe57f8be1f0353d8146724aaad4c80e790b85559e0d7ce10a1b9169429b112fc17dafde4902e26617ced00b3540a3999c50b606f3ff7564759464 rootfs-usr-lib-systemd-system-systemd-cryptsetup@.service.d-factory-reset-ordering.conf
612e50282c0ceb7676d8e62853e99bd693103fc59098d8ac91173538ff1c3fa8b69d3eebeaa6390df7c8f9cc62fddae1ca2d35d07d97e60b40b660bc62bf049b rootfs-usr-lib-systemd-system-unl0kr-agent.path.d-plymouth-handoff.conf
b1aa826b83fbb5d606b5dc37a4db67a7233519e0ac953325395dd7f3b7e303d206f6297e51a749d07ee173ccbc4ccf055e92888e38345783d4f4c7687a6a9870 rootfs-usr-lib-systemd-system-unl0kr-agent.service.d-conflicts-console.conf
017fe2d6298842b24498000c98e851b441da4edf91cadf2bfc0542a46364f105eda9a5ca772c87e7bd4749e101833fdb43604081b7ab26ebea426559f9de418e rootfs-usr-lib-systemd-system-unl0kr-agent.service.d-plymouth-handoff.conf
2e4d532a0d996d8e68007b6036b4564ca3d654dd91cadd80ed904536d0c3ed1e019b4081f7bdb6d128e695cacfe6949bfb20c074b95d10ef25f948f8eb307e24 rootfs-usr-lib-sysupdate.d-10-usr-verity-sig.transfer
2dbe55c05b8237ad85c4f333a8c02eec26aad14032f159830696ad9b9c6442d624b0190b2e6780e4b8aed590eb12d0c7454d39ab739e55d420300cdba2a78f58 rootfs-usr-lib-sysupdate.d-11-usr-verity.transfer
14df73dfc786a249b300f8998b1f51301834cf058b65c95701b1350f112d2c3deed3b8db9ba439bd90537cef533f148d187bd73961707dca12ef84735e2d5595 rootfs-usr-lib-sysupdate.d-12-usr.transfer
4ffb2be01cb033c1282b6661052250937e1ac457cec3900b075d0dcdf59de7dcb2eb927c39db0e96468a90b03fed0a4e6db0d99f62d714c31a29d4684e79751f rootfs-usr-lib-sysupdate.d-20-uki.transfer
fa1c3dd727fd20d16d18695d58dd10ec335d64ece00152346c84ae750ed5ade8faf5ba11747135d79972dd1a3f4f1ab496ae9425ebcf7232a4203ca9396fa7e0 rootfs-usr-lib-sysusers.d-10-pmos-user.conf
0779c6d5959aef0e325adb69f3e0720a3f271f474c0f14e9c830f015c923c432d07222a24ce722a3ea58670d2eff3536d45bcaf7ae848b587676f1586a348e0e rootfs-usr-lib-tmpfiles.d-opt.conf
7d98cb314472d7db64599c8abfc2e21ef6437b0c5bf407f0c21a42ec2f14f660fb3aef64c99a13aada124a822a85d8e4a6674838d1fe07719774331121d5414b rootfs-usr-lib-tmpfiles.d-var.conf
ede57bf8696dce4ee295dc2a1dfe31dec415e7ce994600ce3bf7386ac1ad63797a6c5a88434ca0cc854cdad6618647845126b1f8757c48f83ce308d394b8c68d rootfs-usr-lib-udev-rules.d-90-udisks-ignore-verity-hack.rules
7f2b98edc7b485129ea862c5c852422e8996ffe0a2bdae3160b6c94667b4e345a1792164e3143d58cb1a51c3c6981381ad7610d709aba210bb61a0c883455476 rootfs-usr-share-duranium-f0rmz-firstboot.conf
765797421f9123cdcf85e1496d01884cce1c8c304d15369b7eee32a8fc5abcd860dc5856c0d493f1cd3c6ad2a0ccdc4d6ba7d2ae356f0fc824dc16cdb84ebf37 rootfs-etc-profile.d-99-local-bin.sh
6661a981a3c2e549f0f1a12d4777f662eb88aa581608529f6e75a5d448b78fcb709812a4291d5708da840ba72d6cbf48375485d3ecd900d252efd2eb4a9e170c rootfs-usr-libexec-duranium-first-boot
bb3ec1693f88a61563876cd069b49351861a31c15b1795702031e5beb4e89cefe302644c1ea9d0bb74746aa97007664c1782857900e2b85dd71182e95efd66ae rootfs-usr-libexec-duranium-mount-subpartitions
9912109374399bf6df4d45c05baff5b3c424e05380b81b2be5b55e28e95f07bda71b5a4f7345a40498c1b11df465a9f4410e740d119ab98543ce28b75c53eb0f rootfs-usr-libexec-duranium-usb-gadget-setup
ca5b767d4bbc5857c2282124fee3082fafaca8e735ab2077112060e67a37afdb521c28c81fc3b548f114e857aaa702169ee77fab8b7b37c44b5233c83a94afd4 dev-gpt-auto-root-no-timeout.conf
"

View file

@ -0,0 +1,7 @@
# systemd-gpt-auto-generator does not set JobTimeoutSec=infinity on
# dev-gpt-auto-root.device, so if the user takes longer than
# DefaultDeviceTimeoutSec (90s) to enter their LUKS passphrase, the device
# unit times out and the system falls into the emergency shell.
# Workaround for: https://github.com/systemd/systemd/issues/35870
[Unit]
JobTimeoutSec=infinity

View file

@ -0,0 +1,2 @@
# For auto-unlocking the volume if the default/empty passphrase is set
root PARTLABEL=pmOS_root /dev/null luks,keyfile-timeout=0

View file

@ -0,0 +1 @@
export PATH="$PATH:$HOME/.local/bin"

View file

@ -0,0 +1,8 @@
# List of modules that require firmware, and should not be loaded
# in the initramfs
blacklist ath11k
blacklist ipa
blacklist qcom_q6v5_pas
blacklist venus_core
blacklist btqca

View file

@ -0,0 +1,6 @@
[Partition]
Type=esp
Format=vfat
CopyFiles=/boot:/
SizeMinBytes=1G
SizeMaxBytes=1G

View file

@ -0,0 +1,4 @@
[Partition]
Type=usr-verity-sig
Label=%M_%A_vts
CopyBlocks=auto

View file

@ -0,0 +1,4 @@
[Partition]
Type=usr-verity
Label=%M_%A_vty
CopyBlocks=auto

View file

@ -0,0 +1,4 @@
[Partition]
Type=usr
Label=%M_%A_usr
CopyBlocks=auto

View file

@ -0,0 +1,3 @@
[Partition]
Type=usr-verity-sig
Label=_empty

View file

@ -0,0 +1,6 @@
[Partition]
Type=usr-verity
Label=_empty
NoAuto=1
SizeMinBytes=400M
SizeMaxBytes=400M

View file

@ -0,0 +1,6 @@
[Partition]
Type=usr
Label=_empty
NoAuto=1
SizeMinBytes=5G
SizeMaxBytes=5G

View file

@ -0,0 +1,8 @@
[Partition]
Type=root
Format=ext4
Encrypt=key-file
SizeMinBytes=1G
Weight=20000
FactoryReset=yes
Label=pmOS_root

View file

@ -0,0 +1,6 @@
enable initramfs-buffyboard.service
enable initramfs-telnetd.service
enable initramfs-unudhcpd.service
enable initramfs-usb-gadget.service
enable mount-subpartitions.service
enable unl0kr-agent.path

View file

@ -0,0 +1,13 @@
[Unit]
Description=Duranium First Boot Setup
ConditionPathExists=!/etc/default_user
After=plymouth-quit.service
Before=display-manager.service
[Service]
Type=oneshot
ExecStart=/usr/libexec/duranium/first-boot
[Install]
WantedBy=multi-user.target
RequiredBy=display-manager.service

View file

@ -0,0 +1,2 @@
[Unit]
After=duranium-firstboot.service

View file

@ -0,0 +1,13 @@
[Unit]
Description=Framebuffer Touchscreen Keyboard (initramfs)
DefaultDependencies=no
ConditionPathExists=/dev/fb0
After=modprobe@uinput.service
Requires=modprobe@uinput.service
[Service]
Type=simple
ExecStart=/usr/bin/buffyboard
[Install]
WantedBy=emergency.target

View file

@ -0,0 +1,13 @@
[Unit]
Description=Telnet Debug Shell (initramfs)
DefaultDependencies=no
After=initramfs-unudhcpd.service
ConditionPathExists=/run/usb-gadget-iface
[Service]
Type=simple
# FIXME: network addresses should be configurable, perhaps via cmdline param
ExecStart=/usr/sbin/telnetd -b 172.16.42.1:23 -F -l /bin/sh
[Install]
WantedBy=emergency.target

View file

@ -0,0 +1,13 @@
[Unit]
Description=DHCP Server for USB Gadget (initramfs)
DefaultDependencies=no
After=initramfs-usb-gadget.service
ConditionPathExists=/run/usb-gadget-iface
[Service]
Type=simple
# FIXME: network addresses should be configurable, perhaps via cmdline param
ExecStart=/bin/sh -c 'unudhcpd -i "$(cat /run/usb-gadget-iface)" -s 172.16.42.1 -c 172.16.42.2'
[Install]
WantedBy=emergency.target

View file

@ -0,0 +1,15 @@
[Unit]
Description=USB Gadget Network Setup (initramfs)
DefaultDependencies=no
After=systemd-modules-load.service sys-kernel-config.mount modprobe@libcomposite.service
Requires=sys-kernel-config.mount modprobe@libcomposite.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/libexec/duranium/usb-gadget-setup
StandardOutput=journal+console
StandardError=journal+console
[Install]
WantedBy=emergency.target

View file

@ -0,0 +1,16 @@
[Unit]
Description=Mount Subpartitions
DefaultDependencies=no
After=systemd-udev-settle.service
Before=systemd-repart.service
Before=initrd-root-device.target
Before=systemd-veritysetup@.service
Before=systemd-cryptsetup@.service
OnFailure=emergency.target
[Service]
Type=oneshot
ExecStart=/usr/libexec/duranium/mount-subpartitions
[Install]
WantedBy=initrd.target

View file

@ -0,0 +1 @@
enable duranium-firstboot.service

View file

@ -0,0 +1,3 @@
[Unit]
# make sure factory reset (which includes running repart) is done
After=factory-reset-now.target

View file

@ -0,0 +1,5 @@
[Unit]
# This unit's upstream has ConditionPathExists=!/run/plymouth/pid so the path
# watcher would *not* start if plymouth is running. Plymouth handoff
# (deactivate/reactivate) is handled in the unl0kr-agent.service drop-in.
ConditionPathExists=

View file

@ -0,0 +1,2 @@
[Unit]
Conflicts=systemd-ask-password-console.service systemd-ask-password-console.path

View file

@ -0,0 +1,6 @@
[Unit]
ConditionPathExists=
[Service]
ExecStartPre=-sh -c 'plymouth --ping && plymouth deactivate'
ExecStopPost=-sh -c 'plymouth --ping && plymouth reactivate'

View file

@ -0,0 +1,15 @@
[Transfer]
ProtectVersion=%A
[Source]
Type=url-file
Path=https://duranium.postmarketos.org/images/%M_%W_%w/
MatchPattern=%M_%W_%w_@v.usr-%a-verity-sig.@u.raw.zst
[Target]
Type=partition
Path=auto
MatchPattern=%M_@v_vts
MatchPartitionType=usr-verity-sig
PartitionFlags=0
ReadOnly=1

View file

@ -0,0 +1,15 @@
[Transfer]
ProtectVersion=%A
[Source]
Type=url-file
Path=https://duranium.postmarketos.org/images/%M_%W_%w/
MatchPattern=%M_%W_%w_@v.usr-%a-verity.@u.raw.zst
[Target]
Type=partition
Path=auto
MatchPattern=%M_@v_vty
MatchPartitionType=usr-verity
PartitionFlags=0
ReadOnly=1

View file

@ -0,0 +1,15 @@
[Transfer]
ProtectVersion=%A
[Source]
Type=url-file
Path=https://duranium.postmarketos.org/images/%M_%W_%w/
MatchPattern=%M_%W_%w_@v.usr-%a.@u.raw.zst
[Target]
Type=partition
Path=auto
MatchPattern=%M_@v_usr
MatchPartitionType=usr
PartitionFlags=0
ReadOnly=1

View file

@ -0,0 +1,19 @@
[Transfer]
ProtectVersion=%A
[Source]
Type=url-file
Path=https://duranium.postmarketos.org/images/%M_%W_%w/
MatchPattern=%M_%W_%w_@v.efi
[Target]
Type=regular-file
Path=/EFI/Linux
PathRelativeTo=boot
MatchPattern=%M_%W_%w_@v+@l-@d.efi \
%M_%W_%w_@v+@l.efi \
%M_%W_%w_@v.efi
Mode=0600
TriesLeft=3
TriesDone=0
InstancesMax=2

View file

@ -0,0 +1,6 @@
g audio - -
g input - -
g netdev - -
g plugdev - -
g video - -
g wheel - -

View file

@ -0,0 +1,5 @@
# Populate /opt from factory defaults
# The 'C' directive copies if destination doesn't exist (works since /opt is empty on first boot)
# Copy entire /opt structure from factory on first boot
C /opt - - - - /usr/share/factory/opt

View file

@ -0,0 +1,16 @@
# Populate /var from factory defaults
# The 'C' directive copies if destination doesn't exist (works since /var is empty on first boot)
# Copy entire /var structure from factory on first boot
C+ /var - - - - /usr/share/factory/var
# Additional runtime directories that may not exist in factory
d /var/run 0755 root root -
L /run - - - - /var/run
# Ensure critical runtime directories exist
d /var/log/journal 2755 root systemd-journal -
d /var/log/journal/%m 2755 root systemd-journal -
d /var/tmp 1777 root root -
d /var/cache 0755 root root -
d /var/lib 0755 root root -

View file

@ -0,0 +1,5 @@
# FIXME: udisks2 (or udiskie) insist on trying to unlock verity hash partitions,
# probably because ID_FS_USAGE="crypto" or something. This just tells udisks to
# ignore the verity hash device...
SUBSYSTEM=="block", ENV{ID_FS_TYPE}=="DM_verity_hash", ENV{ID_FS_USAGE}=="crypto", ENV{UDISKS_IGNORE}="1"

View file

@ -0,0 +1,69 @@
#!/bin/sh
set -e
# Parse output from f0rmz UI
# Sets: firstboot_username, firstboot_password
get_firstboot_config() {
local output
# f0rmz emits 15/SIGTERM on success...
output=$(f0rmz -C /usr/share/duranium/f0rmz-firstboot.conf || true)
if [ -z "$output" ] || ! echo "$output" | grep -q "username="; then
echo "ERROR: running f0rmz configuration UI failed!" >&2
exit 1
fi
while IFS= read -r line; do
key="${line%%=*}"
value="${line#*=}"
case "$key" in
username) firstboot_username="$value" ;;
password) firstboot_password="$value" ;;
fde_passphrase) firstboot_fde_passphrase="$value" ;;
esac
done <<EOF
$output
EOF
}
get_luks_device() {
local dm_name
dm_name=$(findmnt -n -o SOURCE / | sed 's|/dev/mapper/||')
cryptsetup status "$dm_name" | awk '/device:/{print $2}'
}
get_firstboot_config
useradd \
--comment "Default User" \
--create-home \
--user-group \
--shell /bin/sh \
--groups audio,input,netdev,plugdev,video,wheel,render \
"$firstboot_username"
# set user password
printf '%s:%s' "$firstboot_username" "$firstboot_password" | chpasswd
# Keep this at the end so it can also act as the sentinel file for running the
# first boot setup, e.g. if this is the first boot or the above operations are
# interrupted
printf '%s\n' "$firstboot_username" > /etc/default_user
# Re-trigger generators after creating the default user, because some UIs
# (plasma mobile, tinydm) are system daemons that run as the default user, and
# the generator depends on /etc/default_user to work properly. It will have run
# previously during first boot, before this firstboot app ran, and would have
# used the wrong UID in the unit override it generated.
systemctl daemon-reload
if [ -z "$firstboot_fde_passphrase" ]; then
echo "No FDE passphrase given, not changing default/empty passphrase"
exit 0
fi
# replace empty LUKS passphrase slot with user's passphrase
luks_dev=$(get_luks_device)
systemd-run --pipe \
--property="SetCredential=cryptenroll.new-passphrase:${firstboot_fde_passphrase}" \
-- \
systemd-cryptenroll --unlock-key-file=/dev/null --wipe-slot=empty --password "$luks_dev"

View file

@ -0,0 +1,66 @@
#!/bin/sh
set -e
# Read ESP UUID from EFI variable
mount -t efivarfs efivarfs /sys/firmware/efi/efivars 2>/dev/null || true
efi_var="$(find /sys/firmware/efi/efivars -name 'LoaderDevicePartUUID-*' 2>/dev/null | head -n1)"
if [ ! -e "$efi_var" ]; then
echo "ERROR: LoaderDevicePartUUID EFI variable not found"
exit 1
fi
esp_uuid="$(dd if="$efi_var" bs=1 skip=4 2>/dev/null | tr -d '\0' | tr '[:upper:]' '[:lower:]')"
if [ -z "$esp_uuid" ]; then
echo "ERROR: Failed to read ESP UUID from EFI variable"
exit 1
fi
echo "ESP UUID from EFI: $esp_uuid"
losetup_args="-Pfv --show --direct-io=on"
attempt_start="$(cut -d. -f1 /proc/uptime)"
wait_seconds=10
echo "Scanning for subpartition container (timeout: ${wait_seconds}s)..."
while true; do
if [ -b "/dev/disk/by-partuuid/$esp_uuid" ]; then
echo "ESP partition already visible, no subpartition mounting needed"
exit 0
fi
for dev in /sys/class/block/*/partition; do
[ -e "$dev" ] || continue
partition="/dev/$(basename "$(dirname "$dev")")"
[ -b "$partition" ] || continue
if sfdisk --dump "$partition" 2>/dev/null | grep -qi "$esp_uuid"; then
sector_size="$(lsblk -ndo PHY-SEC "$partition" 2>/dev/null)"
if [ -n "$sector_size" ]; then
# not strictly required if it's 512 (the default for losetup)
# but doing this anyways saves us from a potentially risky
# numerical comparison to a parsed string value
losetup_args="$losetup_args --sector-size $sector_size"
fi
echo "Found subpartition container: $partition"
# shellcheck disable=SC2086
loop_dev="$(losetup $losetup_args "$partition")"
if [ -z "$loop_dev" ]; then
echo "ERROR: losetup failed for $partition"
exit 1
fi
echo "Mounted subpartitions via $loop_dev"
exit 0
fi
done
now="$(cut -d. -f1 /proc/uptime)"
if [ "$now" -ge $(( attempt_start + wait_seconds )) ]; then
echo "ERROR: subpartition container not found after ${wait_seconds}s"
exit 1
fi
sleep 0.1
done

View file

@ -0,0 +1,68 @@
#!/bin/sh
# FIXME: vendor/product IDs, USB function, and network addresses should be
# configurable, perhaps via a config file or kernel cmdline parameters.
CONFIGFS="/sys/kernel/config/usb_gadget"
HOST_IP="172.16.42.1"
# No UDC = no gadget support (e.g. laptop), exit silently
if [ -z "$(ls /sys/class/udc 2>/dev/null)" ]; then
echo "No UDC found, skipping USB gadget setup"
exit 0
fi
if ! mountpoint -q /sys/kernel/config; then
modprobe configfs
mount -t configfs configfs /sys/kernel/config
fi
GADGET="$CONFIGFS/g1"
mkdir -p "$GADGET"
echo "0x1d6b" > "$GADGET/idVendor" # Linux Foundation
echo "0x0104" > "$GADGET/idProduct" # Multifunction Composite Gadget
mkdir -p "$GADGET/strings/0x409"
echo "postmarketOS" > "$GADGET/strings/0x409/manufacturer"
echo "Duranium Debug" > "$GADGET/strings/0x409/product"
echo "duranium-debug" > "$GADGET/strings/0x409/serialnumber"
# Try ncm first, fall back to rndis
USB_FUNCTION="ncm.usb0"
if ! mkdir -p "$GADGET/functions/$USB_FUNCTION" 2>/dev/null; then
USB_FUNCTION="rndis.usb0"
mkdir -p "$GADGET/functions/$USB_FUNCTION"
fi
# Set a random host-side MAC so the host doesn't get confused across reboots
# NOTE: should eventually be configurable or derived from device identity
RANDOM_MAC="$(dd if=/dev/urandom bs=6 count=1 2>/dev/null | od -An -tx1 | tr -d ' \n' | sed 's/\(..\)/\1:/g;s/:$//')"
# locally administered, unicast
BYTE1="$(printf '%02x' $(( 0x$(echo "$RANDOM_MAC" | cut -c1-2) | 0x02 & 0xfe )))"
RANDOM_MAC="${BYTE1}$(echo "$RANDOM_MAC" | cut -c3-)"
mkdir -p "$GADGET/configs/c.1/strings/0x409"
echo "USB network" > "$GADGET/configs/c.1/strings/0x409/configuration"
ln -sf "$GADGET/functions/$USB_FUNCTION" "$GADGET/configs/c.1/"
UDC="$(for _udc in /sys/class/udc/*/; do basename "$_udc"; break; done)"
echo "$UDC" > "$GADGET/UDC"
# Wait briefly for the interface to appear
IFACE=""
for _ in $(seq 1 20); do
IFACE="$(cat "$GADGET/functions/$USB_FUNCTION/ifname" 2>/dev/null || true)"
[ -n "$IFACE" ] && break
sleep 0.1
done
if [ -z "$IFACE" ]; then
echo "ERROR: USB gadget interface did not appear"
exit 1
fi
ip link set "$IFACE" up
ip addr add "$HOST_IP/24" dev "$IFACE"
echo "$IFACE" > /run/usb-gadget-iface
echo "USB gadget up on $IFACE ($HOST_IP)"

View file

@ -0,0 +1,18 @@
[intro]
title=Welcome to postmarketOS!
body=The next few screens will assist you with setting up your device.
[form.username]
type=text
label=Please enter a new username
required=true
[form.password]
type=password
label=Set a password for the account
required=true
[form.fde_passphrase]
type=password
label=Set a passphrase for disk encryption (optional)
required=false