patches and low-level development discussion
 help / color / mirror / code / Atom feed
From: Demi Marie Obenour <demiobenour@gmail.com>
To: Spectrum OS Development <devel@spectrum-os.org>
Cc: Demi Marie Obenour <demiobenour@gmail.com>, Alyssa Ross <hi@alyssa.is>
Subject: [PATCH v4 11/13] Support updates via systemd-sysupdate
Date: Wed, 26 Nov 2025 14:34:09 -0500	[thread overview]
Message-ID: <20251126-updates-v4-11-40c438d2dcaf@gmail.com> (raw)
In-Reply-To: <20251126-updates-v4-0-40c438d2dcaf@gmail.com>

Include a new `spectrum-update` command to update the system.  This
tells the new sys.appvm-systemd-sysupdate VM to download the updates
into a staging directory using systemd-sysupdate.  The host then runs
systemd-sysupdate to apply the updates itself.

sys.appvm-systemd-sysupdate uses host-provided information to fetch the
update.  This allows editing files on the host to change the update URL
and signing key.

systemd-sysupdate requires /boot to be mounted so that systemd-sysupdate
can update the unified kernel image.  systemd-sysupdate also requires
that /tmp is writable so that it can store temporary files, so put a
tmpfs there.  Furthermore, there needs to be a directory for storing
downloaded updates.  Create /home so that users can mount their
persistent data there.

The directory the VM downloads updates into is *not* reset (wiped)
before or after the update.  This allows the VM to know if the system is
already up to date.  Otherwise, it would redownload the entire
multi-gigabyte update image.

Updates are currently not compressed.  This should be changed in the
future, but it would add a small amount of additional complexity.  In
particular, the script generating the update directory would need to
generate a SHA256SUMS containing the hash of both the compressed and
uncompressed versions.  More importantly, the VM must not be able to
make the host use the compressed version.  This would be a potential
security risk because decompression happens before signature
verification.  GnuPG currently decompresses signatures, but in the
future it will be replaced by Sequoia which does not.

Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
---
Changes since v4:
- Do not strip leading and trailing whitespace from update URLs.
- Create a single script that does the work.  Pass the paths to curl and
  systemd sysupdate to it as environment variables.  Inline the awk
  script into it.
- Rebase and fix merge conflict.

Changes since v3:

- Move builtins.path from lib/config.nix to host/rootfs/default.nix.
- Change config options from "update-url" to "updateUrl" and
  "update-signing-key" to "updateSigningKey".

Changes since v2:

- Generate the transfer files in the guest, not the host.
- Do not use an environment file.
- Reject URLs that cannot work.
- Escape sed metacharacters.
- Escape backslashes for systemd-sysupdate.
- Do not validate update URLs at build time, only at runtime.
- Reject only update URLs that cannot possibly work.
- Set the partition UUIDs according to systemd's recommendation.
- Do not rely on finding partitions by label.
- Strip leading and trailing whitespace from the update URL.
- Rename the update command from `update` to `spectrum-update`.
- Delete the list of steps.  Replace it with comments in the script.

The awk script in the VM rejects URLs that contain whitespace.  This is
because they can't work, and passing them to systemd-sysupdate would
require figuring out how to escape them.  Rejecting such bogus URLs is
simpler than preventing them from being mangled.

Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
---
 host/rootfs/Makefile                               | 17 +++-
 host/rootfs/default.nix                            | 16 +++-
 host/rootfs/file-list.mk                           |  7 ++
 host/rootfs/image/etc/fstab                        |  1 +
 .../image/etc/sysupdate.d/50-verity.transfer       | 20 +++++
 host/rootfs/image/etc/sysupdate.d/60-root.transfer | 20 +++++
 .../image/etc/sysupdate.d/70-kernel.transfer       | 20 +++++
 .../image/etc/vm-sysupdate.d/50-verity.transfer    | 18 +++++
 .../image/etc/vm-sysupdate.d/60-root.transfer      | 18 +++++
 .../image/etc/vm-sysupdate.d/70-kernel.transfer    | 18 +++++
 host/rootfs/image/usr/bin/spectrum-update          | 92 ++++++++++++++++++++++
 host/rootfs/os-release.in                          | 15 ++++
 lib/config.default.nix                             |  2 +
 lib/fake-update-signing-key.gpg                    |  3 +
 vm/app/systemd-sysupdate/default.nix               | 26 ++++++
 vm/app/systemd-sysupdate/download-update           | 68 ++++++++++++++++
 16 files changed, 355 insertions(+), 6 deletions(-)

diff --git a/host/rootfs/Makefile b/host/rootfs/Makefile
index a6d9f23e9f5277b7c79a53105eb2dfe1bab1451e..74ff64019560aae6387df0e1b3409bc174251bdb 100644
--- a/host/rootfs/Makefile
+++ b/host/rootfs/Makefile
@@ -10,6 +10,7 @@ include file-list.mk
 ROOT_FS = build
 
 DIRS = \
+	boot \
 	dev \
 	etc/s6-linux-init/env \
 	etc/s6-linux-init/run-image/configs \
@@ -33,13 +34,15 @@ DIRS = \
 	etc/s6-linux-init/run-image/vm/by-id \
 	etc/s6-linux-init/run-image/vm/by-name \
 	ext \
+	home \
 	proc \
 	run \
-	sys
+	sys \
+	tmp
 
 FIFOS = etc/s6-linux-init/run-image/service/s6-svscan-log/fifo
 
-BUILD_FILES = build/etc/s6-rc
+BUILD_FILES = build/etc/s6-rc build/etc/os-release build/etc/update-url
 
 # This rule produces three files but Make only (portably)
 # supports one output per rule.  Instead of resorting to temporary
@@ -59,12 +62,22 @@ $(ROOT_FS_IMAGE): ../../scripts/make-erofs.sh $(PACKAGES_FILE) $(FILES) $(BUILD_
 	mkdir -p $(ROOT_FS) && \
 	{ \
 	    cat $(PACKAGES_FILE) ;\
+	    printf '%s\n%s\n' "$$UPDATE_SIGNING_KEY" /etc/systemd/import-pubring.gpg; \
 	    for file in $(FILES) $(LINKS); do printf '%s\n%s\n' $$file "$${file#image/}"; done ;\
 	    for file in $(BUILD_FILES); do printf '%s\n%s\n' $$file $${file#build/}; done ;\
 	    printf 'build/empty\n%s\n' $(DIRS) ;\
 	    printf 'build/fifo\n%s\n' $(FIFOS) ;\
 	} | ../../scripts/make-erofs.sh $@
 
+build/etc/update-url:
+	mkdir -p build/etc
+	# might have metacharacters, so avoid interpolation
+	printf %s\\n "$${UPDATE_URL:?'update URL empty or missing'}" > build/etc/update-url
+
+build/etc/os-release:
+	mkdir -p build/etc
+	sed 's/@VERSION@/$(VERSION)/g' < os-release.in > build/etc/os-release
+
 build/fifo:
 	mkdir -p build
 	mkfifo -m 0600 $@
diff --git a/host/rootfs/default.nix b/host/rootfs/default.nix
index 16a151971715f9a9d987dc92a1d06eb169de1144..8b62c78510fd4e41c2cd1e5075cc8fafc08fa415 100644
--- a/host/rootfs/default.nix
+++ b/host/rootfs/default.nix
@@ -13,6 +13,7 @@ pkgsStatic.callPackage (
 , busybox, cloud-hypervisor, cryptsetup, dbus, execline, inkscape
 , iproute2, inotify-tools, jq, mdevd, s6, s6-linux-init, socat
 , util-linuxMinimal, virtiofsd, xorg, xdg-desktop-portal-spectrum-host
+, btrfs-progs
 }:
 
 let
@@ -33,8 +34,8 @@ let
   foot = pkgsGui.foot.override { allowPgo = false; };
 
   packages = [
-    cloud-hypervisor cryptsetup dbus execline inotify-tools iproute2
-    jq mdevd s6 s6-linux-init s6-rc socat spectrum-host-tools
+    btrfs-progs cloud-hypervisor cryptsetup dbus execline inotify-tools
+    iproute2 jq mdevd s6 s6-linux-init s6-rc socat spectrum-host-tools
     util-linuxMinimal virtiofsd xdg-desktop-portal-spectrum-host
 
     (busybox.override {
@@ -43,7 +44,7 @@ let
     })
 
   # Take kmod from pkgsGui since we use pkgsGui.kmod.lib below anyway.
-  ] ++ (with pkgsGui; [ cosmic-files crosvm foot fuse3 kmod systemd ]);
+  ] ++ (with pkgsGui; [ cosmic-files crosvm foot fuse3 kmod ]);
 
   nixosAllHardware = nixos ({ modulesPath, ... }: {
     imports = [ (modulesPath + "/profiles/all-hardware.nix") ];
@@ -64,17 +65,19 @@ let
   # https://inbox.vuxu.org/musl/20251017-dlopen-use-rpath-of-caller-dso-v1-1-46c69eda1473@iscas.ac.cn/
   usrPackages = [
     appvm kernel.modules firmware netvm
-  ] ++ (with pkgsGui; [ dejavu_fonts kmod.lib mesa westonLite ]);
+  ] ++ (with pkgsGui; [ dejavu_fonts kmod.lib mesa westonLite systemd ]);
 
   appvms = {
     appvm-firefox = callSpectrumPackage ../../vm/app/firefox.nix {};
     appvm-foot = callSpectrumPackage ../../vm/app/foot.nix {};
     appvm-gnome-text-editor = callSpectrumPackage ../../vm/app/gnome-text-editor.nix {};
+    appvm-systemd-sysupdate = callSpectrumPackage ../../vm/app/systemd-sysupdate {};
   };
 
   packagesSysroot = runCommand "packages-sysroot" {
     depsBuildBuild = [ inkscape ];
     nativeBuildInputs = [ xorg.lndir ];
+    src = builtins.path { name = "os-release"; path = ./os-release.in; };
   } ''
     mkdir -p $out/usr/bin $out/usr/share/dbus-1/services \
       $out/usr/share/icons/hicolor/20x20/apps
@@ -125,6 +128,11 @@ stdenvNoCC.mkDerivation {
       printf "%s\n/\n" ${packagesSysroot} >$out
       sed p ${writeClosure [ packagesSysroot] } >>$out
     '';
+    UPDATE_SIGNING_KEY = builtins.path {
+      name = "signing-key";
+      path = config.updateSigningKey;
+    };
+    UPDATE_URL = config.updateUrl;
     VERSION = config.version;
   };
 
diff --git a/host/rootfs/file-list.mk b/host/rootfs/file-list.mk
index 7625c54c0ae74ded2f3c9f4a860f21491f6e20a7..c08ecf0ab94a857fafc9ccdc9ea604885a57954f 100644
--- a/host/rootfs/file-list.mk
+++ b/host/rootfs/file-list.mk
@@ -37,13 +37,20 @@ FILES = \
 	image/etc/s6-linux-init/run-image/service/vmm/run \
 	image/etc/s6-linux-init/run-image/service/vmm/template/notification-fd \
 	image/etc/s6-linux-init/scripts/rc.init \
+	image/etc/sysupdate.d/50-verity.transfer \
+	image/etc/sysupdate.d/60-root.transfer \
+	image/etc/sysupdate.d/70-kernel.transfer \
 	image/etc/udev/rules.d/99-spectrum.rules \
+	image/etc/vm-sysupdate.d/50-verity.transfer \
+	image/etc/vm-sysupdate.d/60-root.transfer \
+	image/etc/vm-sysupdate.d/70-kernel.transfer \
 	image/etc/xdg/weston/autolaunch \
 	image/etc/xdg/weston/weston.ini \
 	image/usr/bin/assign-devices \
 	image/usr/bin/create-vm-dependencies \
 	image/usr/bin/run-appimage \
 	image/usr/bin/run-vmm \
+	image/usr/bin/spectrum-update \
 	image/usr/bin/vm-console \
 	image/usr/bin/vm-import \
 	image/usr/bin/vm-start \
diff --git a/host/rootfs/image/etc/fstab b/host/rootfs/image/etc/fstab
index 5dc9b2a3c4dff62ee49b2d827f53b45b7781a60f..6230d910a23339925fea0f2ffbc2baa5241ce3f2 100644
--- a/host/rootfs/image/etc/fstab
+++ b/host/rootfs/image/etc/fstab
@@ -4,3 +4,4 @@ proc	/proc		proc	defaults	0	0
 devpts	/dev/pts	devpts	gid=5,mode=620	0	0
 tmpfs	/dev/shm	tmpfs	defaults	0	0
 sysfs	/sys		sysfs	defaults	0	0
+tmpfs	/tmp		tmpfs	defaults	0	0
diff --git a/host/rootfs/image/etc/sysupdate.d/50-verity.transfer b/host/rootfs/image/etc/sysupdate.d/50-verity.transfer
new file mode 100644
index 0000000000000000000000000000000000000000..9cd64b58ae55d55d378d99f5701f1ecef867e436
--- /dev/null
+++ b/host/rootfs/image/etc/sysupdate.d/50-verity.transfer
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+# Uses example code from systemd man pages which is under MIT-0
+# (no attribution required).
+[Transfer]
+ProtectVersion=%A
+
+[Source]
+Type=url-file
+Path=file:///run/updater
+MatchPattern=Spectrum_@v_@u.verity
+
+[Target]
+Type=partition
+Path=auto
+MatchPattern=Spectrum_@v.verity
+MatchPartitionType=root-verity
+PartitionFlags=0
+ReadOnly=1
diff --git a/host/rootfs/image/etc/sysupdate.d/60-root.transfer b/host/rootfs/image/etc/sysupdate.d/60-root.transfer
new file mode 100644
index 0000000000000000000000000000000000000000..cd12d2bd2b4ecd9bb5c7d26cc7c27a4bdb74cac8
--- /dev/null
+++ b/host/rootfs/image/etc/sysupdate.d/60-root.transfer
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+# Uses example code from systemd man pages which is under MIT-0
+# (no attribution required).
+[Transfer]
+ProtectVersion=%A
+
+[Source]
+Type=url-file
+Path=file:///run/updater
+MatchPattern=Spectrum_@v_@u.root
+
+[Target]
+Type=partition
+Path=auto
+MatchPattern=Spectrum_@v
+MatchPartitionType=root
+PartitionFlags=0
+ReadOnly=1
diff --git a/host/rootfs/image/etc/sysupdate.d/70-kernel.transfer b/host/rootfs/image/etc/sysupdate.d/70-kernel.transfer
new file mode 100644
index 0000000000000000000000000000000000000000..e4190587a6bb127cb7315f38d59e48cf279318a4
--- /dev/null
+++ b/host/rootfs/image/etc/sysupdate.d/70-kernel.transfer
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+# Uses example code from systemd man pages which is under MIT-0
+# (no attribution required).
+[Transfer]
+ProtectVersion=%A
+
+[Source]
+Type=url-file
+Path=file:///run/updater
+MatchPattern=Spectrum_@v.efi
+
+[Target]
+Type=regular-file
+Path=/EFI/Linux
+PathRelativeTo=boot
+MatchPattern=Spectrum_@v.efi
+Mode=0644
+InstancesMax=2
diff --git a/host/rootfs/image/etc/vm-sysupdate.d/50-verity.transfer b/host/rootfs/image/etc/vm-sysupdate.d/50-verity.transfer
new file mode 100644
index 0000000000000000000000000000000000000000..ab4997c83605e3820a22b0b2178dcd76dfcf787e
--- /dev/null
+++ b/host/rootfs/image/etc/vm-sysupdate.d/50-verity.transfer
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+# Uses example code from systemd man pages which is under MIT-0
+# (no attribution required).
+[Transfer]
+Verify=yes
+
+[Source]
+Type=url-file
+Path=@UPDATE_URL@
+MatchPattern=Spectrum_@v_@u.verity
+
+[Target]
+Type=regular-file
+Path=/run/virtiofs/virtiofs0/updates
+MatchPattern=Spectrum_@v_@u.verity
+Mode=0644
diff --git a/host/rootfs/image/etc/vm-sysupdate.d/60-root.transfer b/host/rootfs/image/etc/vm-sysupdate.d/60-root.transfer
new file mode 100644
index 0000000000000000000000000000000000000000..8a3175684f1697e0eca443eb0a1a97176e4f66d4
--- /dev/null
+++ b/host/rootfs/image/etc/vm-sysupdate.d/60-root.transfer
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+# Uses example code from systemd man pages which is under MIT-0
+# (no attribution required).
+[Transfer]
+Verify=yes
+
+[Source]
+Type=url-file
+Path=@UPDATE_URL@
+MatchPattern=Spectrum_@v_@u.root
+
+[Target]
+Type=regular-file
+Path=/run/virtiofs/virtiofs0/updates
+MatchPattern=Spectrum_@v_@u.root
+Mode=0644
diff --git a/host/rootfs/image/etc/vm-sysupdate.d/70-kernel.transfer b/host/rootfs/image/etc/vm-sysupdate.d/70-kernel.transfer
new file mode 100644
index 0000000000000000000000000000000000000000..cb181239d71c5a6d0a5b3652d5534a23eda64183
--- /dev/null
+++ b/host/rootfs/image/etc/vm-sysupdate.d/70-kernel.transfer
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+# Uses example code from systemd man pages which is under MIT-0
+# (no attribution required).
+[Transfer]
+Verify=yes
+
+[Source]
+Type=url-file
+Path=@UPDATE_URL@
+MatchPattern=Spectrum_@v.efi
+
+[Target]
+Type=regular-file
+Path=/run/virtiofs/virtiofs0/updates
+MatchPattern=Spectrum_@v.efi
+Mode=0644
diff --git a/host/rootfs/image/usr/bin/spectrum-update b/host/rootfs/image/usr/bin/spectrum-update
new file mode 100755
index 0000000000000000000000000000000000000000..613b43570d0538fce20296ccb1de2a6364e0df55
--- /dev/null
+++ b/host/rootfs/image/usr/bin/spectrum-update
@@ -0,0 +1,92 @@
+#!/bin/execlineb -WS1
+# SPDX-License-Identifier: EUPL-1.2+
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+if { mkdir -p -m 0700 /run/updater }
+
+# Take a global lock to avoid races.
+s6-setlock /run/update-lock
+
+foreground { redirfd -w 2 /dev/null rmdir -- $1 }
+if { umask 0077 mkdir -p -- $1 }
+cd $1
+foreground {
+  # If this exists already that is okay.
+  foreground { redirfd -w 2 /dev/null btrfs subvolume create -- shared }
+
+  # Delete any stale temporary files.  Delete any existing signature
+  # files.  If the VM is still running (it should not be), the VM might
+  # have write access to the directory.  However, updates-dir-check is
+  # safe against that.
+  if { updates-dir-check cleanup shared }
+
+  if {
+    foreground {
+      # TODO: suppress only "subvolume does not exist" errors.
+      redirfd -w 2 /dev/null
+      btrfs subvolume delete snapshot
+    }
+    rm -f snapshot
+  }
+
+  backtick -E update_vm_id {
+    backtick -E id_path { readlink /run/vm/by-name/sys.appvm-systemd-sysupdate }
+    basename -- $id_path
+  }
+
+  # $fsdir is read-only to the guest, but read-write to the host.
+  # Directories bind-mounted into it are read-write to the guest.
+  # See etc/s6-linux-init/run-image/service/vhost-user-fs/template/run
+  # for details.
+
+  # Set up /etc with what the VM needs.  The VM will overlay this
+  # on its own /etc.
+  #
+  # In the future, this should use a bind mount instead of copying
+  # into a tmpfs.  However, this would significantly complicate the
+  # cleanup code.  Deleting fs/etc would require undoing the bind
+  # mounts instead of rm -rf.  Once this code is in a separate mount
+  # namespace, the copies should be replaced by bind mounts.
+  if {
+    if { rm -rf -- /run/vm/by-id/${update_vm_id}/fs/etc }
+    umask 022
+    if { mkdir -p -- /run/vm/by-id/${update_vm_id}/fs/updates /run/vm/by-id/${update_vm_id}/fs/etc/systemd }
+    if { cp -R -- /etc/vm-sysupdate.d /etc/update-url /run/vm/by-id/${update_vm_id}/fs/etc }
+    cp -- /etc/systemd/import-pubring.gpg /run/vm/by-id/${update_vm_id}/fs/etc/systemd
+  }
+
+  # If the directory is already mounted, unmount it.  This prevents a
+  # confusing error from mount.
+  foreground { redirfd -w 2 /dev/null umount -- /run/vm/by-id/${update_vm_id}/fs/updates }
+
+  # Share the update directory with the VM.
+  if { mount --bind -- shared /run/vm/by-id/${update_vm_id}/fs/updates }
+
+  # Start the update VM.
+  if { vm-start $update_vm_id }
+
+  # Wait for the VM to exit.
+  # TODO: This is racy.  If the update finishes before this code runs,
+  # the s6-svwait call will fail.
+  if { s6-svwait -D /run/service/vmm/instance/${update_vm_id} }
+
+  # Remove the bind mount.
+  if { umount -- /run/vm/by-id/${update_vm_id}/fs/updates }
+
+  # Ensure that the VM cannot change the directory
+  # while systemd-sysupdate is using it.
+  if { btrfs subvolume snapshot -- shared snapshot }
+
+  # Validate the update directory.  Delete any stale temporary files.
+  # Check that a signature file was downloaded.
+  if { updates-dir-check check snapshot }
+
+  unshare --mount
+  if { mount --bind -o ro -- snapshot /run/updater }
+
+  /usr/lib/systemd/systemd-sysupdate update
+}
+importas -i sysupdate_exit_status ?
+# Clean up.
+foreground { btrfs subvolume delete -- snapshot }
+exit $sysupdate_exit_status
diff --git a/host/rootfs/os-release.in b/host/rootfs/os-release.in
new file mode 100644
index 0000000000000000000000000000000000000000..d6e699e82f87dcb1c4656ac19d4e9986282f14a5
--- /dev/null
+++ b/host/rootfs/os-release.in
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+NAME="Spectrum OS"
+ID=spectrum
+PRETTY_NAME="Spectrum @VERSION@"
+VERSION=@VERSION@
+VERSION_ID=@VERSION@
+IMAGE_ID=spectrum-root
+IMAGE_VERSION=@VERSION@
+RELEASE_TYPE=development
+HOME_URL="https://spectrum-os.org"
+BUG_REPORT_URL="mailto:discuss@spectrum-os.org"
+ANSI_COLOR="1;34"
+VENDOR_NAME=Spectrum
+VENDOR_URL="https://spectrum-os.org"
diff --git a/lib/config.default.nix b/lib/config.default.nix
index 489c231490a8b66aa01f50053b25646060f7f963..f6b70fa5e8431bef79222c10c79e8015f7fe65be 100644
--- a/lib/config.default.nix
+++ b/lib/config.default.nix
@@ -5,4 +5,6 @@
   pkgsFun = import ./nixpkgs.default.nix;
   pkgsArgs = {};
   version = "0.0.0";
+  updateUrl = "https://your-spectrum-os-update-server.invalid/download-directory";
+  updateSigningKey = ./fake-update-signing-key.gpg;
 }
diff --git a/lib/fake-update-signing-key.gpg b/lib/fake-update-signing-key.gpg
new file mode 100644
index 0000000000000000000000000000000000000000..12e18f4c7c740e31692e1f1975282fa72ac1f2e3
--- /dev/null
+++ b/lib/fake-update-signing-key.gpg
@@ -0,0 +1,3 @@
+SPDX-License-Identifier: CC0-1.0
+SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+NOT A VALID KEY - UPDATES WILL NOT WORK
diff --git a/vm/app/systemd-sysupdate/default.nix b/vm/app/systemd-sysupdate/default.nix
new file mode 100644
index 0000000000000000000000000000000000000000..69be0bab500ea2ea6cb3b6d71edbf1a3e7bddbba
--- /dev/null
+++ b/vm/app/systemd-sysupdate/default.nix
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2023 Alyssa Ross <hi@alyssa.is>
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+import ../../../lib/call-package.nix (
+{ callSpectrumPackage, curl, lib, src
+, runCommand, systemd, writeScript
+}:
+
+let
+  downloadUpdate = builtins.path {
+    name = "download-update";
+    path = ./download-update;
+  };
+in
+
+callSpectrumPackage ../../make-vm.nix {} {
+  providers.net = [ "sys.netvm" ];
+  type = "nix";
+  run = writeScript "run-script" ''
+#!/usr/bin/env -S execlineb -WS0
+export CURL_PATH ${curl}/bin/curl
+export SYSTEMD_SYSUPDATE_PATH ${systemd}/lib/systemd/systemd-sysupdate
+${downloadUpdate}
+'';
+}) (_: {})
diff --git a/vm/app/systemd-sysupdate/download-update b/vm/app/systemd-sysupdate/download-update
new file mode 100755
index 0000000000000000000000000000000000000000..eada41c6c8ad5edcedd9f4d76b76492e0b8be826
--- /dev/null
+++ b/vm/app/systemd-sysupdate/download-update
@@ -0,0 +1,68 @@
+#!/usr/bin/env -S execlineb -WS0
+# SPDX-License-Identifier: EUPL-1.2+
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+export LC_ALL C
+export LANGUAGE C
+if { mount -toverlay -olowerdir=/run/virtiofs/virtiofs0/etc:/etc -- overlay /etc }
+backtick tmpdir { mktemp -d /tmp/sysupdate-XXXXXX }
+# Not a useless use of cat: if there are NUL bytes in the URL
+# busybox's awk might misbehave.
+backtick update_url { cat /etc/update-url }
+if {
+  backtick sed_rhs {
+    # Use awk to both validate the URL and to escape sed metacharacters.
+    # Reject URLs with control characters, query parameters, or fragments.
+    # They *cannot* work and so are rejected to produce better error messages.
+    #
+    # curl rejects control characters with "Malformed input to a URL function".
+    # Fragment specifiers ("#") and query parameters ("?") break concatenating
+    # /SHA256SUMS and /SHA256SUMS.sha256.asc onto the update URL.  Also, it is
+    # simpler to reject update URLs that contain whitespace than to try to
+    # escape them.
+    #
+    # Backslash needs to be escaped once for systemd-sysupdate and again for sed.
+    # Ampersand needs to be escaped once for sed.
+    awk "BEGIN {
+      update_url = ENVIRON[\"update_url\"];
+      if (update_url ~ /^[^\\001-\\040?#\\x7F]+$/) {
+        # Use & to avoid extra escaping (16 or 32 backslashes!)
+        # and a divergence between POSIX and GNU awk.
+        gsub(/\\\\/, \"&&&&\", update_url);
+        gsub(/&/, \"\\\\\\\\&\", update_url);
+        print update_url;
+        exit 0;
+      } else {
+        print ARGV[2] > \"/dev/stderr\";
+        exit 100;
+      }
+    }" -- $3
+    "Bad update URL from host: control characters, whitespace, query parameters, and fragment specifiers not allowed"
+  }
+  elglob -w -0 transfer_file_ /etc/vm-sysupdate.d/*.transfer
+  forx -E transfer_file { $transfer_file_ }
+  backtick target_basename {
+    basename -- $transfer_file
+  }
+  multisubstitute {
+    importas -iuS sed_rhs
+    importas -iuS target_basename
+    importas -iuS tmpdir
+    define sed_input $transfer_file
+  }
+  redirfd -w 1 ${tmpdir}/${target_basename}
+  sed -E -- "s#@UPDATE_URL@#${sed_rhs}#g" $sed_input
+}
+multisubstitute {
+  importas -iuS update_url
+  importas -iuS CURL_PATH
+  importas -iuS SYSTEMD_SYSUPDATE_PATH
+  importas -iuS tmpdir
+}
+if { $SYSTEMD_SYSUPDATE_PATH --definitions=${tmpdir} update }
+# [ and ] are allowed in update URLs so that IPv6 addresses work, but
+# they cause globbing in the curl command-line tool by default.  Use --globoff
+# to disable this feature.
+if { $CURL_PATH -L --proto-redir =http,https --globoff
+     -o /run/virtiofs/virtiofs0/updates/SHA256SUMS -- ${update_url}/SHA256SUMS }
+$CURL_PATH -L --proto-redir =http,https --globoff
+     -o /run/virtiofs/virtiofs0/updates/SHA256SUMS.sha256.asc -- ${update_url}/SHA256SUMS.sha256.asc

-- 
2.52.0


  parent reply	other threads:[~2025-11-26 19:37 UTC|newest]

Thread overview: 177+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-29 10:12 [PATCH 0/7] System updates based on systemd-sysupdate Demi Marie Obenour
2025-10-29 10:12 ` [PATCH 1/7] host/rootfs: Use full util-linux and systemd Demi Marie Obenour
2025-10-29 11:36   ` Alyssa Ross
2025-11-01  3:25     ` Demi Marie Obenour
2025-11-01 12:13       ` Alyssa Ross
2025-11-06  9:15         ` Demi Marie Obenour
2025-10-29 10:12 ` [PATCH 2/7] release/combined: Compress installation image Demi Marie Obenour
2025-10-29 11:50   ` Alyssa Ross
2025-10-29 16:51     ` Alyssa Ross
2025-11-01 22:15       ` Demi Marie Obenour
2025-11-02  0:18         ` Demi Marie Obenour
2025-11-02 12:05           ` Alyssa Ross
2025-11-02 14:42             ` Alyssa Ross
2025-11-02 19:38             ` Demi Marie Obenour
2025-10-29 10:12 ` [PATCH 3/7] tools: Add directory checker for updates Demi Marie Obenour
2025-10-29 12:01   ` Alyssa Ross
2025-10-31 20:31     ` Demi Marie Obenour
2025-11-01 12:17       ` Alyssa Ross
2025-11-01 14:09         ` Alyssa Ross
2025-11-01 18:36         ` Demi Marie Obenour
2025-11-02 12:18           ` Alyssa Ross
2025-11-02 12:43             ` Alyssa Ross
2025-11-02 19:34               ` Demi Marie Obenour
2025-11-04 15:26                 ` Alyssa Ross
2025-11-02 19:21             ` Demi Marie Obenour
2025-11-04 15:27               ` Alyssa Ross
2025-11-04 22:56                 ` Demi Marie Obenour
2025-11-06 10:15                   ` Alyssa Ross
2025-10-29 10:12 ` [PATCH 4/7] Adjust partition layout to support updates Demi Marie Obenour
2025-10-29 15:49   ` Alyssa Ross
2025-10-29 10:12 ` [PATCH 5/7] release: add install step Demi Marie Obenour
2025-10-29 12:20   ` Alyssa Ross
2025-10-29 10:12 ` [PATCH 6/7] Factor out dm-verity build rules Demi Marie Obenour
2025-10-29 12:22   ` Alyssa Ross
2025-10-31  6:39     ` Demi Marie Obenour
2025-10-29 10:12 ` [PATCH 7/7] Support updates via systemd-sysupdate Demi Marie Obenour
2025-10-29 15:48   ` Alyssa Ross
2025-11-12 22:14 ` [PATCH v2 0/8] System updates based on systemd-sysupdate Demi Marie Obenour
2025-11-12 22:14   ` [PATCH v2 1/8] host/rootfs: Install all programs from util-linuxMinimal Demi Marie Obenour
2025-11-13 12:35     ` Alyssa Ross
2025-11-12 22:14   ` [PATCH v2 2/8] host/rootfs: Install systemd-pull Demi Marie Obenour
2025-11-13 15:22     ` Alyssa Ross
2025-11-13 23:46       ` Demi Marie Obenour
2025-11-14 11:59         ` Alyssa Ross
2025-11-12 22:14   ` [PATCH v2 3/8] tools: Add directory checker for updates Demi Marie Obenour
2025-11-13 13:21     ` Alyssa Ross
2025-11-13 17:53       ` Demi Marie Obenour
2025-11-13 18:01         ` Alyssa Ross
2025-11-13 18:03           ` Demi Marie Obenour
2025-11-14 13:08             ` Alyssa Ross
2025-11-14 18:37               ` Demi Marie Obenour
2025-11-15 15:20                 ` Alyssa Ross
2025-11-12 22:14   ` [PATCH v2 4/8] Adjust partition layout to support updates Demi Marie Obenour
2025-11-13 16:00     ` Alyssa Ross
2025-11-12 22:14   ` [PATCH v2 5/8] release: Create directory with system update Demi Marie Obenour
2025-11-13 16:04     ` Alyssa Ross
2025-11-13 18:23       ` Demi Marie Obenour
2025-11-13 19:09         ` Alyssa Ross
2025-11-12 22:15   ` [PATCH v2 6/8] Support updates via systemd-sysupdate Demi Marie Obenour
2025-11-13 16:44     ` Alyssa Ross
2025-11-13 20:25       ` Demi Marie Obenour
2025-11-14 12:14         ` Alyssa Ross
2025-11-14 23:16           ` Demi Marie Obenour
2025-11-20 14:56             ` Alyssa Ross
2025-11-20 19:42               ` Demi Marie Obenour
2025-11-12 22:15   ` [PATCH v2 7/8] Documentation: Update support Demi Marie Obenour
2025-11-13 16:49     ` Alyssa Ross
2025-11-13 22:24       ` Demi Marie Obenour
2025-11-14 12:16         ` Alyssa Ross
2025-11-12 22:15   ` [PATCH v2 8/8] lib/config.nix: Validate configuration parameters Demi Marie Obenour
2025-11-13 17:16     ` Alyssa Ross
2025-11-19  8:18   ` [PATCH v3 00/14] System updates based on systemd-sysupdate Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 01/14] host/rootfs: Install all programs from util-linuxMinimal Demi Marie Obenour
2025-11-19 14:14       ` Alyssa Ross
2025-11-20  0:12         ` Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 02/14] host/rootfs: Install systemd-pull Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 03/14] tools: Add directory checker for updates Demi Marie Obenour
2025-11-19 14:45       ` Alyssa Ross
2025-11-19 23:58         ` Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 04/14] scripts: port make-gpt.sh to bash Demi Marie Obenour
2025-11-20 10:28       ` Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 05/14] scripts/make-gpt.sh: Allow specifying partition size Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 06/14] Support generating multiple partition UUIDs Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 07/14] scripts: Use shell expansion to get partition path Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 08/14] Use OS version to set partition labels and UKI name Demi Marie Obenour
2025-11-20 12:11       ` Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 09/14] release: Compress installation images and remove live image Demi Marie Obenour
2025-11-20 12:14       ` Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 10/14] Add B partitions to installation images Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 11/14] release: Create directory with system update Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 12/14] Support updates via systemd-sysupdate Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 13/14] Documentation: Update support Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 14/14] Validate configuration parameters Demi Marie Obenour
2025-11-22  1:23     ` [PATCH v4 00/14] System updates based on systemd-sysupdate Demi Marie Obenour
2025-11-22  1:23       ` [PATCH v4 01/14] host/rootfs: Install all programs from util-linuxMinimal Demi Marie Obenour
2025-11-25 11:56         ` Alyssa Ross
2025-11-22  1:23       ` [PATCH v4 02/14] host/rootfs: Install systemd-pull Demi Marie Obenour
2025-11-25  7:36         ` Alyssa Ross
2025-11-22  1:23       ` [PATCH v4 03/14] tools: Add directory checker for updates Demi Marie Obenour
2025-11-22  1:23       ` [PATCH v4 04/14] scripts: port make-gpt.sh to bash Demi Marie Obenour
2025-11-22  1:23       ` [PATCH v4 05/14] scripts/make-gpt.sh: Allow specifying partition size Demi Marie Obenour
2025-11-22  1:23       ` [PATCH v4 06/14] Support generating multiple partition UUIDs Demi Marie Obenour
2025-11-25 13:02         ` Alyssa Ross
2025-11-26 18:26           ` Demi Marie Obenour
2025-11-22  1:23       ` [PATCH v4 07/14] scripts: Use shell expansion to get partition path Demi Marie Obenour
2025-11-22  1:23       ` [PATCH v4 08/14] release: Compress installation images and remove live image Demi Marie Obenour
2025-11-25 13:19         ` Alyssa Ross
2025-11-25 22:38           ` Demi Marie Obenour
2025-11-28 11:09             ` Alyssa Ross
2025-11-28 19:45               ` Demi Marie Obenour
2025-11-22  1:23       ` [PATCH v4 09/14] Use OS version to set partition labels and UKI name Demi Marie Obenour
2025-11-25 14:11         ` Alyssa Ross
2025-11-22  1:23       ` [PATCH v4 10/14] Add B partitions to installation images Demi Marie Obenour
2025-11-25 16:31         ` Alyssa Ross
2025-11-22  1:23       ` [PATCH v4 11/14] release: Create directory with system update Demi Marie Obenour
2025-11-25 16:50         ` Alyssa Ross
2025-11-22  1:23       ` [PATCH v4 12/14] Support updates via systemd-sysupdate Demi Marie Obenour
2025-11-25 17:54         ` Alyssa Ross
2025-11-22  1:23       ` [PATCH v4 13/14] Documentation: Update support Demi Marie Obenour
2025-11-25 18:00         ` Alyssa Ross
2025-11-22  1:23       ` [PATCH v4 14/14] Validate configuration parameters Demi Marie Obenour
2025-11-25 18:06         ` Alyssa Ross
2025-11-25 12:22       ` [PATCH v4 00/14] System updates based on systemd-sysupdate Alyssa Ross
2025-11-26 19:40       ` [PATCH v5 00/13] " Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 01/13] tools: Add directory checker for updates Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 02/13] scripts: port make-gpt.sh to bash Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 03/13] scripts/make-gpt.sh: Allow specifying partition size Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 04/13] Port scripts/format-uuid.sh to awk Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 05/13] Use set and a command substitution to set UUID variables Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 06/13] scripts: Use shell expansion to get partition path Demi Marie Obenour
2025-11-28 11:20           ` Alyssa Ross
2025-11-26 19:40         ` [PATCH v5 07/13] release: Compress installation images and remove live image Demi Marie Obenour
2025-11-28 11:21           ` Alyssa Ross
2025-11-26 19:40         ` [PATCH v5 08/13] Use OS version to set partition labels and UKI name Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 09/13] Add B partitions to installation images Demi Marie Obenour
2025-11-28 11:23           ` Alyssa Ross
2025-11-26 19:40         ` [PATCH v5 10/13] release: Create directory with system update Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 11/13] Support updates via systemd-sysupdate Demi Marie Obenour
2025-11-28 13:47           ` Alyssa Ross
2025-11-28 20:27             ` Demi Marie Obenour
2025-11-28 20:41               ` Alyssa Ross
2025-11-28 20:44                 ` Demi Marie Obenour
2025-11-28 21:08                   ` Alyssa Ross
2025-11-28 21:28                     ` Demi Marie Obenour
2025-11-28 21:30                       ` Alyssa Ross
2025-11-26 19:40         ` [PATCH v5 12/13] Documentation: Update support Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 13/13] Validate configuration parameters Demi Marie Obenour
2025-11-29  9:49         ` [PATCH v6 0/8] System updates based on systemd-sysupdate Demi Marie Obenour
2025-11-29  9:49           ` [PATCH v6 1/8] tools: Add directory checker for updates Demi Marie Obenour
2025-11-29 11:16             ` Alyssa Ross
2025-11-29  9:49           ` [PATCH v6 2/8] release: Compress installation images and remove live image Demi Marie Obenour
2025-11-29 11:16             ` Alyssa Ross
2025-11-29  9:50           ` [PATCH v6 3/8] Use OS version to set partition labels and UKI name Demi Marie Obenour
2025-11-29 11:16             ` Alyssa Ross
2025-11-29  9:50           ` [PATCH v6 4/8] Add B partitions to installation images Demi Marie Obenour
2025-11-29 11:16             ` Alyssa Ross
2025-11-29  9:50           ` [PATCH v6 5/8] release: Create directory with system update Demi Marie Obenour
2025-11-29 11:16             ` Alyssa Ross
2025-11-29  9:50           ` [PATCH v6 6/8] Support updates via systemd-sysupdate Demi Marie Obenour
2025-11-29 11:16             ` Alyssa Ross
2025-11-29  9:50           ` [PATCH v6 7/8] Documentation: Update support Demi Marie Obenour
2025-11-30 21:46             ` Alyssa Ross
2025-11-29  9:50           ` [PATCH v6 8/8] Validate configuration parameters Demi Marie Obenour
2025-11-26 19:33     ` [PATCH v4 00/13] System updates based on systemd-sysupdate Demi Marie Obenour
2025-11-26 19:33       ` [PATCH v4 01/13] tools: Add directory checker for updates Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 02/13] scripts: port make-gpt.sh to bash Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 03/13] scripts/make-gpt.sh: Allow specifying partition size Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 04/13] Port scripts/format-uuid.sh to awk Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 05/13] Use set and a command substitution to set UUID variables Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 06/13] scripts: Use shell expansion to get partition path Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 07/13] release: Compress installation images and remove live image Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 08/13] Use OS version to set partition labels and UKI name Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 09/13] Add B partitions to installation images Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 10/13] release: Create directory with system update Demi Marie Obenour
2025-11-26 19:34       ` Demi Marie Obenour [this message]
2025-11-26 19:34       ` [PATCH v4 12/13] Documentation: Update support Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 13/13] Validate configuration parameters Demi Marie Obenour

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251126-updates-v4-11-40c438d2dcaf@gmail.com \
    --to=demiobenour@gmail.com \
    --cc=devel@spectrum-os.org \
    --cc=hi@alyssa.is \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://spectrum-os.org/git/crosvm
	https://spectrum-os.org/git/doc
	https://spectrum-os.org/git/mktuntap
	https://spectrum-os.org/git/nixpkgs
	https://spectrum-os.org/git/spectrum
	https://spectrum-os.org/git/ucspi-vsock
	https://spectrum-os.org/git/www

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).