Compare commits

...
Author SHA1 Message Date
Clayton Craft b4aed77e58 DO NOT MERGE
Signed-off-by: Clayton Craft <craftyguy@postmarketos.org>
2026-02-04 10:48:24 -08:00
Clayton Craft 71cd0d66b4 DO NOT MERGE main/postmarketos-initramfs: initialize subpartitions as loopback devices
Signed-off-by: Clayton Craft <craftyguy@postmarketos.org>
2026-02-04 08:48:25 -08:00
Clayton Craft 5a89f02432 DO NOT MERGE: linux-postmarketos-qcom-sdm845: enable immutable config
Signed-off-by: Clayton Craft <craftyguy@postmarketos.org>
2026-02-04 07:46:45 -08:00
Clayton Craft 80516ba181 main/postmarketos-initramfs: add support for immutable booting
This adds support for booting immutable pmOS images, triggered by
the presence of "usrhash=" on the kernel cmdline. This is a safe flag
for "is it immutable?" because it is used by dm-verity for verified
partitions (in this case, the usr partition), so this param is not going
to be present on mutable installs).

Immutable-specific logic, for the most part, is provided by a
separate functions file that is installed by a separate package
(postmarketos-duranium). This functions file is sourced if it exists and
functions to support immutable boot are conditionally.

Some changes were made to subpartition support, since for immutable boot
we need to be able to search PARTUUIDs of subpartitions:
 - find_partition can now locate partitions by PARTUUID in nested
   partition tables using sfdisk (immutable-only, as sfdisk not available
   in mutable initramfs)
 - mount_subpartitions checks for both root and boot partitions to
   support first boot scenarios where root doesn't exist yet
2026-02-03 14:26:35 -08:00
Clayton Craft 0022ba277f main/postmarketos-initramfs: allow any number of subpartitions
I have no clue why this is conditionally selected when the number of
subpartitions is "2", this code predates pmbootstrap@ed9fc7f, when
pmaports used to be in the pmb repo...

Signed-off-by: Clayton Craft <craftyguy@postmarketos.org>
2026-02-03 14:26:35 -08:00
Clayton Craft acd2f0c57e main/postmarketos-initramfs: search part. UUIDs when locating partitions
This also fixes the uuid param comment for the function... Before this
change the function only searched filesystem UUIDs even though the
comment suggested it was searching partition UUIDs.

Signed-off-by: Clayton Craft <craftyguy@postmarketos.org>
2026-02-03 14:26:35 -08:00
Clayton Craft 2588d5370f main/postmarketos-initramfs: check os-release symlink when mounting root
Signed-off-by: Clayton Craft <craftyguy@postmarketos.org>
2026-02-03 14:26:35 -08:00
Clayton Craft 4de256ea48 main/postmarketos-initramfs: add try_mount_root_partition
This allows re-using the logic for mounting the root partition without
making the failure conditions fatal to booting. There should be no
impact to existing boot flows, failing to mount root is still a failure
to boot.

Signed-off-by: Clayton Craft <craftyguy@postmarketos.org>
2026-02-03 14:26:34 -08:00
Clayton Craft 2b03a8bd35 main/postmarketos-duranium: new aport
This package contains the systemd-repart, systemd-sysusers, and (some)
tmpfiles config to support booting and running immutable images of pmOS.
Specifically, this config is used at runtime:

- in the initramfs
- first boot of the rootfs
- sysupdate, for finding/upgrading images

It's important to note that this is not the entire set of configuration
needed to build immutable images, there is some systemd-repart and
systems-sysupdate config that is in the mkosi config repo[1]. The
configuration there is used by mkosi when creating images for installing
and updating an immutable pmOS.

"First boot" - disk with booted ESP is detected, detects the correct
usr partition to verify/mount using usrhash, and runs f0rmz to collect
username, password, (optional) fde passphrase and (optional) hostname.
Then systemd-repart is used to create the rest of the partition layout
(rootfs, slot B usr partition stuff), and (optionally) encrypts the
rootfs. Next it runs systemd-sysusers and systemd-firstboot on the new
root partition to create the user account and set hostname and some
other tings, mounts the booted usr partition into it and then boot
continues (e.g. to switch_root)

"Normal boot" - disk with booted ESP is detected, this disk is searched
for the appropriate root partition type for the booted arch and FDE
unlocked if necessary. usrhash is used to verify and mount the usr
partition. Then boot continues (e.g. to switch_root)

"Factory reset" - A reset condition is detected by looking at a specific
EFI variable set on the previous boot, and systemd-repart is run to
erase the rootfs. Then the initramfs continues with the "First boot"
flow above, where the user can continue to configure a new install
OR shut down the device to leave it in an unconfigured state to be
continued later.

It's important to note that finding the root partition is implemented
in a different way than how the initramfs finds the root partition for
mutable booting. I attempted to lean on the Discoverable Partition Spec,
which depends on an EFI variable and partition type UUIDs to locate
appropriate disks and partitions to search.
2026-02-03 14:26:21 -08:00
32 changed files with 1266 additions and 82 deletions

No files matched your search

@@ -5,7 +5,7 @@ maintainer="Casey Connolly <kcxt@postmarketos.org>"
_flavor="postmarketos-qcom-sdm845"
pkgname=linux-$_flavor
pkgver=6.16.7
pkgrel=3
pkgrel=999
pkgdesc="Mainline Kernel fork for SDM845 devices"
arch="aarch64"
_carch="arm64"
@@ -85,6 +85,6 @@ package() {
sha512sums="
803d2cf85b248c3da41a9c9178d69d211eccf5d1adb72acd5343e09cbb65782f727e579939287c91712c69a862bed7344a0e2bc893c2791a971b6fed95ec6f1a linux-sdm845-6.16.7-r0.tar.gz
57f744a06dd664d6ba7efec82ab1d8101d5b83dce424a388df95467183aa7c2d2fabf3a9e34271cd00e28eb731b74ef09e9613384765cf6eff7a6e2bd27b2c1d config-postmarketos-qcom-sdm845.aarch64
b6bac51d8eb626eb861c934d89a1ad81dc7cbc32d5d8f9418f051334ed4ef5f817719cb45b82c6837b034961026ceaa524ed98772a52a5e029575792007c7425 config-postmarketos-qcom-sdm845.aarch64
fbd04f5f429ca1067dadb4209d5279e5fd3cfb4a5ae5957e8e189488109a6cf44d27aa1f38cceb78e1cc360d3020b0c4342ca768243eb6ba7460093a1306b98c 0001-arm64-dts-qcom-sdm845-xiaomi-beryllium-Fix-compatibl.patch
"
@@ -2,17 +2,18 @@
# Automatically generated file; DO NOT EDIT.
# Linux/arm64 6.16.7 Kernel Configuration
#
CONFIG_CC_VERSION_TEXT="Alpine clang version 21.1.2"
CONFIG_CC_VERSION_TEXT="Alpine clang version 21.1.8"
CONFIG_GCC_VERSION=0
CONFIG_CC_IS_CLANG=y
CONFIG_CLANG_VERSION=210102
CONFIG_CLANG_VERSION=210108
CONFIG_AS_IS_LLVM=y
CONFIG_AS_VERSION=210102
CONFIG_AS_VERSION=210108
CONFIG_LD_VERSION=0
CONFIG_LD_IS_LLD=y
CONFIG_LLD_VERSION=210102
CONFIG_LLD_VERSION=210108
CONFIG_RUSTC_VERSION=0
CONFIG_RUSTC_LLVM_VERSION=0
CONFIG_CC_CAN_LINK=y
CONFIG_CC_HAS_ASM_GOTO_OUTPUT=y
CONFIG_CC_HAS_ASM_GOTO_TIED_OUTPUT=y
CONFIG_TOOLS_SUPPORT_RELR=y
@@ -2264,7 +2265,9 @@ CONFIG_DM_THIN_PROVISIONING=y
# CONFIG_DM_INIT is not set
# CONFIG_DM_UEVENT is not set
# CONFIG_DM_FLAKEY is not set
# CONFIG_DM_VERITY is not set
CONFIG_DM_VERITY=y
# CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG is not set
# CONFIG_DM_VERITY_FEC is not set
# CONFIG_DM_SWITCH is not set
# CONFIG_DM_LOG_WRITES is not set
CONFIG_DM_INTEGRITY=y
@@ -8173,7 +8176,19 @@ CONFIG_PSTORE_PMSG=y
CONFIG_PSTORE_RAM=y
# CONFIG_PSTORE_BLK is not set
# CONFIG_UFS_FS is not set
# CONFIG_EROFS_FS is not set
CONFIG_EROFS_FS=m
# CONFIG_EROFS_FS_DEBUG is not set
CONFIG_EROFS_FS_XATTR=y
CONFIG_EROFS_FS_POSIX_ACL=y
CONFIG_EROFS_FS_SECURITY=y
CONFIG_EROFS_FS_BACKED_BY_FILE=y
CONFIG_EROFS_FS_ZIP=y
# CONFIG_EROFS_FS_ZIP_LZMA is not set
# CONFIG_EROFS_FS_ZIP_DEFLATE is not set
# CONFIG_EROFS_FS_ZIP_ZSTD is not set
# CONFIG_EROFS_FS_ZIP_ACCEL is not set
# CONFIG_EROFS_FS_ONDEMAND is not set
# CONFIG_EROFS_FS_PCPU_KTHREAD is not set
CONFIG_NETWORK_FILESYSTEMS=y
CONFIG_NFS_FS=y
# CONFIG_NFS_V2 is not set
+93
View File
@@ -0,0 +1,93 @@
# 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://postmarketos.org"
options="!check" # no tests
depends="
f0rmz
losetup
sfdisk
shadow
tar
unl0kr
"
# x86: f0rmz
arch="noarch !x86"
license="GPL-3.0-or-later"
replaces="systemd" # For overwriting tmpfiles.d/{opt,var}.conf
_source644="
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/system-preset/80-duranium.preset
usr/lib/systemd/system/systemd-veritysetup@usr.service.d/10-skip-usr-verified.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/share/f0rmz/f0rmz.conf.d/firstboot.conf
usr/share/initramfs/init_functions_immutable.sh
usr/share/mkinitfs/files-extra/10-duranium-base.files
usr/share/mkinitfs/modules-extra/10-duranium-base.modules
"
_source755="
usr/bin/do-factory-reset
etc/profile.d/99-local-bin.sh
"
# 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)"
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
}
sha512sums="
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
9aa66e6472ce58da316357c61b8a1538d0d8829f6568ec404efc58ea7ad5d06f5f806cb7e1bf726eb9941181971d0f7131c34a446a52eedc96f3388d1c31c18f rootfs-usr-lib-repart.d-20-usr-verity-sig.conf
1c452cec92a07859e7bed68ea6b67143d9329446db36eb4aa796424609cc6d96ad2e19d8be53bba776fc2efa75b02c87ec7557eaf233926afd1609611f38a81a rootfs-usr-lib-repart.d-21-usr-verity.conf
f1833d1627640a30dcb7aebc4ad4296c00f1799168712bf556e54ce74f7e56002b0a37e42ea20bfd10a02d32a9e7b7750c6e8d1b957c416903cd6e1bb19d25c6 rootfs-usr-lib-repart.d-22-usr.conf
ee77d7903f108ef3566ca99091e95aeefc70f19930fe635b6110c71566225beb9675356162628efeb5d4c5cc6e386127035d8c34fdbe5ca28b123b69f4e15ddc rootfs-usr-lib-repart.d-30-root.conf
52a0cddde45bf17e7a7274b804ec818bf7a471ec6fe365931f8e14e2f7279111e9724cbd9941c66ee638e92ecb3579a269e75912130590fc4856bd00b7b86a23 rootfs-usr-lib-systemd-system-preset-80-duranium.preset
d8de011bdaa98045b23fe37540663371602bb48f75314343c60faab39e7cd4f336ace6eddde745517bb99d8b5f3bf40d78584bd3535375b5f5fc9c7ae03de315 rootfs-usr-lib-systemd-system-systemd-veritysetup@usr.service.d-10-skip-usr-verified.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
4aa9c49faa69f66750eec519762fbf85715a9ce0882764b4dece795e391bead4dd6a02a5773e3a81382a8405f57a1e2f70fce31ae6e54c5182c9f2baf30993dd rootfs-usr-share-f0rmz-f0rmz.conf.d-firstboot.conf
5549b0745a15ba4a11769cb2adf29695ddea1f16823b73f3ada5e85e4caecdf3f9fd2295ac95f0fdf9ef72e737ea058dc90e2a81b268b04d1993a0033053e9c2 rootfs-usr-share-initramfs-init_functions_immutable.sh
38d93277341c9f21b28b1b6167fefa73377dbec61e4c8fdb56d8cb5688ce231371e6366a284074cdf64e90f46b893a70a64e98cc478f86f5f984d7db26bf53eb rootfs-usr-share-mkinitfs-files-extra-10-duranium-base.files
99325a25d1a588923bf55c9c803ea6aa8d7b7e82b929e4edafc47b39630d0c229bda89ea3afff1adde4136c2378ffb8ef24a4edbac015032975f40edcbad74ed rootfs-usr-share-mkinitfs-modules-extra-10-duranium-base.modules
97bce380b5c4019aafad6f52856586ffaa4e17efd83f61f133e197bd833a35140406d206f572fb38444c42051b0d1dc789f44313aaf63bf0d4028499cc942b7e rootfs-usr-bin-do-factory-reset
765797421f9123cdcf85e1496d01884cce1c8c304d15369b7eee32a8fc5abcd860dc5856c0d493f1cd3c6ad2a0ccdc4d6ba7d2ae356f0fc824dc16cdb84ebf37 rootfs-etc-profile.d-99-local-bin.sh
"
@@ -0,0 +1 @@
export PATH="$PATH:$HOME/.local/bin"
@@ -0,0 +1,84 @@
#!/bin/sh
# Factory reset script for postmarketOS
# Sets EFI variable to trigger factory reset on next boot
set -e
# Check if running as root
if [ "$(id -u)" -ne 0 ]; then
echo "Error: This script must be run as root" >&2
echo "Try: doas $0" >&2
exit 1
fi
# EFI variable path
EFIVAR_PATH="/sys/firmware/efi/efivars/FactoryReset-8cf2644b-4b0b-428f-9387-6d876050dc67"
# Check if EFI variables are available
if [ ! -d "/sys/firmware/efi/efivars" ]; then
echo "Error: EFI variables not available (not booted with UEFI?)" >&2
exit 1
fi
# Check if factory reset is supported by checking for repart configs
if ! systemd-repart --can-factory-reset >/dev/null 2>&1; then
echo "Error: Factory reset not supported (no partitions marked FactoryReset=yes)" >&2
exit 1
fi
# Warn user and ask for confirmation
echo "WARNING: This will erase all user data and reset the system to factory defaults!"
echo "This action cannot be undone."
echo
printf "Are you sure you want to continue? (yes/no): "
read -r response
case "$response" in
yes|YES)
echo "Proceeding with factory reset..."
;;
*)
echo "Factory reset cancelled."
exit 0
;;
esac
# Remove existing EFI variable if it exists
if [ -f "$EFIVAR_PATH" ]; then
echo "Removing existing FactoryReset variable..."
# Clear immutable attribute if set
if ! chattr -i "$EFIVAR_PATH"; then
echo "Warning: Failed to clear immutable attribute, removal may fail" >&2
fi
if ! rm "$EFIVAR_PATH"; then
echo "Error: Failed to remove existing EFI variable" >&2
echo "Try manually: chattr -i $EFIVAR_PATH && rm $EFIVAR_PATH" >&2
exit 1
fi
fi
# Create EFI variable
# Format: 4-byte attributes (0x07 = NON_VOLATILE | BOOTSERVICE_ACCESS | RUNTIME_ACCESS) + "yes"
echo "Creating FactoryReset EFI variable..."
if printf '\x07\x00\x00\x001\x00\x00\x00' > "$EFIVAR_PATH"; then
echo "Factory reset EFI variable set successfully."
echo "System will factory reset on next boot."
echo
printf "Reboot now? (yes/no): "
read -r reboot_response
case "$reboot_response" in
yes|YES)
echo "Rebooting..."
reboot
;;
*)
echo "Factory reset is scheduled for next boot."
echo "Run 'reboot' when ready to perform the reset."
;;
esac
else
echo "Error: Failed to set EFI variable" >&2
echo "Check if EFI variables are writable and try again" >&2
exit 1
fi
@@ -0,0 +1,6 @@
[Partition]
Type=esp
Format=vfat
CopyFiles=/boot:/
SizeMinBytes=1G
SizeMaxBytes=1G
@@ -0,0 +1,4 @@
[Partition]
Type=usr-verity-sig
Label=%M_%A_vts
CopyBlocks=auto
@@ -0,0 +1,4 @@
[Partition]
Type=usr-verity
Label=%M_%A_vty
CopyBlocks=auto
@@ -0,0 +1,4 @@
[Partition]
Type=usr
Label=%M_%A_usr
CopyBlocks=auto
@@ -0,0 +1,5 @@
[Partition]
Type=usr-verity-sig
Label=_empty
SizeMinBytes=16K
SizeMaxBytes=16K
@@ -0,0 +1,4 @@
[Partition]
Type=usr-verity
Label=_empty
NoAuto=1
@@ -0,0 +1,4 @@
[Partition]
Type=usr
Label=_empty
NoAuto=1
@@ -0,0 +1,7 @@
[Partition]
Type=root
Format=btrfs
SizeMinBytes=1G
Weight=20000
FactoryReset=yes
Label=pmOS_root
@@ -0,0 +1,2 @@
enable systemd-sysupdate.timer
enable systemd-sysupdated.service
@@ -0,0 +1,3 @@
[Unit]
# This is the patch that the pmOS initramfs creates the mapping
ConditionPathExists=!/dev/mapper/usr-verified
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,6 @@
g audio - -
g input - -
g netdev - -
g plugdev - -
g video - -
g wheel - -
@@ -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
@@ -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 -
@@ -0,0 +1,23 @@
[intro]
title=Welcome to postmarketOS!
body=The next few screens will assist you with setting up your device.
[form.username]
type=text
label=Username
required=true
[form.password]
type=password
label=Login Password
required=true
[form.fde_password]
type=password
label=Data Encryption Password. Leave empty to disable, not recommended!
required=false
[form.hostname]
type=text
label=Device Name
required=false
@@ -0,0 +1,728 @@
#!/bin/busybox ash
# This file will be in /init_functions_immutable.sh inside the initramfs.
# shellcheck disable=SC3043
USR_PARTITION=""
VERITY_PARTITION=""
BOOT_DEVICE=""
# Mount EFI vars
setup_efivarfs() {
if mountpoint -q /sys/firmware/efi/efivars; then
info "efivars already mounted"
return 0
fi
info "Mounting efivars"
modprobe efivarfs || true
mount -t efivarfs efivarfs /sys/firmware/efi/efivars || true
}
# Extract the ESP *partition* UUID from EFI's LoaderDevicePart variable
# Uses: (none)
# Sets: uuid variable via output parameters
# $1: variable name to store ESP partition UUID
get_esp_partition_uuid_from_efi() {
local result="$1"
local efi_var uuid
setup_efivarfs
efi_var="$(ls /sys/firmware/efi/efivars/LoaderDevicePartUUID-*)"
if [ ! -e "$efi_var" ]; then
echo "ERROR: LoaderDevicePartUUID not found - not booted via EFI or efivarfs failed?"
return 1
fi
# Read UUID, skip first 4 bytes (EFI attributes), convert to lowercase
# https://docs.kernel.org/filesystems/efivarfs.html
uuid=$(dd if="$efi_var" bs=1 skip=4 2>/dev/null | tr -d '\0' | tr '[:upper:]' '[:lower:]')
info "Found ESP UUID: $uuid"
# Set the result
if [ -n "$result" ]; then eval "$result=\"$uuid\""; fi
}
# Get boot device from EFI LoaderDevicePartUUID variable
# Sets: BOOT_DEVICE
# Returns: 1 if EFI variable not found or invalid, 0 on success
find_boot_device() {
local part_uuid device
# If we have subpartitions, the boot device is the partition containing them
if [ -n "$SUBPARTITION_DEV" ]; then
BOOT_DEVICE="$SUBPARTITION_DEV"
info "Using subpartition container as boot device: $BOOT_DEVICE"
return 0
fi
if ! get_esp_partition_uuid_from_efi part_uuid; then
return 1
fi
info "Searching for boot device with uuid: $part_uuid"
show_splash "Waiting for boot device..."
for _ in $(seq 1 30); do
# Find device with matching PARTUUID
device="$(readlink -f "/dev/disk/by-partuuid/$part_uuid")"
if [ -b "$device" ]; then
device="$(basename "$device")"
parent_path="/sys/class/block/$device/.."
info "Searching parent path: $parent_path"
if [ -d "$parent_path" ]; then
BOOT_DEVICE="/dev/$(basename "$(readlink -f "$parent_path")")"
if [ -n "$BOOT_DEVICE" ] && [ "$BOOT_DEVICE" != "/dev/" ]; then
info "Found boot device: $BOOT_DEVICE"
return 0
fi
fi
fi
info "Boot device not found yet, trying again in 1 second..."
sleep 1
done
echo "ERROR: boot device not found after 30 seconds"
return 1
}
factory_reset_requested() {
# TODO: In systemd 258 this variable will be `FactoryResetRequest`
local efi_var="/sys/firmware/efi/efivars/FactoryReset-8cf2644b-4b0b-428f-9387-6d876050dc67"
[ -e "$efi_var" ] || return
# Read value (skip 4-byte EFI attributes)
local value
value="$(dd if="$efi_var" bs=1 skip=4 2>/dev/null | tr -d '\0')"
info "Factory reset EFI var value: $value"
[ "$value" = "1" ] || [ "$value" = "yes" ] || [ "$value" = "true" ]
}
# Run systemd-repart to initialize A/B partition layout on GPT-partitioned block
# devices, creating B-slot partitions sized to match A-slot
# NOTE: This does *not* support subpartitioned devices!
# $1: optional path to luks keyfile for encrypting partition
# Uses: USR_PARTITION, VERITY_PARTITION
# Sets: (none)
# Returns: 1 on error, else 0
run_repart() {
local keyfile="$1"
set -x
# HACK: Symlink os-release --> initrd-release. Ideally this would be handled by
# mkinitfs config, but there's a bug preventing that from working correctly,
# so for now let's do this. Using -f because if the bug is fixed, we don't want
# the initramfs init to fail if the script hasn't been updated yet to remove
# this hack.
# https://gitlab.postmarketos.org/postmarketOS/postmarketos-mkinitfs/-/issues/50
#
# Repart will not enable factory reset logic unless this file exists, to prove
# it's running in the initramfs.
ln -sf /usr/lib/os-release /etc/initrd-release
export TMPDIR=/tmp
# Disable discarding blocks while formatting, otherwise it can take like 10
# minutes to format a 1TB partition with BTRFS on a fast NVMe!
export SYSTEMD_REPART_MKFS_OPTIONS_BTRFS="--nodiscard"
# Calculate slot B partition sizes for repart. Look at their slot A counterparts
# to get the size that they should be. This is better than hardcoding some
# totally made up size (e.g. 20GB) and hoping it's enough for an update to it.
mkdir -p /run/repart.d/22-usr.conf.d
mkdir -p /run/repart.d/21-usr-verity.conf.d
mkdir -p /run/repart.d/12-usr.conf.d
mkdir -p /run/repart.d/11-usr-verity.conf.d
# Need usr partitions for size calculations
wait_partition "usr" "find_usr_partition"
wait_partition "verity" "find_usr_verity_partition"
# Get A-slot sizes
usr_size="$(blockdev --getsize64 "$USR_PARTITION")"
verity_size="$(blockdev --getsize64 "$VERITY_PARTITION")"
info "Using usr partition size: $usr_size"
info "Using usr verity partition size: $verity_size"
# Create size override drop-ins
# Note: This sets limits for existing partitions too because repart likes to
# try and grow existing A-slot partitions if the B-slot partitions don't exist
# when repart runs.
cat > /run/repart.d/22-usr.conf.d/10-runtime-size.conf <<-EOF
[Partition]
SizeMinBytes=${usr_size}
SizeMaxBytes=${usr_size}
EOF
cat > /run/repart.d/12-usr.conf.d/10-runtime-size.conf <<-EOF
[Partition]
SizeMinBytes=${usr_size}
SizeMaxBytes=${usr_size}
EOF
cat > /run/repart.d/21-usr-verity.conf.d/10-runtime-size.conf <<-EOF
[Partition]
SizeMinBytes=${verity_size}
SizeMaxBytes=${verity_size}
EOF
cat > /run/repart.d/11-usr-verity.conf.d/10-runtime-size.conf <<-EOF
[Partition]
SizeMinBytes=${verity_size}
SizeMaxBytes=${verity_size}
EOF
# NOTE: when repart detects it is running in the initramfs, it will assume
# root=sysroot and read configuration from there. Since /sysroot is not mounted
# yet and the filesystem that backs it hasn't been created/populated yet (that's
# the point of repart), we need to tell repart to use the repart.d config in the
# initramfs by specifying root=/
repart_args="--no-pager --dry-run=no --root=/ "
if [ -n "$keyfile" ]; then
repart_args="$repart_args --key-file=$keyfile"
fi
info "Running systemd-repart with args: $repart_args"
local target_device="$BOOT_DEVICE"
if [ -n "$SUBPARTITION_DEV" ]; then
target_device="$(losetup -j "$BOOT_DEVICE" -O NAME --noheadings)"
if [ -z "$target_device" ]; then
echo "ERROR: SUBPARTITION_DEV set but no loop device found"
return 1
fi
fi
if ! systemd-repart $repart_args "$target_device"; then
return 1
fi
partprobe
udevadm settle --timeout=30
}
# Show an interactive UI to let the user configure the system and report the
# values
# Uses: (none)
# Sets: firstboot_username, firstboot_hostname, firstboot_password,
# firstboot_fde_password
# Returns: (none)
get_firstboot_config() {
# Make sure the splash doesn't interfere
hide_splash
local output
output=$(f0rmz)
if [ -n "$output" ] && echo "$output" | grep -q "username="; then
while IFS= read -r line; do
key="${line%%=*}"
value="${line#*=}"
case "$key" in
username)
firstboot_username="$value" ;;
hostname)
firstboot_hostname="$value" ;;
password)
firstboot_password="$value" ;;
fde_password)
firstboot_fde_password="$value" ;;
esac
done <<-EOF
$output
EOF
else
echo "ERROR: running f0rmz configuration UI failed!"
fail_halt_boot
fi
[ -z "$firstboot_hostname" ] && firstboot_hostname="pmOS"
}
# Create dm-verity mapping for /usr partition
# Uses: USR_PARTITION, VERITY_PARTITION, usrhash
# Sets: (none)
# $1: variable name to store verity device path
# Returns: 1 if verity setup fails, 0 on success
setup_verity_mapping() {
local result="$1"
local verity_device="/dev/mapper/usr-verified"
# Wait for both partitions to be available
wait_partition "usr" "find_usr_partition"
wait_partition "verity" "find_usr_verity_partition"
# Set up dm-verity mapping
# shellcheck disable=SC2154
if ! veritysetup open "$USR_PARTITION" usr-verified "$VERITY_PARTITION" "$usrhash"; then
echo "ERROR: Unable to create verity mapping for /usr"
return 1
fi
# Set the result
if [ -n "$result" ]; then eval "$result=\"$verity_device\""; fi
}
# Mount verity-protected /usr partition
# Uses: USR_PARTITION, VERITY_PARTITION, usrhash
# Sets: (none)
# Returns: 1 if verity setup or mount fails, 0 on success
mount_usr_verified() {
local device_path
# Set up dm-verity mapping
if ! setup_verity_mapping device_path; then
return 1
fi
info "Using verified usr partition mapped at: $device_path"
modprobe erofs || true
# Mount /usr read-only
mkdir -p /sysroot/usr
if ! mount -t auto -o ro "$device_path" /sysroot/usr; then
echo "ERROR: Unable to mount verified /usr"
return 1
fi
return 0
}
# Handle systemd-repart for first boot/factory reset
# Uses: BOOT_DEVICE, USR_PARTITION, VERITY_PARTITION, firstboot_fde_password
# Sets: PMOS_ROOT (after partition creation)
# Returns: 1 if repart fails, 0 on success
setup_first_boot_partitions() {
local disk_mb usr_mb verity_mb ab_needed_mb required_mb min_root_mb
local root_part_num repart_args
info "Searching for usr + verity partitions"
wait_partition "usr" "find_usr_partition"
wait_partition "verity" "find_usr_verity_partition"
# Calculate A/B usr+verity + root space needed
disk_mb=$(($(blockdev --getsize64 "$BOOT_DEVICE") / 1024 / 1024))
usr_mb=$(($(blockdev --getsize64 "$USR_PARTITION") / 1024 / 1024))
verity_mb=$(($(blockdev --getsize64 "$VERITY_PARTITION") / 1024 / 1024))
ab_needed_mb=$((usr_mb * 2 + verity_mb * 2))
min_root_mb=5120 # 5GB
required_mb=$((ab_needed_mb + min_root_mb))
info "Disk space analysis:"
info " Disk: ${disk_mb}MB"
info " USR: ${usr_mb}MB, Verity: ${verity_mb}MB"
info " A/B slots need: ${ab_needed_mb}MB"
info " Root needs: ${min_root_mb}MB"
info " Total required: ${required_mb}MB"
if [ $disk_mb -lt $required_mb ]; then
echo "ERROR: Disk too small. Need ${required_mb}MB, have ${disk_mb}MB"
return 1
fi
# Remove any existing root partition, so that it can be recreated with optional encryption
unset PMOS_ROOT && find_root_partition
info "Existing root partiton (if found): $PMOS_ROOT"
root_part_num="$(blkid -p -s PART_ENTRY_NUMBER -o value "$PMOS_ROOT")"
if [ -n "$root_part_num" ]; then
info "Removing existing root partition"
if ! parted -s "$BOOT_DEVICE" rm "$root_part_num"; then
echo "ERROR: Unable to prepare disk for first boot setup"
return 1
fi
fi
# Create runtime repart config based on user choices
mkdir -p /run/repart.d
local keyfile=""
if [ -n "$firstboot_fde_password" ]; then
info "FDE enabled for new root partition"
# Copy base config and add encryption
cp /usr/lib/repart.d/30-root.conf /run/repart.d/
cat >> /run/repart.d/30-root.conf <<- EOF
Encrypt=key-file
EOF
# Create key file for repart
printf "%s" "$firstboot_fde_password" > /tmp/luks.key
chmod 0600 /tmp/luks.key
keyfile="/tmp/luks.key"
fi
if ! run_repart "$keyfile"; then
echo "ERROR: systemd-repart failed"
return 1
fi
# Find the newly created root partition
unset PMOS_ROOT && find_root_partition
info "Found new root partition: $PMOS_ROOT"
if [ -z "$PMOS_ROOT" ]; then
echo "ERROR: Root partition not found after repart"
return 1
fi
# Unlock automatically if encrypted, since we already have the passphrase
if [ -n "$firstboot_fde_password" ] && [ -f /tmp/luks.key ]; then
info "Unlocking new root partition"
if ! cryptsetup open --key-file=/tmp/luks.key "$PMOS_ROOT" root; then
echo "ERROR: Failed to unlock encrypted root partition"
return 1
fi
PMOS_ROOT="/dev/mapper/root"
fi
# Clean up key file
rm -f /tmp/luks.key
return 0
}
# Create user account and run systemd-firstboot
# Uses: firstboot_* variables from get_firstboot_config
# Sets: (none)
# Returns: 1 if user setup fails, 0 on success
setup_first_boot_system() {
# Manually copy user/group files from factory, we want to append to these
info "Creating skeleton /etc with user/group databases from factory"
mkdir -p /sysroot/etc
for f in passwd group shadow; do
if ! cp -a /sysroot/usr/share/factory/etc/"$f" /sysroot/etc/; then
echo "ERROR: Failed to copy factory $f file"
return 1
fi
done
# useradd later will automatically add the new user to these files if they exist beforehand
touch /sysroot/etc/subuid
touch /sysroot/etc/subgid
# Run systemd-firstboot
echo "Setting up system configuration..."
if ! systemd-firstboot --root=/sysroot \
--keymap=us \
--locale=en_US.UTF-8 \
--timezone="UTC" \
--hostname="$firstboot_hostname"; then
echo "ERROR: systemd-firstboot failed"
return 1
fi
echo "Creating user account..."
# Create user account
if ! useradd --root /sysroot \
--comment "Default User" \
--create-home \
--user-group \
--shell /bin/ash \
--groups audio,input,netdev,plugdev,video,wheel,render \
"$firstboot_username"; then
echo "ERROR: Failed to create user account"
return 1
fi
# Set password for user account
info "Configuring user password"
if ! echo "$firstboot_username:$firstboot_password" | chpasswd -R /sysroot; then
echo "ERROR: Failed to set user password"
return 1
fi
# Save user's name for future generators
echo "$firstboot_username" > /sysroot/etc/default_user
# Lock root account
info "Locking root account"
echo "root:!" | chpasswd -e -R /sysroot
# Create essential symlinks
info "usr merge \o/"
ln -s /usr/bin /sysroot/bin
ln -s /usr/sbin /sysroot/sbin
ln -s /usr/lib /sysroot/lib
return 0
}
# Handle first boot scenario
# Uses: BOOT_DEVICE, USR_PARTITION, VERITY_PARTITION
# Sets: PMOS_ROOT (via setup_first_boot_partitions)
# Returns: calls fail_halt_boot on error, 0 on success
handle_first_boot() {
echo "No root partition found - performing first boot setup"
show_splash "Preparing for first boot..."
# Get configuration values from user
get_firstboot_config
echo "Creating partitions"
show_splash "Preparing disk..."
# Create partitions and unlock if encrypted
if ! setup_first_boot_partitions; then
show_splash "ERROR: Failed to partition disk"
fail_halt_boot
fi
# Mount root partition (os-release won't exist yet, that's OK)
try_mount_root_partition
case $? in
0) # Success, continue
;;
3) # Missing os-release, expected for first boot
info "Root partition not populated yet, and that's OK"
;;
*) # Other error
show_splash "ERROR: Failed to mount root partition"
echo "ERROR: Failed to mount root partition"
fail_halt_boot
;;
esac
info "Mounting verified usr partition"
if ! mount_usr_verified; then
show_splash "ERROR: Failed to mount usr partition"
fail_halt_boot
fi
# Setup user account and system configuration
show_splash "Configuring system..."
echo "Configuring system"
if ! setup_first_boot_system; then
show_splash "ERROR: Failed to setup user account"
fail_halt_boot
fi
echo "First boot setup complete"
show_splash "First boot setup complete"
}
# Resolve the given partuuid to a block device
# $1: partition UIUD to resolve
# Returns: path to block device, else empty string if not found
find_part_by_uuid() {
local uuid="$1"
local path=/dev/disk/by-partuuid/"$uuid"
if [ -L "$path" ]; then
readlink -f "$path"
return
fi
# Check nested partition table if we have subpartitions
if [ -n "$SUBPARTITION_DEV" ]; then
local partnum
partnum="$(sfdisk -d "$SUBPARTITION_DEV" 2>/dev/null | grep -i "uuid=$uuid" | sed -n 's/.*p\([0-9]\+\) :.*/\1/p')"
if [ -n "$partnum" ]; then
echo "/dev/mapper/$(basename "$SUBPARTITION_DEV")p${partnum}"
return
fi
fi
echo ""
}
# Change a UUID in the form XXXXXXXXXXXXX... to XXXXXXXX-XXXX-XXXX-...
format_raw_uuid(){
local raw="$1"
local ret
# NOTE: parts are extracted below one at a time to help with code
# readability
ret="$(echo "$raw" | cut -c1-8)"
ret="$ret-$(echo "$raw" | cut -c9-12)"
ret="$ret-$(echo "$raw" | cut -c13-16)"
ret="$ret-$(echo "$raw" | cut -c17-20)"
ret="$ret-$(echo "$raw" | cut -c21-32)"
echo "$ret"
}
# Search for a /usr partition using usrhash
# Uses: usrhash
# Sets: USR_PARTITION
# $1: variable name to store result
# Returns: 0 always
find_usr_partition() {
# $1: variable to set result to
local result="$1"
local usr_uuid_raw usr_uuid
if [ -z "$USR_PARTITION" ]; then
# Extract first 128 bits (32 hex chars) for /usr partition UUID
# shellcheck disable=SC2154
usr_uuid_raw="$(echo "$usrhash" | cut -c1-32)"
usr_uuid="$(format_raw_uuid "$usr_uuid_raw")"
USR_PARTITION="$(find_part_by_uuid "$usr_uuid")"
fi
# Set the result, since using a subshell prevents us from caching
if [ -n "$result" ]; then eval "$result=\"$USR_PARTITION\""; fi
}
# Search for a /usr verity partition using usrhash
# Uses: usrhash
# Sets: VERITY_PARTITION
# $1: variable name to store result
# Returns: 0 always
find_usr_verity_partition() {
# $1: variable to set result to
local result="$1"
local verity_uuid_raw verity_uuid
if [ -z "$VERITY_PARTITION" ]; then
# Extract last 128 bits (32 hex chars) for verity partition UUID
verity_uuid_raw="$(echo "$usrhash" | cut -c33-64)"
verity_uuid="$(format_raw_uuid "$verity_uuid_raw")"
VERITY_PARTITION="$(find_part_by_uuid "$verity_uuid")"
fi
# Set the result, since using a subshell prevents us from caching
if [ -n "$result" ]; then eval "$result=\"$VERITY_PARTITION\""; fi
}
# Get the appropriate root GPT type for the booted arch
get_root_partition_uuid() {
# shellcheck disable=SC2154
case "$deviceinfo_arch" in
x86_64) echo "4f68bce3-e8cd-4db1-96e7-fbcaf984b709" ;;
x86) echo "44479540-f297-41b2-9af7-d131d5f0458a" ;;
aarch64) echo "b921b045-1df0-41c3-af44-4c6f280d3fae" ;;
armv7|armhf) echo "69dad710-2ce4-4e3c-b16c-21a1d49abed3" ;;
riscv64) echo "72ec70a6-cf74-40e6-bd49-4bda08e8f224" ;;
*) echo "" ;; # Unsupported arch
esac
}
# Use GPT type to find the appropriate root partition on the booted block device
find_root_on_boot_device() {
local root_uuid
root_uuid="$(get_root_partition_uuid)"
[ -z "$root_uuid" ] && return 1
set -x
# For subpartitions, search the nested partition table
if [ -n "$SUBPARTITION_DEV" ]; then
local partnum
partnum="$(sfdisk -d "$SUBPARTITION_DEV" 2>/dev/null | grep -i "type=$root_uuid" | sed -n 's/.*p\([0-9]\+\) :.*/\1/p')"
if [ -n "$partnum" ]; then
echo "${SUBPARTITION_LOOP}p${partnum}"
return
fi
else
# Check each partition on the boot device
local partition
for partition in "$BOOT_DEVICE"*; do
if blkid -p -s PART_ENTRY_TYPE "$partition" | grep -qi "$root_uuid"; then
echo "$partition"
return
fi
done
fi
}
# Limit USB storage transfer size to work around dm-verity corruption issue
# See: https://gitlab.postmarketos.org/postmarketOS/duranium/-/issues/1
# Uses: BOOT_DEVICE
# Sets: (none)
# Returns: 0 always
limit_usb_transfer_size() {
local device_name sysfs_path device_path
device_name="$(basename "$BOOT_DEVICE")"
sysfs_path="/sys/block/$device_name/queue/max_sectors_kb"
device_path="/sys/block/$device_name/device"
if [ ! -L "$device_path" ]; then
info "Not setting max_sectors_kb: device path not found"
return
fi
# Check if device symlink contains /usb, indicating it's a usb device. This
# seems like a safe assumption to make, and it prevents us from applying this
# workaround for non-usb devices.
if ! readlink -f "$device_path" | grep -q "/usb"; then
info "Not setting max_sectors_kb: not a USB device"
return
fi
if [ ! -f "$sysfs_path" ]; then
info "Not setting max_sectors_kb: sysfs file not found"
return
fi
echo 32 > "$sysfs_path"
info "Applied USB workaround: set max_sectors_kb=32 for $device_name"
return
}
# Handle immutable boot with A/B updates and factory reset
# Uses: usrhash
# Sets: BOOT_DEVICE, USR_PARTITION, VERITY_PARTITION, PMOS_ROOT
# Returns: calls fail_halt_boot on error, 0 on success
handle_immutable_boot() {
# Check usrhash length, fail fast (should be 64 hex characters = 256 bits)
if [ ${#usrhash} -ne 64 ]; then
show_splash "ERROR: Unable to boot immutable image!"
echo "ERROR: usrhash should be 64 hex characters (256 bits)"
fail_halt_boot
fi
setup_efivarfs
# Make sure loop is loaded, else repart falls back to using a file and it's
# buggy
modprobe loop
if ! find_boot_device; then
show_splash "ERROR: Unable to find required EFI variable"
fail_halt_boot
fi
limit_usb_transfer_size
set -x
# Factory reset handling
if factory_reset_requested; then
show_splash "Factory resetting device..."
echo "Factory reset requested - deleting partitions"
if ! run_repart; then
show_splash "ERROR: Factory reset failed"
echo "ERROR: systemd-repart failed"
fail_halt_boot
fi
# Treat this as a "first boot" since partitions were cleared
handle_first_boot
return
fi
set +x
# Try normal boot
info "Trying normal bootup"
find_root_partition
if [ -z "$PMOS_ROOT" ]; then
info "No root partition found, proceeding to first boot setup"
handle_first_boot
return
fi
info "Root partition found: $PMOS_ROOT"
unlock_root_partition
if ! try_mount_root_partition; then
info "Unable to mount root, it was not populated, proceeding to first boot setup"
# Clean up
if mountpoint -q /sysroot; then
umount /sysroot
fi
cryptsetup close /dev/mapper/root || true
handle_first_boot
return
fi
info "Root mounted, proceeding with normal boot"
if ! mount_usr_verified; then
show_splash "ERROR: Unable to mount OS data"
fail_halt_boot
fi
}
@@ -0,0 +1,15 @@
/usr/bin/systemd-firstboot
/usr/bin/systemd-repart
/usr/lib/repart.d
/usr/sbin/mkfs.btrfs
/usr/sbin/sfdisk
/usr/sbin/losetup
/usr/sbin/useradd
/usr/sbin/veritysetup
/usr/share/initramfs/init_functions_immutable.sh:/init_functions_immutable.sh
/usr/share/systemd/kbd-model-map
/usr/share/zoneinfo/
# Note: initrd-release is used to signal to repart that it's running in an
# initramfs environment. If we don't convince it, then it won't let us do
# factory resetting.
/usr/lib/os-release:/etc/initrd-release
@@ -0,0 +1,3 @@
dm-verity
efivarfs
erofs
@@ -4,7 +4,7 @@
/etc/unudhcpd.conf
/sbin/blkid
/usr/bin/unudhcpd
/usr/sbin/kpartx
/usr/sbin/losetup
/usr/share/deviceinfo/deviceinfo
/usr/share/initramfs/init_functions.sh:/init_functions.sh
/usr/share/initramfs/init.sh:/init
+7 -6
View File
@@ -1,7 +1,7 @@
# Co-Maintainer: Clayton Craft <clayton@craftyguy.net>
maintainer="Casey Connolly <kcxt@postmarketos.org>"
pkgname=postmarketos-initramfs
pkgver=3.8.11
pkgver=3.9.0
pkgrel=0
pkgdesc="Base files for the postmarketOS initramfs / initramfs-extra"
url="https://postmarketos.org"
@@ -28,6 +28,7 @@ depends="
iskey
kmod
libinput-libs
losetup
lz4
multipath-tools
parted
@@ -111,12 +112,12 @@ package() {
sha512sums="
9d1358886fc2d3eac27c13b11e998458da41ac222c1bfa986ae948970419775b5488ab8b8404a6aa0e21c47717316aa9717b5d5ddc53ec610ad875328ea18e9f 00-default.modules
9c0e8f6f61d5da191e03a1aa9d5d0ceb5baf1eae6dbb9bfb0af59817783525119ac8394b135f303f7b6434a3eab0b49185fb90379e06823db847a4999c75ce33 00-initramfs-base.dirs
e268094cbd25e063fdab2226f8cf93885bd8b90455991a53296231a3401a03669440333025ca44b2a8459df98787a5725f7b227ccb9750f8fcad17ef58ba21a1 00-initramfs-base.files
229910dc50758e079ef35210891825d950c9266e3dc11ac1b420f32ceb552cbced282ee03d4674f94238b6e3688cdef9391b18b9b311fc548f051fb95d4210d2 00-initramfs-base.files
1bb1930cb5bdb60a074195f772dc015b827487a3efd0d68b51ade52164b6371a756249753fbbdc56e975d18f24ada6a1339ea204d2d8fe7ecd95d7aec8647161 00-initramfs-extra-base.files
e90596540805662211bd2a2e427ce7cc38f6a30023267f902e23f2bae136b6cd8f4f4716b8fb611be9d671a964edd2ecce03fa32bc8024e29b8375b0097fde0c init.sh
05d1ec265f6ca5c4073e0f985729905fdd8f8e11d64bd099f7c50f172827614dd43193399e04d5405edf20248223494b884319d3cc866a32033fb5c313c6e226 init_functions.sh
6b4881f9b43c90ebcc70d7b7a192d26a2e3ce01c43b8536fe900c2ffaf74ef035803b9aa083e7a9d5c49c75be8ba6ff22997a037ae4f399e6ee5e1978c4b3f9c init_2nd.sh
de14f6f678856ae316f28c003a6293a383aa6b7cbb19951b981430eb0e3b91d0634dedf62d29d105845b6e172daa18e6b6e45e03342d72ae806ca611a1236d18 init_functions_2nd.sh
a625447092b7c14b759acda96fe2c311500d621bc54d7e6d809f5cd4ba03018051e0d2f3937fe2abc43955aac5410ab8f706cf259ebcc34a80bf788fad2a4176 init.sh
977f024cef32c391dc6c556dbfdecb8c18be3e74ae8476e6b3c5152e555c228a5bf48845f5e693ee3c0d8320ec819593184925d77659034fb7c55772071f77d8 init_functions.sh
2ca09c293d631a22c5e7861f9f74de82743b61755de619b8ddd88d2f8a09ba12dba06d6a1c8494a82730e9f63d100bd233fb85e27a3cb6004950a50cb263e6da init_2nd.sh
70600706cd8eb18cc990a4e4e286807873a5e0369124c28f0ee511876411535911d073367c959cad8452a72a4750741d49208fa9d1a2ee4c73e968171c5e3523 init_functions_2nd.sh
675e7d5bee39b2df7d322117f8dcaccc274d61beaf4d50ead19bbf2109446d64b1c0aa0c5b4f9846eb6c1c403418f28f6364eff4537ba41120fbfcbc484b7da7 mdev.conf
ba3275a9af788c7c782322a22a0f144d5e50e3498ea6886486a29331f23ae89cd32d500a3635cfa7cab369afba92edc18aeca64ccbf0cd589061cce23d15b46c unudhcpd.conf
"
+4
View File
@@ -24,6 +24,10 @@ trap 'reboot -f' TERM
# provide a default for os-release's VERSION in case the file doesn't exist
VERSION="${VERSION:-unknown}"
if [ -f /init_functions_immutable.sh ]; then
. /init_functions_immutable.sh
fi
# This is set during packaging and is used when triaging bug reports
INITRAMFS_PKG_VERSION="<<INITRAMFS_PKG_VERSION>>"
+20 -12
View File
@@ -8,6 +8,9 @@
# The set -a in init.sh only exports variables, not functions
. /init_functions.sh
. /init_functions_2nd.sh
if [ -f /init_functions_immutable.sh ]; then
. /init_functions_immutable.sh
fi
# Handle halt/poweroff/reboot
# Signals from busybox/halt.c
@@ -50,19 +53,23 @@ mount_subpartitions
run_hooks /hooks-extra
wait_root_partition
delete_old_install_partition
resize_root_partition
unlock_root_partition
resize_root_filesystem
mount_root_partition
resize_filesystem_after_mount /sysroot
if is_immutable_boot; then
handle_immutable_boot
else
wait_root_partition
delete_old_install_partition
resize_root_partition
unlock_root_partition
resize_root_filesystem
mount_root_partition
resize_filesystem_after_mount /sysroot
# Mount boot partition into sysroot if needed since some
# old installations don't have a proper /etc/fstab file. See #2800
if [ -z "$(cat /sysroot/etc/fstab | grep -v "#" | tr -d '[:space:]')" ]; then
wait_boot_partition
mount_boot_partition /sysroot/boot "rw"
# Mount boot partition into sysroot if needed since some
# old installations don't have a proper /etc/fstab file. See #2800
if [ -z "$(cat /sysroot/etc/fstab | grep -v "#" | tr -d '[:space:]')" ]; then
wait_boot_partition
mount_boot_partition /sysroot/boot "rw"
fi
fi
init="/sbin/init"
@@ -102,6 +109,7 @@ done
# with /dev/log in there.
rm /dev/log 2>/dev/null || true
# shellcheck disable=SC2093
exec switch_root /sysroot "$init"
+122 -52
View File
@@ -7,6 +7,7 @@
PMOS_BOOT="${PMOS_BOOT:-}"
PMOS_ROOT="${PMOS_ROOT:-}"
SUBPARTITION_DEV="${SUBPARTITION_DEV:-}"
SUBPARTITION_LOOP="${SUBPARTITION_LOOP:-}"
CONFIGFS="/config/usb_gadget"
CONFIGFS_ACM_FUNCTION="acm.usb0"
@@ -18,6 +19,7 @@ deviceinfo_name="${deviceinfo_name:-}"
deviceinfo_codename="${deviceinfo_codename:-}"
deviceinfo_create_initfs_extra="${deviceinfo_create_initfs_extra:-}"
deviceinfo_no_framebuffer="${deviceinfo_no_framebuffer:-}"
deviceinfo_rootfs_image_sector_size="${deviceinfo_rootfs_image_sector_size:-}"
# Does word start with prefix?
startswith() {
@@ -99,6 +101,11 @@ parse_cmdline_item() {
# shellcheck disable=SC2034
log_info=y
;;
usrhash)
# used by init_2nd.sh which sources this script.
# shellcheck disable=SC2034
usrhash="$value"
;;
[![:alpha:]_]* | [[:alpha:]_]*[![:alnum:]_]*)
# invalid shell variable, ignore it
;;
@@ -260,6 +267,10 @@ info() {
echo "$@"
}
is_immutable_boot() {
[ -n "$usrhash" ]
}
mount_proc_sys_dev() {
# mdev
mount -t proc -o nodev,noexec,nosuid proc /proc || echo "Couldn't mount /proc"
@@ -342,6 +353,7 @@ setup_dynamic_partitions() {
}
mount_subpartitions() {
set -x
# skip if ran already (unmerged -extra)
if [ -n "$PMOS_ROOT" ] && [ -n "$PMOS_BOOT" ]; then
return
@@ -352,36 +364,43 @@ mount_subpartitions() {
[ -e "$x" ] && android_parts="$android_parts $x"
done
local loopdev
local losetup_args="--show -Pfv --direct-io=on"
if [ -n "$deviceinfo_rootfs_image_sector_size" ]; then
losetup_args="$losetup_args --sector-size $deviceinfo_rootfs_image_sector_size"
fi
attempt_start=$(get_uptime_seconds)
wait_seconds=10
echo "Trying to mount subpartitions for $wait_seconds seconds..."
find_root_partition
while [ -z "$PMOS_ROOT" ]; do
# For immutable boot, root partition may not exist yet (first boot), but boot
# partition with ESP will. Break early if we find either to avoid timeout.
while [ -z "$PMOS_ROOT" ] && [ -z "$PMOS_BOOT" ]; do
partitions="$android_parts $(grep -v "loop\|ram" < /proc/diskstats |\
sed 's/\(\s\+[0-9]\+\)\+\s\+//;s/ .*//;s/^/\/dev\//')"
for partition in $partitions; do
# Skip whole disks - only check partitions and logical device-mapper devices for subpartitions
[ -e "/sys/class/block/$(basename "$partition")/partition" ] || [ -d "/sys/class/block/$(basename "$partition")/dm" ] || continue
case "$(kpartx -l "$partition" 2>/dev/null | wc -l)" in
2)
echo "Mount subpartitions of $partition"
SUBPARTITION_DEV="$partition"
kpartx -afs "$partition"
# Ensure that this was the *correct* subpartition
# Some devices have mmc partitions that appear to have
# subpartitions, but aren't our subpartition.
find_root_partition
if [ -n "$PMOS_ROOT" ]; then
break
fi
kpartx -d "$partition"
SUBPARTITION_DEV=""
continue
;;
*)
continue
;;
esac
local part_count
part_count="$(parted -s "$partition" print 2>/dev/null | grep -c '^ [0-9]')"
if [ "$part_count" -ge 2 ]; then
echo "Mount subpartitions of $partition"
SUBPARTITION_DEV="$partition"
local loopdev
loopdev="$(losetup $losetup_args "$partition")"
SUBPARTITION_LOOP="$loopdev"
# Ensure that this was the *correct* subpartition
# Some devices have mmc partitions that appear to have
# subpartitions, but aren't our subpartition.
find_root_partition
find_boot_partition
if [ -n "$PMOS_ROOT" ] || [ -n "$PMOS_BOOT" ]; then
break
fi
[ -n "$loopdev" ] && losetup -vd "$loopdev"
SUBPARTITION_DEV=""
SUBPARTITION_LOOP=""
fi
done
if [ "$(get_uptime_seconds)" -ge $(( attempt_start + wait_seconds )) ]; then
echo "ERROR: failed to mount subpartitions!"
@@ -408,7 +427,7 @@ pretty_dm_path() {
# Prints the path to the partition if found, or nothing.
find_partition() {
# $1: UUID of partition if known
# $1: UUID of partition or filesystem on the partition, if known
# $2: path to partition
# $3: label of partition
# $4: additional blkid token to check (e.g TYPE=crypto_LUKS)
@@ -422,10 +441,24 @@ find_partition() {
if [ -n "$uuid" ]; then
partition="$(blkid --uuid "$uuid")"
if [ -z "$partition" ]; then
# Don't fall back to anything if the given UUID wasn't
# found, it might show up later but if not we should
# error out.
return
# Try to find by PARTUUID in nested partition table if we have subpartitions and
# doing immutable boot. We have to use sfdisk here to search partition UUIDs
# since those don't seem to be exposed by blkid
if [ -n "$SUBPARTITION_DEV" ] && is_immutable_boot; then
# Get partition number from nested GPT
local partnum
partnum="$(sfdisk -d "$SUBPARTITION_DEV" 2>/dev/null | grep -i "uuid=$uuid" | sed -n 's/.*p\([0-9]\+\) :.*/\1/p')"
if [ -n "$partnum" ]; then
partition="/dev/mapper/$(basename "$SUBPARTITION_DEV")p${partnum}"
fi
fi
if [ -z "$partition" ]; then
# Don't fall back to anything if the given UUID wasn't
# found, it might show up later but if not we should
# error out.
return
fi
fi
fi
@@ -466,7 +499,11 @@ find_root_partition() {
# mount_subpartitions() must get executed before calling
# find_root_partition(), so partitions from b) also get found.
if [ -z "$PMOS_ROOT" ]; then
PMOS_ROOT="$(find_partition "$root_uuid" "$root_path" "pmOS_root" "TYPE=crypto_LUKS")"
if is_immutable_boot; then
PMOS_ROOT="$(find_root_on_boot_device)"
else
PMOS_ROOT="$(find_partition "$root_uuid" "$root_path" "pmOS_root" "TYPE=crypto_LUKS")"
fi
fi
# Set the result, since using a subshell prevents us from caching
@@ -485,6 +522,9 @@ find_boot_partition() {
PMOS_BOOT="/sysroot/boot"
mount --bind /sysroot/boot /boot
else
if is_immutable_boot; then
get_esp_partition_uuid_from_efi boot_uuid
fi
PMOS_BOOT="$(find_partition "$boot_uuid" "$boot_path" "pmOS_boot")"
fi
fi
@@ -675,56 +715,86 @@ has_unallocated_space() {
head -n1 | grep -qi "free space"
}
mount_root_partition() {
# Mount root partition to /sysroot with error handling
# Uses: rootfsopts
# Sets: partition and type variables via output parameters
# $1: variable name to store partition path
# $2: variable name to store filesystem type
# Returns: 0 on success, 1 if unsupported filesystem, 2 if mount fails, 3 if no /etc/os-release
try_mount_root_partition() {
# Don't mount root if it is already mounted
if mountpoint -q /sysroot; then
return
fi
local partition
local partition_var="$1"
local type_var="$2"
local partition_path fs_type
find_root_partition partition
find_root_partition partition_path
echo "Mount root partition ($partition) to /sysroot (read-write) with options ${rootfsopts#,}"
type="$(get_partition_type "$partition")"
info "Detected $type filesystem"
echo "Mount root partition ($partition_path) to /sysroot (read-write) with options ${rootfsopts#,}"
fs_type="$(get_partition_type "$partition_path")"
info "Detected $fs_type filesystem"
case "$type" in
btrfs|ext4|f2fs|xfs)
;;
*)
echo "ERROR: Detected unsupported '$type' filesystem ($partition)."
show_splash "ERROR: unsupported '$type' filesystem ($partition)\\nhttps://postmarketos.org/troubleshooting"
fail_halt_boot
;;
case "$fs_type" in
btrfs|ext4|f2fs|xfs) ;;
*) return 1 ;;
esac
if ! modprobe "$type"; then
info "Unable to load module '$type' - assuming it's built-in"
if ! modprobe "$fs_type"; then
info "Unable to load module '$fs_type' - assuming it's built-in"
fi
# btrfs may be using multiple backing block devices, scan for the rest of them
if [ "$type" = "btrfs" ]; then
if [ "$fs_type" = "btrfs" ]; then
btrfs device scan
fi
if ! mount -t "$type" -o rw"$rootfsopts" "$partition" /sysroot; then
echo "ERROR: unable to mount root partition!"
show_splash "ERROR: unable to mount root partition\\nhttps://postmarketos.org/troubleshooting"
fail_halt_boot
if ! mount -t "$fs_type" -o rw"$rootfsopts" "$partition_path" /sysroot; then
return 2
fi
if [ -e /sysroot/.stowaways/pmos/etc/os-release ]; then
umount /sysroot
mkdir /stowaway
mount -t "$type" -o rw"$rootfsopts" "$partition" /stowaway
mount -t "$fs_type" -o rw"$rootfsopts" "$partition_path" /stowaway
mount --bind /stowaway/.stowaways/pmos/ /sysroot
fi
if ! [ -e /sysroot/etc/os-release ]; then
show_splash "ERROR: root partition does not contain a root filesystem\\nhttps://postmarketos.org/troubleshooting"
fail_halt_boot
# This also explicitly checks if os-release is a symlink, since -e can fail if
# it exists but is a broken symlink (e.g. the usr partition isn't mounted yet)
if ! [ -e /sysroot/etc/os-release ] && ! [ -L /sysroot/etc/os-release ]; then
return 3
fi
if [ -n "$partition_var" ]; then eval "$partition_var=\"$partition_path\""; fi
if [ -n "$type_var" ]; then eval "$type_var=\"$fs_type\""; fi
}
# Mount root partition to /sysroot (wrapper with error handling)
# Uses: (none)
# Sets: (none)
# Returns: calls fail_halt_boot on error, 0 on success
mount_root_partition() {
local partition type
if ! try_mount_root_partition partition type; then
local ret=$?
case $ret in
1)
echo "ERROR: Detected unsupported '$type' filesystem ($partition)."
show_splash "ERROR: unsupported '$type' filesystem ($partition)\\nhttps://postmarketos.org/troubleshooting"
fail_halt_boot ;;
2)
echo "ERROR: unable to mount root partition!"
show_splash "ERROR: unable to mount root partition\\nhttps://postmarketos.org/troubleshooting"
fail_halt_boot ;;
3)
echo "ERROR: root partition does not contain a root filesystem"
show_splash "ERROR: root partition does not contain a root filesystem\\nhttps://postmarketos.org/troubleshooting"
fail_halt_boot
esac
fi
}
@@ -45,10 +45,15 @@ resize_root_partition() {
fi
if has_unallocated_space "$partition_dev"; then
echo "Resize root partition ($partition)"
# unmount subpartition, resize and remount it
kpartx -d "$partition"
# find and detach the backing loop device
loopdev=$(losetup -j "$partition_dev" -O NAME --noheadings)
[ -n "$loopdev" ] && losetup -vd "$loopdev"
parted -f -s "$partition_dev" resizepart 2 100%
kpartx -afs "$partition_dev"
local losetup_args="--show -Pfv --direct-io=on"
if [ -n "$deviceinfo_rootfs_image_sector_size" ]; then
losetup_args="$losetup_args --sector-size $deviceinfo_rootfs_image_sector_size"
fi
losetup $losetup_args "$partition_dev"
else
echo "Not resizing root partition ($partition): no free space left"
fi