From 437f05da94a4e036c817a490c794c043f799e0bd Mon Sep 17 00:00:00 2001 From: Cappy Ishihara Date: Sat, 31 May 2025 22:56:35 +0700 Subject: [PATCH 01/17] Initial repart configs for factory reset --- templates/wholedisk/20-efi.conf | 1 + templates/wholedisk/45-boot.conf | 1 + templates/wholedisk/47-recovery.conf | 14 ++++++++++++++ templates/wholedisk/50-root.conf | 1 + 4 files changed, 17 insertions(+) create mode 100644 templates/wholedisk/47-recovery.conf diff --git a/templates/wholedisk/20-efi.conf b/templates/wholedisk/20-efi.conf index b5dae29d..13cbb0d5 100644 --- a/templates/wholedisk/20-efi.conf +++ b/templates/wholedisk/20-efi.conf @@ -6,3 +6,4 @@ SizeMaxBytes=512M MountPoint=/boot/efi:umask=0077,shortname=winnt # This path is actually relative; see man repart.d at --copy-source option CopyFiles=/boot/efi/:/ +FactoryReset=true \ No newline at end of file diff --git a/templates/wholedisk/45-boot.conf b/templates/wholedisk/45-boot.conf index 82109b16..1cc89821 100644 --- a/templates/wholedisk/45-boot.conf +++ b/templates/wholedisk/45-boot.conf @@ -8,3 +8,4 @@ MountPoint=/boot/:defaults CopyFiles=/boot:/ ExcludeFiles=/boot/efi Type=xbootldr +FactoryReset=true \ No newline at end of file diff --git a/templates/wholedisk/47-recovery.conf b/templates/wholedisk/47-recovery.conf new file mode 100644 index 00000000..eb064691 --- /dev/null +++ b/templates/wholedisk/47-recovery.conf @@ -0,0 +1,14 @@ +# This partition is an EROFS mirror of rootfs, used for special recovery purposes +[Partition] +Type=linux-generic +Format=erofs +CopyFiles=/:/ +Label=um_recovery +# Priority of 5000 ensures that it wouldn't be created if +# We don't have enough space +Priority=5000 +# This partition will be 8GB in size, for future-proofing and +# recovery image updates. +SizeMaxBytes=8G +SizeMinBytes=8GB +FactoryReset=false \ No newline at end of file diff --git a/templates/wholedisk/50-root.conf b/templates/wholedisk/50-root.conf index b4ec0682..eb3d9597 100644 --- a/templates/wholedisk/50-root.conf +++ b/templates/wholedisk/50-root.conf @@ -9,3 +9,4 @@ Format=btrfs CopyFiles=/:/ Subvolumes=/ /home CompressionLevel=1 +FactoryReset=true \ No newline at end of file From c11aa8a37384ac9146dc91c7d04535c947b2b05c Mon Sep 17 00:00:00 2001 From: Cappy Ishihara Date: Sat, 31 May 2025 23:14:29 +0700 Subject: [PATCH 02/17] fix typo --- templates/wholedisk/47-recovery.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/wholedisk/47-recovery.conf b/templates/wholedisk/47-recovery.conf index eb064691..33f23dce 100644 --- a/templates/wholedisk/47-recovery.conf +++ b/templates/wholedisk/47-recovery.conf @@ -10,5 +10,5 @@ Priority=5000 # This partition will be 8GB in size, for future-proofing and # recovery image updates. SizeMaxBytes=8G -SizeMinBytes=8GB +SizeMinBytes=8G FactoryReset=false \ No newline at end of file From aaa28a6e8fb82d5ad06d0ac1965feaba85398188 Mon Sep 17 00:00:00 2001 From: Cappy Ishihara Date: Sun, 1 Jun 2025 01:37:02 +0700 Subject: [PATCH 03/17] update config file --- templates/wholedisk/47-recovery.conf | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/templates/wholedisk/47-recovery.conf b/templates/wholedisk/47-recovery.conf index 33f23dce..1e831134 100644 --- a/templates/wholedisk/47-recovery.conf +++ b/templates/wholedisk/47-recovery.conf @@ -1,7 +1,8 @@ # This partition is an EROFS mirror of rootfs, used for special recovery purposes [Partition] -Type=linux-generic +Type=root Format=erofs +SizeMinBytes=4G CopyFiles=/:/ Label=um_recovery # Priority of 5000 ensures that it wouldn't be created if @@ -9,6 +10,6 @@ Label=um_recovery Priority=5000 # This partition will be 8GB in size, for future-proofing and # recovery image updates. -SizeMaxBytes=8G -SizeMinBytes=8G +PaddingMinBytes=2G +SizeMaxBytes=10G FactoryReset=false \ No newline at end of file From 5c3ffbdb1f068afcef78197a9ff4e38c29ec2f3a Mon Sep 17 00:00:00 2001 From: Cappy Ishihara Date: Sun, 1 Jun 2025 01:58:59 +0700 Subject: [PATCH 04/17] reserve partition --- templates/wholedisk/47-recovery.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/wholedisk/47-recovery.conf b/templates/wholedisk/47-recovery.conf index 1e831134..a6be84fd 100644 --- a/templates/wholedisk/47-recovery.conf +++ b/templates/wholedisk/47-recovery.conf @@ -1,6 +1,7 @@ # This partition is an EROFS mirror of rootfs, used for special recovery purposes [Partition] -Type=root +# Let's reserve this +Type=8DA63339-0007-60C0-C436-083AC8230908 Format=erofs SizeMinBytes=4G CopyFiles=/:/ From 4dfbe76669bad430533ce341ebcefdd095593214 Mon Sep 17 00:00:00 2001 From: Cappy Ishihara Date: Sun, 1 Jun 2025 04:18:24 +0700 Subject: [PATCH 05/17] add full priority to root --- templates/wholedisk/50-root.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/wholedisk/50-root.conf b/templates/wholedisk/50-root.conf index eb3d9597..1bf24fc0 100644 --- a/templates/wholedisk/50-root.conf +++ b/templates/wholedisk/50-root.conf @@ -5,6 +5,7 @@ DefaultSubvolume=/ Type=root Compression=zstd ExcludeFiles=/boot/ +Priority=0 Format=btrfs CopyFiles=/:/ Subvolumes=/ /home From 310451a6b95118431f6fa554818364b1832c4b76 Mon Sep 17 00:00:00 2001 From: Cappy Ishihara Date: Sun, 1 Jun 2025 04:19:02 +0700 Subject: [PATCH 06/17] I forgot we can do negative numbers --- templates/wholedisk/50-root.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/wholedisk/50-root.conf b/templates/wholedisk/50-root.conf index 1bf24fc0..4c7a1b48 100644 --- a/templates/wholedisk/50-root.conf +++ b/templates/wholedisk/50-root.conf @@ -5,7 +5,7 @@ DefaultSubvolume=/ Type=root Compression=zstd ExcludeFiles=/boot/ -Priority=0 +Priority=-10000 Format=btrfs CopyFiles=/:/ Subvolumes=/ /home From d535b0d5b90edecbfdbe4e4d1d8cd9daac48ca39 Mon Sep 17 00:00:00 2001 From: Cappy Ishihara Date: Sun, 1 Jun 2025 04:32:27 +0700 Subject: [PATCH 07/17] set partition weights --- templates/wholedisk/45-boot.conf | 2 +- templates/wholedisk/47-recovery.conf | 3 ++- templates/wholedisk/50-root.conf | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/templates/wholedisk/45-boot.conf b/templates/wholedisk/45-boot.conf index 1cc89821..145fee73 100644 --- a/templates/wholedisk/45-boot.conf +++ b/templates/wholedisk/45-boot.conf @@ -1,7 +1,7 @@ [Partition] Format=ext4 SizeMinBytes=1G -Weight=100 +Weight=200 SizeMaxBytes=2G MountPoint=/boot/:defaults # This path is actually relative; see man repart.d at --copy-source option diff --git a/templates/wholedisk/47-recovery.conf b/templates/wholedisk/47-recovery.conf index a6be84fd..80903cde 100644 --- a/templates/wholedisk/47-recovery.conf +++ b/templates/wholedisk/47-recovery.conf @@ -9,8 +9,9 @@ Label=um_recovery # Priority of 5000 ensures that it wouldn't be created if # We don't have enough space Priority=5000 +Weight=100 # This partition will be 8GB in size, for future-proofing and # recovery image updates. PaddingMinBytes=2G -SizeMaxBytes=10G +SizeMaxBytes=8G FactoryReset=false \ No newline at end of file diff --git a/templates/wholedisk/50-root.conf b/templates/wholedisk/50-root.conf index 4c7a1b48..17dd3f61 100644 --- a/templates/wholedisk/50-root.conf +++ b/templates/wholedisk/50-root.conf @@ -6,6 +6,7 @@ Type=root Compression=zstd ExcludeFiles=/boot/ Priority=-10000 +Weight=5000 Format=btrfs CopyFiles=/:/ Subvolumes=/ /home From 4726dc94515680f936b08daed67ffb0ba736c77e Mon Sep 17 00:00:00 2001 From: Cappy Ishihara Date: Sun, 1 Jun 2025 04:40:01 +0700 Subject: [PATCH 08/17] funny comment update --- templates/wholedisk/47-recovery.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/wholedisk/47-recovery.conf b/templates/wholedisk/47-recovery.conf index 80903cde..ae862b14 100644 --- a/templates/wholedisk/47-recovery.conf +++ b/templates/wholedisk/47-recovery.conf @@ -10,7 +10,7 @@ Label=um_recovery # We don't have enough space Priority=5000 Weight=100 -# This partition will be 8GB in size, for future-proofing and +# This partition will be 4-8GB in size, for future-proofing and # recovery image updates. PaddingMinBytes=2G SizeMaxBytes=8G From e612c974b20f1cce6bd7fadacfb58888965b8d55 Mon Sep 17 00:00:00 2001 From: Cappy Ishihara Date: Sun, 1 Jun 2025 04:41:08 +0700 Subject: [PATCH 09/17] Remove partition padding after EROFS recovery --- templates/wholedisk/47-recovery.conf | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/wholedisk/47-recovery.conf b/templates/wholedisk/47-recovery.conf index ae862b14..21d6f97e 100644 --- a/templates/wholedisk/47-recovery.conf +++ b/templates/wholedisk/47-recovery.conf @@ -12,6 +12,5 @@ Priority=5000 Weight=100 # This partition will be 4-8GB in size, for future-proofing and # recovery image updates. -PaddingMinBytes=2G SizeMaxBytes=8G FactoryReset=false \ No newline at end of file From b2de410cdb6c67793101e0529dd32c9042670439 Mon Sep 17 00:00:00 2001 From: Cappy Ishihara Date: Tue, 3 Jun 2025 19:01:32 +0700 Subject: [PATCH 10/17] Add recovery stage setup in kernel reinstallation process --- po/en-US/readymade.ftl | 1 + src/backend/postinstall/reinstall_kernel.rs | 46 +++++++++++++++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/po/en-US/readymade.ftl b/po/en-US/readymade.ftl index 0364e40d..d0bb864a 100644 --- a/po/en-US/readymade.ftl +++ b/po/en-US/readymade.ftl @@ -92,4 +92,5 @@ stage-grub1 = Generating stage 1 grub.cfg in ESP... stage-grub2 = Generating stage 2 grub.cfg in /boot/grub2/grub.cfg... stage-biosgrub = Installing BIOS Grub2 stage-kernel = Reinstalling kernels +stage-recovery = Setting up recovery environment stage-selinux = Setting SELinux labels diff --git a/src/backend/postinstall/reinstall_kernel.rs b/src/backend/postinstall/reinstall_kernel.rs index 54c9783f..d617f98b 100644 --- a/src/backend/postinstall/reinstall_kernel.rs +++ b/src/backend/postinstall/reinstall_kernel.rs @@ -11,21 +11,29 @@ pub struct ReinstallKernel; impl PostInstallModule for ReinstallKernel { fn run(&self, _context: &Context) -> Result<()> { let kernel_vers = std::fs::read_dir("/lib/modules")? - .map(|entry| entry.unwrap().file_name()) + .filter_map(|entry| entry.ok().map(|e| e.file_name())) .collect_vec(); tracing::info!(?kernel_vers, "Kernel versions found"); // We're gonna just install the first kernel we find, so let's do that - let kver = kernel_vers.first().unwrap().to_str().unwrap(); + let kver = kernel_vers + .first() + .ok_or_else(|| color_eyre::eyre::eyre!("No kernel versions found in /lib/modules"))? + .to_str() + .ok_or_else(|| color_eyre::eyre::eyre!("Kernel version filename is not valid UTF-8"))?; // install kernel + let vmlinuz_path = format!("/lib/modules/{kver}/vmlinuz"); + if !std::path::Path::new(&vmlinuz_path).exists() { + bail!("Kernel version {kver} does not have a vmlinuz file at {vmlinuz_path}"); + } stage!(kernel { let kernel_install_cmd_status = Command::new("kernel-install") .arg("add") .arg(kver) - .arg(format!("/lib/modules/{kver}/vmlinuz")) + .arg(&vmlinuz_path) .arg("--verbose") .status()?; @@ -37,6 +45,38 @@ impl PostInstallModule for ReinstallKernel { } }); + stage!(recovery { + // copy to /boot/vmlinuz-recovery + let recovery_vmlinuz_path = format!("/boot/vmlinuz-recovery"); + if !std::path::Path::new(&recovery_vmlinuz_path).exists() { + std::fs::copy(&vmlinuz_path, &recovery_vmlinuz_path) + .map_err(|e| color_eyre::eyre::eyre!(e))?; + } else { + tracing::warn!("Recovery kernel already exists at {recovery_vmlinuz_path}, skipping copy"); + } + + // create a recovery initramfs + let recovery_initramfs_path = format!("/boot/initramfs-recovery.img"); + + let recovery_dracut = Command::new("dracut") + .arg("--force") + .arg("--add") + .arg("dmsquash-live overlayfs rescue") + .arg("--no-hostonly") + .arg("--no-uefi") + .arg("--kver") + .arg(kver) + .arg(&recovery_initramfs_path) + .status()?; + if !recovery_dracut.success() { + bail!( + "dracut failed with exit code {:?}", + recovery_dracut.code() + ); + } + tracing::info!("Recovery initramfs created at {recovery_initramfs_path}"); + }); + Ok(()) } } From e62dac0806e215f4fca87b67326a93b7d5533ec0 Mon Sep 17 00:00:00 2001 From: Jose Hernandez Date: Fri, 23 May 2025 14:08:18 +0000 Subject: [PATCH 11/17] Translated using Weblate (Spanish) Currently translated at 33.7% (25 of 74 strings) Translation: tauOS/Readymade Translate-URL: https://weblate.fyralabs.com/projects/tauOS/readymade/es/ --- po/es/readymade.ftl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/po/es/readymade.ftl b/po/es/readymade.ftl index 977eecfd..0c1d31aa 100644 --- a/po/es/readymade.ftl +++ b/po/es/readymade.ftl @@ -20,4 +20,10 @@ page-installation = Instalación page-installation-progress = Instalando sistema base... page-installationtype = Tipo de Instalación page-installationtype-chromebook = Chromebook - +unknown-os = SO Desconocido +page-installationtype-tpm = Habilitar TPM +parttype-esp = Partición de sistema EFI ({ $path }) +parttype-home = Información de usuario +parttype-other = Punto de montaje personalizado +page-welcome = Bienvenido a { $distro } +page-installationtype-encrypt = Habilitar encriptación de disco From 320a008653569d0b8083e1cec8c7df59695d4fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9ane=20GRASSER?= Date: Tue, 27 May 2025 15:58:24 +0000 Subject: [PATCH 12/17] Translated using Weblate (French) Currently translated at 100.0% (74 of 74 strings) Translation: tauOS/Readymade Translate-URL: https://weblate.fyralabs.com/projects/tauOS/readymade/fr/ --- po/fr/readymade.ftl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/po/fr/readymade.ftl b/po/fr/readymade.ftl index 0de2ae87..ee4e9be3 100644 --- a/po/fr/readymade.ftl +++ b/po/fr/readymade.ftl @@ -5,7 +5,7 @@ next = Suivant unknown-os = OS inconnu parttype-root = Racine du système de fichiers ({ $path }) -parttype-esp = Partition EFI système ({ $path }) +parttype-esp = Partition système EFI ({ $path }) parttype-home = Données utilisateur ({ $path }) parttype-var = Données variables ({ $path }) page-welcome = Bienvenue dans { $distro } @@ -45,7 +45,7 @@ page-installationtype-chromebook = Chromebook page-installationtype-custom = Personnalisée dialog-installtype-encrypt = Chiffrement du disque dialog-installtype-password = Mot de passe -dialog-installtype-repeat = Saisissez le mot de passe à nouveau +dialog-installtype-repeat = Saisissez à nouveau le mot de passe dialog-installtype-cancel = Annuler dialog-installtype-confirm = Confirmer installtype-edit-mp = Modifier le point de montage @@ -53,7 +53,7 @@ installtype-rm-mp = Supprimer le point de montage dialog-mp-part = Partition dialog-mp-at = Monter sur dialog-mp-opts = Options de montage -installtype-parttool = Sélectionnez votre outil de partitionnement +installtype-parttool = Sélectionner votre outil de partitionnement stage-extracting = Extraction des fichiers stage-copying = Copie des fichiers stage-initramfs = Régénération de l'initramfs @@ -73,9 +73,9 @@ page-installationtype-dual = Double démarrage stage-grub2 = Génération du fichier grub.cfg d'étape 2 dans /boot/grub2/grub.cfg... dialog-installtype-encrypt-desc = Veuillez définir le mot de passe de chiffrement du disque. - Si vous perdez ce mot de passe, vos données ne seront pas récupérables. + Si vous perdez ce mot de passe, vos données ne pourront pas être récupérées. stage-grub = Génération des valeurs système par défaut pour GRUB -stage-selinux = Définition des étiquettes SELinux +stage-selinux = Ajout des étiquettes SELinux page-confirmation-problem-device-mounted = { $dev } est monté sur { $mountpoint }. Démontez-le pour continuer. page-confirmation-problem-devblkopen = Le périphérique de blocs { $dev } est utilisé par les processus suivants : From dcc972a6ee9825852831bc5f77f8b06644703761 Mon Sep 17 00:00:00 2001 From: kira-we1ss Date: Fri, 30 May 2025 10:37:56 +0000 Subject: [PATCH 13/17] Translated using Weblate (Russian) Currently translated at 100.0% (74 of 74 strings) Translation: tauOS/Readymade Translate-URL: https://weblate.fyralabs.com/projects/tauOS/readymade/ru/ --- po/ru/readymade.ftl | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/po/ru/readymade.ftl b/po/ru/readymade.ftl index 98b016e3..47f953e8 100644 --- a/po/ru/readymade.ftl +++ b/po/ru/readymade.ftl @@ -8,7 +8,7 @@ parttype-home = Домашняя директория пользователя ( parttype-var = Переменные данные ({ $path }) parttype-other = Пользовательская точка монтирования раздела page-welcome = Добро пожаловать в { $distro } -page-welcome-desc = Либо попробуйте { $distro } из этой программы установки, либо начните установку сейчас. Вы всегда можете вернуться к этому экрану, выбрав в меню приложение «Installer». +page-welcome-desc = Вы можете попробовать { $distro }, или начать установку уже сейчас. page-welcome-try = Попробовать page-welcome-install = Установить page-failure = Сбой установки @@ -35,11 +35,11 @@ page-installation-help = Нужна помощь? page-installation-help-desc = Задайте вопрос в одном из наших чатов! page-installation-contrib = Внести вклад в { $distro } page-installation-contrib-desc = Узнайте, как пожертвовать своим временем, деньгами или оборудованием. -page-installation-progress = Установка базовой системы... +page-installation-progress = Установка базовых системных пакетов... page-installcustom = Пользовательская установка page-installcustom-title = Разделы и точки монтирования page-installcustom-desc = { $num } правил(а) -page-installcustom-tool = Открыть дисковую утилиту +page-installcustom-tool = Открыть утилиту разметки page-installcustom-add = Добавить новое правило page-installationtype = Тип установки page-installationtype-entire = Весь диск @@ -61,7 +61,7 @@ installtype-rm-mp = Удалить точку монтирования dialog-mp-part = Раздел dialog-mp-at = Монтировать на dialog-mp-opts = Настройки монтирования -installtype-parttool = Выберите вашу дисковую утилиту +installtype-parttool = Выберите свою утилиту разметки stage-extracting = Распаковка файлов stage-copying = Копирование файлов stage-mkpart = Создание разделов и копирование файлов @@ -70,5 +70,10 @@ stage-grub = Генерация системных настроек grub по у stage-grub1 = Генерация stage 1 grub.cfg на ESP... stage-grub2 = Генерация stage 2 grub.cfg в /boot/grub2/grub.cfg... stage-biosgrub = Установка BIOS Grub2 -stage-kernel = Повторная установка ядер +stage-kernel = Переустановка ядер stage-selinux = Установка политик SELinux +page-confirmation-problem-devblkopen = + Накопитель { $dev } уже используется следующими процессами: + { $pids } + Данные процессы должны быть остановлены для дальнейшей работы установщика. +page-confirmation-problem-device-mounted = { $dev } уже смонтирован в { $mountpoint }. Размонтируйте чтобы продолжить. From f0bbc1498c647f47200dc9dda921a5513907baf4 Mon Sep 17 00:00:00 2001 From: Cappy Ishihara Date: Tue, 3 Jun 2025 19:20:35 +0700 Subject: [PATCH 14/17] feat: lock blockdev before running systemd-repart to ensure exclusive access --- Cargo.lock | 11 +++++++++++ Cargo.toml | 1 + src/backend/install.rs | 42 +++++++++++++++++++++++++++++++----------- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index afbf1b1b..12e8a560 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -782,6 +782,16 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "file-guard" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21ef72acf95ec3d7dbf61275be556299490a245f017cf084bd23b4f68cf9407c" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "filesystem-table" version = "0.1.2" @@ -2618,6 +2628,7 @@ dependencies = [ "const_format", "derivative", "enum_dispatch", + "file-guard", "filesystem-table", "format-bytes", "freedesktop-desktop-entry", diff --git a/Cargo.toml b/Cargo.toml index 8c6851f7..b0199b02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,7 @@ no_color = "0.2.0" poly_l10n = "0.0.6" derivative = "2.2.0" parking_lot = "0.12.3" +file-guard = "0.2.0" # [dependencies.os-detect] # git = "https://github.com/FyraLabs/distinst" diff --git a/src/backend/install.rs b/src/backend/install.rs index f0200060..a2af2485 100644 --- a/src/backend/install.rs +++ b/src/backend/install.rs @@ -1,8 +1,10 @@ +use file_guard::Lock; use ipc_channel::ipc::{IpcError, IpcOneShotServer, IpcSender}; use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use std::{ io::Write, + os::unix::process::CommandExt, path::{Path, PathBuf}, process::{Command, Stdio}, sync::OnceLock, @@ -818,17 +820,35 @@ impl FinalInstallationState { ["--key-file", keyfile_path] }); - let repart_cmd = Command::new("systemd-repart") - .args(["--dry-run", if dry_run { "yes" } else { "no" }]) - .args(["--definitions", cfgdir.to_str().unwrap()]) - .args(["--empty", "force", "--offline", "false", "--json", "pretty"]) - .args(["--copy-source", ©_source].iter().filter(|_| !is_bootc)) - .args(arg_keyfile.iter().flatten()) - .arg(blockdev) - .stdout(Stdio::piped()) - .stderr(Stdio::inherit()) - .output() - .context("can't run systemd-repart")?; + // Scope to ensure device and lock live long enough for the command + let repart_cmd = { + // lock device + let mut device = std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(blockdev) + .context("Failed to open block device")?; + // SAFETY: We're locking the device so that repart doesn't fail due to EBUSY + unsafe { + Command::new("systemd-repart") + .args(["--dry-run", if dry_run { "yes" } else { "no" }]) + .args(["--definitions", cfgdir.to_str().unwrap()]) + .args(["--empty", "force", "--offline", "false", "--json", "pretty"]) + .args(["--copy-source", ©_source].iter().filter(|_| !is_bootc)) + .args(arg_keyfile.iter().flatten()) + .arg(blockdev) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .pre_exec(move || { + // SAFETY: We are locking the device so that repart doesn't fail due to device busy + // The lock is held in this scope + let mut _lock = file_guard::lock(&mut device, Lock::Exclusive, 0, 1)?; + Ok(()) + }) + .output() + .context("can't run systemd-repart")? + } + }; if !repart_cmd.status.success() { bail!( From 034ad60007c13872db4c662cb30c0b3bf1686a49 Mon Sep 17 00:00:00 2001 From: Cappy Ishihara Date: Tue, 3 Jun 2025 19:25:29 +0700 Subject: [PATCH 15/17] Lock before spawning the process --- src/backend/install.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/backend/install.rs b/src/backend/install.rs index a2af2485..00028bba 100644 --- a/src/backend/install.rs +++ b/src/backend/install.rs @@ -828,6 +828,9 @@ impl FinalInstallationState { .write(true) .open(blockdev) .context("Failed to open block device")?; + // SAFETY: We are locking the device so that repart doesn't fail due to device busy + // The lock is held in this scope + let mut _lock = file_guard::lock(&mut device, Lock::Exclusive, 0, 1)?; // SAFETY: We're locking the device so that repart doesn't fail due to EBUSY unsafe { Command::new("systemd-repart") @@ -839,12 +842,7 @@ impl FinalInstallationState { .arg(blockdev) .stdout(Stdio::piped()) .stderr(Stdio::inherit()) - .pre_exec(move || { - // SAFETY: We are locking the device so that repart doesn't fail due to device busy - // The lock is held in this scope - let mut _lock = file_guard::lock(&mut device, Lock::Exclusive, 0, 1)?; - Ok(()) - }) + .pre_exec(move || Ok(())) .output() .context("can't run systemd-repart")? } From 7c6509845dff4be6b5da422b61190f4b56a86d12 Mon Sep 17 00:00:00 2001 From: Cappy Ishihara Date: Tue, 3 Jun 2025 19:26:38 +0700 Subject: [PATCH 16/17] Remove redundant unsafe block --- src/backend/install.rs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/backend/install.rs b/src/backend/install.rs index 00028bba..0ccb68a9 100644 --- a/src/backend/install.rs +++ b/src/backend/install.rs @@ -828,24 +828,20 @@ impl FinalInstallationState { .write(true) .open(blockdev) .context("Failed to open block device")?; - // SAFETY: We are locking the device so that repart doesn't fail due to device busy + // We are locking the device so that repart doesn't fail due to device busy // The lock is held in this scope let mut _lock = file_guard::lock(&mut device, Lock::Exclusive, 0, 1)?; - // SAFETY: We're locking the device so that repart doesn't fail due to EBUSY - unsafe { - Command::new("systemd-repart") - .args(["--dry-run", if dry_run { "yes" } else { "no" }]) - .args(["--definitions", cfgdir.to_str().unwrap()]) - .args(["--empty", "force", "--offline", "false", "--json", "pretty"]) - .args(["--copy-source", ©_source].iter().filter(|_| !is_bootc)) - .args(arg_keyfile.iter().flatten()) - .arg(blockdev) - .stdout(Stdio::piped()) - .stderr(Stdio::inherit()) - .pre_exec(move || Ok(())) - .output() - .context("can't run systemd-repart")? - } + Command::new("systemd-repart") + .args(["--dry-run", if dry_run { "yes" } else { "no" }]) + .args(["--definitions", cfgdir.to_str().unwrap()]) + .args(["--empty", "force", "--offline", "false", "--json", "pretty"]) + .args(["--copy-source", ©_source].iter().filter(|_| !is_bootc)) + .args(arg_keyfile.iter().flatten()) + .arg(blockdev) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .output() + .context("can't run systemd-repart")? }; if !repart_cmd.status.success() { From 755f82bad29ff6ee46210a65e954a6b54310bf13 Mon Sep 17 00:00:00 2001 From: Cappy Ishihara Date: Wed, 4 Jun 2025 07:11:17 +0700 Subject: [PATCH 17/17] clippy lint cleanup --- src/backend/postinstall/reinstall_kernel.rs | 11 ++++++----- src/backend/repart_output.rs | 7 +++++++ templates/wholedisk/47-recovery.conf | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/backend/postinstall/reinstall_kernel.rs b/src/backend/postinstall/reinstall_kernel.rs index d617f98b..ca69a089 100644 --- a/src/backend/postinstall/reinstall_kernel.rs +++ b/src/backend/postinstall/reinstall_kernel.rs @@ -47,16 +47,16 @@ impl PostInstallModule for ReinstallKernel { stage!(recovery { // copy to /boot/vmlinuz-recovery - let recovery_vmlinuz_path = format!("/boot/vmlinuz-recovery"); - if !std::path::Path::new(&recovery_vmlinuz_path).exists() { + let recovery_vmlinuz_path = "/boot/vmlinuz-recovery".to_owned(); + if std::path::Path::new(&recovery_vmlinuz_path).exists() { + tracing::warn!("Recovery kernel already exists at {recovery_vmlinuz_path}, skipping copy"); + } else { std::fs::copy(&vmlinuz_path, &recovery_vmlinuz_path) .map_err(|e| color_eyre::eyre::eyre!(e))?; - } else { - tracing::warn!("Recovery kernel already exists at {recovery_vmlinuz_path}, skipping copy"); } // create a recovery initramfs - let recovery_initramfs_path = format!("/boot/initramfs-recovery.img"); + let recovery_initramfs_path = "/boot/initramfs-recovery.img".to_owned(); let recovery_dracut = Command::new("dracut") .arg("--force") @@ -76,6 +76,7 @@ impl PostInstallModule for ReinstallKernel { } tracing::info!("Recovery initramfs created at {recovery_initramfs_path}"); }); + // todo: grub.d template for boot entry in OS Ok(()) } diff --git a/src/backend/repart_output.rs b/src/backend/repart_output.rs index 772310ce..3d68b463 100644 --- a/src/backend/repart_output.rs +++ b/src/backend/repart_output.rs @@ -229,6 +229,13 @@ impl RepartOutput { .map(|part| part.node.clone()) } + pub fn get_recovery_partition(&self) -> std::option::Option { + self.partitions + .iter() + .find(|part| part.label == "os_recovery") + .map(|part| part.node.clone()) + } + /// Create [`tiffin::Container`] from the repartitioning output with the mountpoints /// from the DDI partition types pub fn to_container( diff --git a/templates/wholedisk/47-recovery.conf b/templates/wholedisk/47-recovery.conf index 21d6f97e..c10187da 100644 --- a/templates/wholedisk/47-recovery.conf +++ b/templates/wholedisk/47-recovery.conf @@ -5,7 +5,7 @@ Type=8DA63339-0007-60C0-C436-083AC8230908 Format=erofs SizeMinBytes=4G CopyFiles=/:/ -Label=um_recovery +Label=os_recovery # Priority of 5000 ensures that it wouldn't be created if # We don't have enough space Priority=5000