Skip to content

Commit

Permalink
Add support for root=boot (with EFI) and writing UUID file
Browse files Browse the repository at this point in the history
In FCOS we never tried to support root=boot, but for bootupd
to do alongside installs in the general case we have to.

There were two things to fix here:

 - Tweak the EFI "trampoline" to check for both $prefix/grub.cfg and $prefix/boot/grub.cfg
 - Add support for writing the boot UUID into both places

(This avoids higher level tools like bootupd needing to know
 about how to find the EFI vendor dir)

This more fully replicates the logic in coreos-installer.
  • Loading branch information
cgwalters committed Nov 10, 2023
1 parent 9d5d9d5 commit d40ebdc
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 14 deletions.
37 changes: 28 additions & 9 deletions src/bootupd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,27 @@ pub(crate) enum ClientRequest {
Status,
}

pub(crate) enum ConfigMode {
None,
Static,
WithUUID,
}

impl ConfigMode {
pub(crate) fn enabled_with_uuid(&self) -> Option<bool> {
match self {
ConfigMode::None => None,
ConfigMode::Static => Some(false),
ConfigMode::WithUUID => Some(true),
}
}
}

pub(crate) fn install(
source_root: &str,
dest_root: &str,
device: Option<&str>,
with_static_configs: bool,
configs: ConfigMode,
target_components: Option<&[String]>,
auto_components: bool,
) -> Result<()> {
Expand Down Expand Up @@ -89,14 +105,17 @@ pub(crate) fn install(
}
let sysroot = &openat::Dir::open(dest_root)?;

if with_static_configs {
#[cfg(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "powerpc64"
))]
crate::grubconfigs::install(sysroot, installed_efi)?;
// On other architectures, assume that there's nothing to do.
match configs.enabled_with_uuid() {
Some(uuid) => {
#[cfg(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "powerpc64"
))]
crate::grubconfigs::install(sysroot, installed_efi, uuid)?;
// On other architectures, assume that there's nothing to do.
}
None => {}
}

// Unmount the ESP, etc.
Expand Down
16 changes: 14 additions & 2 deletions src/cli/bootupd.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::bootupd;
use crate::bootupd::{self, ConfigMode};
use anyhow::{Context, Result};
use clap::Parser;
use log::LevelFilter;
Expand Down Expand Up @@ -56,6 +56,11 @@ pub struct InstallOpts {
#[clap(long)]
with_static_configs: bool,

/// Implies `--with-static-configs`. When present, this also writes a
/// file with the UUID of the target filesystems.
#[clap(long)]
write_uuid: bool,

#[clap(long = "component", conflicts_with = "auto")]
/// Only install these components
components: Option<Vec<String>>,
Expand Down Expand Up @@ -97,11 +102,18 @@ impl DCommand {

/// Runner for `install` verb.
pub(crate) fn run_install(opts: InstallOpts) -> Result<()> {
let configmode = if opts.write_uuid {
ConfigMode::WithUUID
} else if opts.with_static_configs {
ConfigMode::Static
} else {
ConfigMode::None
};
bootupd::install(
&opts.src_root,
&opts.dest_root,
opts.device.as_deref(),
opts.with_static_configs,
configmode,
opts.components.as_deref(),
opts.auto,
)
Expand Down
44 changes: 44 additions & 0 deletions src/filesystem.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use std::os::fd::AsRawFd;
use std::os::unix::process::CommandExt;
use std::process::Command;

use anyhow::{Context, Result};
use fn_error_context::context;
use serde::Deserialize;

#[derive(Deserialize, Debug)]
#[serde(rename_all = "kebab-case")]
#[allow(dead_code)]
pub(crate) struct Filesystem {
pub(crate) source: String,
pub(crate) fstype: String,
pub(crate) options: String,
pub(crate) uuid: Option<String>,
}

#[derive(Deserialize, Debug)]
pub(crate) struct Findmnt {
pub(crate) filesystems: Vec<Filesystem>,
}

#[context("Inspecting filesystem {path:?}")]
pub(crate) fn inspect_filesystem(root: &openat::Dir, path: &str) -> Result<Filesystem> {
let rootfd = root.as_raw_fd();
// SAFETY: This is unsafe just for the pre_exec, when we port to cap-std we can use cap-std-ext
let o = unsafe {
Command::new("findmnt")
.args(["-J", "-v", "--output-all", path])
.pre_exec(move || nix::unistd::fchdir(rootfd).map_err(Into::into))
.output()?
};
let st = o.status;
if !st.success() {
anyhow::bail!("findmnt failed: {st:?}");
}
let o: Findmnt = serde_json::from_reader(std::io::Cursor::new(&o.stdout))
.context("Parsing findmnt output")?;
o.filesystems
.into_iter()
.next()
.ok_or_else(|| anyhow::anyhow!("findmnt returned no data"))
}
6 changes: 5 additions & 1 deletion src/grub2/grub-static-efi.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ else
fi
fi
set prefix=($prefix)/grub2
configfile $prefix/grub.cfg
if [ -d grub2 ]; then
configfile $prefix/grub.cfg
else
configfile $prefix/boot/grub.cfg
fi
boot

26 changes: 24 additions & 2 deletions src/grubconfigs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub(crate) fn find_efi_vendordir(efidir: &openat::Dir) -> Result<PathBuf> {

/// Install the static GRUB config files.
#[context("Installing static GRUB configs")]
pub(crate) fn install(target_root: &openat::Dir, efi: bool) -> Result<()> {
pub(crate) fn install(target_root: &openat::Dir, efi: bool, write_uuid: bool) -> Result<()> {
let bootdir = &target_root.sub_dir("boot").context("Opening /boot")?;

let mut config = std::fs::read_to_string(Path::new(CONFIGDIR).join("grub-static-pre.cfg"))?;
Expand Down Expand Up @@ -71,6 +71,21 @@ pub(crate) fn install(target_root: &openat::Dir, efi: bool) -> Result<()> {
.context("Copying grub-static.cfg")?;
println!("Installed: grub.cfg");

let uuid_path = if write_uuid {
let bootfs_meta = crate::filesystem::inspect_filesystem(bootdir, ".")?;
let bootfs_uuid = bootfs_meta
.uuid
.ok_or_else(|| anyhow::anyhow!("Failed to find UUID for boot"))?;
let grub2_uuid_contents = format!("set BOOT_UUID=\"{bootfs_uuid}\"\n");
let uuid_path = format!("{GRUB2DIR}/bootuuid.cfg");
bootdir
.write_file_contents(&uuid_path, 0o644, grub2_uuid_contents)
.context("Writing bootuuid.cfg")?;
Some(uuid_path)
} else {
None
};

let efidir = efi
.then(|| {
target_root
Expand All @@ -87,6 +102,13 @@ pub(crate) fn install(target_root: &openat::Dir, efi: bool) -> Result<()> {
.copy_file(&Path::new(CONFIGDIR).join("grub-static-efi.cfg"), target)
.context("Copying static EFI")?;
println!("Installed: {target:?}");
if let Some(uuid_path) = uuid_path {
// SAFETY: we always have a filename
let filename = Path::new(&uuid_path).file_name().unwrap();
bootdir
.copy_file_at(&uuid_path, efidir, filename)
.context("Writing bootuuid.cfg to efi dir")?;
}
}

Ok(())
Expand All @@ -106,7 +128,7 @@ mod tests {
std::fs::create_dir_all(tdp.join("boot/grub2"))?;
std::fs::create_dir_all(tdp.join("boot/efi/EFI/BOOT"))?;
std::fs::create_dir_all(tdp.join("boot/efi/EFI/fedora"))?;
install(&td, true).unwrap();
install(&td, true, false).unwrap();

assert!(td.exists("boot/grub2/grub.cfg")?);
assert!(td.exists("boot/efi/EFI/fedora/grub.cfg")?);
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ mod coreos;
mod daemon;
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
mod efi;
mod filesystem;
mod filetree;
#[cfg(any(
target_arch = "x86_64",
Expand Down

0 comments on commit d40ebdc

Please sign in to comment.