diff --git a/Cargo.lock b/Cargo.lock index 08318b6d..f64d725b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,6 +89,7 @@ dependencies = [ "env_logger", "fs2", "hex", + "lazy_static", "libc", "libsystemd", "log", diff --git a/Cargo.toml b/Cargo.toml index 0d6f619a..0ea90e31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ clap = "~2.33" env_logger = "^0.8" fs2 = "0.4.3" hex = "0.4.2" +lazy_static = "1.4.0" libc = "^0.2" libsystemd = "^0.2" log = "^0.4" diff --git a/src/efi.rs b/src/efi.rs index 423eac61..c37509c8 100644 --- a/src/efi.rs +++ b/src/efi.rs @@ -14,6 +14,7 @@ use anyhow::{bail, Context, Result}; use openat_ext::OpenatDirExt; use chrono::prelude::*; +use lazy_static::lazy_static; use crate::component::*; use crate::filetree; @@ -22,27 +23,64 @@ use crate::ostreeutil; use crate::util; use crate::util::CommandRunExt; -/// The path to the ESP mount -pub(crate) const MOUNT_PATH: &str = "boot/efi"; +/// The ESP partition label +pub(crate) const ESP_PART_LABEL: &str = "EFI-SYSTEM"; + +#[macro_use] +lazy_static! { + /// The path to a temporary ESP mount + static ref MOUNT_PATH: PathBuf = { + // Create new directory in /tmp with randomly generated name at runtime for ESP mount path. + tempfile::tempdir_in("/tmp").expect("Failed to create temp dir for EFI mount").into_path() + }; +} #[derive(Default)] pub(crate) struct EFI {} impl EFI { fn esp_path(&self) -> PathBuf { - Path::new(MOUNT_PATH).join("EFI") + Path::new(&*MOUNT_PATH).join("EFI") + } + + fn esp_device(&self) -> PathBuf { + Path::new("/dev/disk/by-partlabel/").join(ESP_PART_LABEL) } fn open_esp_optional(&self) -> Result> { + self.ensure_mounted_esp()?; let sysroot = openat::Dir::open("/")?; let esp = sysroot.sub_dir_optional(&self.esp_path())?; Ok(esp) } fn open_esp(&self) -> Result { + self.ensure_mounted_esp()?; let sysroot = openat::Dir::open("/")?; let esp = sysroot.sub_dir(&self.esp_path())?; Ok(esp) } + + fn ensure_mounted_esp(&self) -> Result<()> { + let mount_point = &Path::new("/").join(&*MOUNT_PATH); + let output = std::process::Command::new("mountpoint") + .arg(mount_point) + .output()?; + if !output.status.success() { + let esp_device = &self.esp_device(); + if !esp_device.exists() { + log::error!("Single ESP device not found; ESP on multiple independent filesystems currently unsupported"); + anyhow::bail!("Could not find {:?}", esp_device); + } + let status = std::process::Command::new("mount") + .arg(&self.esp_device()) + .arg(mount_point) + .status()?; + if !status.success() { + anyhow::bail!("Failed to mount {:?}", esp_device); + } + }; + Ok(()) + } } impl Component for EFI { @@ -94,7 +132,6 @@ impl Component for EFI { let updatef = filetree::FileTree::new_from_dir(&updated).context("reading update dir")?; // For adoption, we should only touch files that we know about. let diff = updatef.relative_diff_to(&esp)?; - ensure_writable_efi()?; log::trace!("applying adoption diff: {}", &diff); filetree::apply_diff(&updated, &esp, &diff, None).context("applying filesystem changes")?; Ok(InstalledContent { @@ -112,7 +149,8 @@ impl Component for EFI { }; let srcdir_name = component_updatedirname(self); let ft = crate::filetree::FileTree::new_from_dir(&src_root.sub_dir(&srcdir_name)?)?; - let destdir = Path::new(dest_root).join(MOUNT_PATH); + self.ensure_mounted_esp()?; + let destdir = Path::new(dest_root).join(&*MOUNT_PATH); { let destd = openat::Dir::open(&destdir) .with_context(|| format!("opening dest dir {}", destdir.display()))?; @@ -151,10 +189,9 @@ impl Component for EFI { .context("opening update dir")?; let updatef = filetree::FileTree::new_from_dir(&updated).context("reading update dir")?; let diff = currentf.diff(&updatef)?; - let destdir = openat::Dir::open(&Path::new("/").join(MOUNT_PATH).join("EFI")) - .context("opening EFI dir")?; + self.ensure_mounted_esp()?; + let destdir = self.open_esp().context("opening EFI dir")?; validate_esp(&destdir)?; - ensure_writable_efi()?; log::trace!("applying diff: {}", &diff); filetree::apply_diff(&updated, &destdir, &diff, None) .context("applying filesystem changes")?; @@ -256,7 +293,8 @@ impl Component for EFI { .filetree .as_ref() .ok_or_else(|| anyhow::anyhow!("No filetree for installed EFI found!"))?; - let efidir = openat::Dir::open(&Path::new("/").join(MOUNT_PATH).join("EFI"))?; + self.ensure_mounted_esp()?; + let efidir = self.open_esp()?; let diff = currentf.relative_diff_to(&efidir)?; let mut errs = Vec::new(); for f in diff.changes.iter() { @@ -282,7 +320,3 @@ fn validate_esp(dir: &openat::Dir) -> Result<()> { }; Ok(()) } - -fn ensure_writable_efi() -> Result<()> { - util::ensure_writable_mount(&Path::new("/").join(MOUNT_PATH)) -} diff --git a/systemd/bootupd.service b/systemd/bootupd.service index 5a4d08ae..52735bfe 100644 --- a/systemd/bootupd.service +++ b/systemd/bootupd.service @@ -19,7 +19,6 @@ ProtectHome=yes ReadOnlyPaths=/usr PrivateTmp=yes PrivateNetwork=yes -ProtectClock=yes ProtectHostname=yes ProtectControlGroups=yes RestrictSUIDSGID=yes