From a59b98fe34e311ba00dfafa735ac7dca4c71670e Mon Sep 17 00:00:00 2001 From: GlitchSlayed <60859364+GlitchSlayed@users.noreply.github.com> Date: Wed, 18 Mar 2026 21:55:09 -0400 Subject: [PATCH] Add GPT autodiscovery status advisory --- src/app.rs | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 1 deletion(-) diff --git a/src/app.rs b/src/app.rs index 56aa0ce..fe3824a 100644 --- a/src/app.rs +++ b/src/app.rs @@ -11,6 +11,7 @@ use crate::kernel::{ use crate::ukify::{build_uki, UkifyParams}; use anyhow::{Context, Result}; use log::info; +use std::fs; use std::path::{Path, PathBuf}; /// Fully resolved runtime settings for generation. @@ -205,10 +206,94 @@ pub fn status(runner: &dyn CommandRunner, cfg: &AppConfig) -> Result { lines.push(format!("esp_path: {}", cfg.uki.esp_path.display())); lines.push(format!("output_dir: {}", cfg.uki.output_dir.display())); lines.push(format!("cmdline_file: {}", cfg.uki.cmdline_file.display())); + + if let Some(advisory) = discoverable_root_advisory(runner, &cfg.uki.cmdline_file)? { + lines.push(advisory); + } + lines.push(format!("os_release: {}", cfg.uki.os_release.display())); Ok(lines.join("\n")) } +fn discoverable_root_advisory( + runner: &dyn CommandRunner, + cmdline_file: &Path, +) -> Result> { + let cmdline = match fs::read_to_string(cmdline_file) { + Ok(content) => content, + Err(_) => return Ok(None), + }; + + let has_root = cmdline + .split_whitespace() + .any(|token| token.starts_with("root=")); + + let mounts = match fs::read_to_string("/proc/mounts") { + Ok(content) => content, + Err(_) => return Ok(None), + }; + + let Some(root_device) = parse_root_mount_source(&mounts) else { + return Ok(None); + }; + + if !root_device.starts_with("/dev/") { + return Ok(None); + } + + let parttype = runner + .run("lsblk", &["-no", "PARTTYPE", &root_device]) + .context("failed determining root partition GPT type GUID")? + .stdout + .trim() + .to_ascii_lowercase(); + + Ok(discoverable_root_message( + has_root, + &parttype, + std::env::consts::ARCH, + )) +} + +fn discoverable_root_message(has_root: bool, parttype: &str, arch: &str) -> Option { + let expected_guid = dps_root_guid(arch)?; + if parttype != expected_guid { + return None; + } + + Some(if has_root { + [ + "[INFO] Root partition carries a discoverable GPT GUID.", + " 'root=' in your cmdline may be unnecessary on this system.", + " Consider removing it and testing with: rustyuki generate --dry-run", + ] + .join("\n") + } else { + "[OK] Cmdline has no 'root='. GPT autodiscovery will locate the root partition.".to_string() + }) +} + +fn parse_root_mount_source(proc_mounts: &str) -> Option { + proc_mounts.lines().find_map(|line| { + let mut fields = line.split_whitespace(); + let source = fields.next()?; + let target = fields.next()?; + (target == "/").then(|| source.to_string()) + }) +} + +fn dps_root_guid(arch: &str) -> Option<&'static str> { + match arch { + "x86_64" => Some("4f68bce3-e8cd-4db1-96e7-fbcaf984b709"), + "x86" | "i386" | "i586" | "i686" => Some("44479540-f297-41b2-9af7-d131d5f0458a"), + "arm" | "armv7" | "armv7l" => Some("69dad710-2ce4-4e3c-b16c-21a1d49abed3"), + "aarch64" => Some("b921b045-1df0-41c3-af44-4c6f280d3fae"), + "riscv64" => Some("72ec70a6-cf74-40e6-bd49-4bda08e8f224"), + "loongarch64" => Some("77055800-792c-4f94-b39a-98c91b762bb6"), + _ => None, + } +} + fn ensure_required_paths(settings: &GenerateSettings) -> Result<()> { let kernel_image = PathBuf::from(format!("/lib/modules/{}/vmlinuz", settings.kernel_version)); require_exists(&kernel_image, "kernel image")?; @@ -230,7 +315,10 @@ fn require_exists(path: &Path, label: &str) -> Result<()> { #[cfg(test)] mod tests { - use super::resolve_generate_settings; + use super::{ + discoverable_root_message, dps_root_guid, parse_root_mount_source, + resolve_generate_settings, + }; use crate::cli::GenerateArgs; use crate::config::AppConfig; @@ -250,4 +338,41 @@ mod tests { let resolved = resolve_generate_settings(&cfg, &args, "6.9.0-test"); assert_eq!(resolved.kernel_version, "6.9.0-test"); } + + #[test] + fn parse_root_mount_source_finds_root_device() { + let mounts = "/dev/mapper/root / xfs rw,relatime 0 0\n/dev/nvme0n1p1 /boot vfat rw 0 0\n"; + assert_eq!( + parse_root_mount_source(mounts).as_deref(), + Some("/dev/mapper/root") + ); + } + + #[test] + fn dps_root_guid_knows_x86_64() { + assert_eq!( + dps_root_guid("x86_64"), + Some("4f68bce3-e8cd-4db1-96e7-fbcaf984b709") + ); + } + + #[test] + fn discoverable_root_message_warns_when_root_token_present() { + let message = + discoverable_root_message(true, "4f68bce3-e8cd-4db1-96e7-fbcaf984b709", "x86_64") + .unwrap_or_else(|| panic!("missing message")); + assert!(message.contains("[INFO] Root partition carries a discoverable GPT GUID.")); + assert!(message.contains("'root=' in your cmdline may be unnecessary")); + } + + #[test] + fn discoverable_root_message_confirms_when_root_token_missing() { + let message = + discoverable_root_message(false, "4f68bce3-e8cd-4db1-96e7-fbcaf984b709", "x86_64") + .unwrap_or_else(|| panic!("missing message")); + assert_eq!( + message, + "[OK] Cmdline has no 'root='. GPT autodiscovery will locate the root partition." + ); + } }