Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 10 additions & 60 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ hex = "0.4.3"
libc = "^0.2"
libsystemd = ">= 0.3, < 0.8"
log = "^0.4"
openat = "0.1.20"
openat-ext = ">= 0.2.2, < 0.3.0"
cap-std = "4.0.2"
openssl = "^0.10"
os-release = "0.1.0"
regex = "1.12.3"
Expand Down
42 changes: 29 additions & 13 deletions src/backend/statefile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
use crate::model::SavedState;
use crate::util::SignalTerminationGuard;
use anyhow::{bail, Context, Result};
use cap_std::ambient_authority;
use cap_std::fs::{Dir, Permissions, PermissionsExt};
use cap_std_ext::dirext::CapStdExtDirExt;
use fn_error_context::context;
use fs2::FileExt;
use openat_ext::OpenatDirExt;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
Expand All @@ -23,9 +25,18 @@ impl SavedState {
/// While ordinarily the daemon runs as a systemd unit (which implicitly
/// ensures a single instance) this is a double check against other
/// execution paths.
pub(crate) fn acquire_write_lock(sysroot: openat::Dir) -> Result<StateLockGuard> {
let lockfile = sysroot.write_file(Self::WRITE_LOCK_PATH, 0o644)?;
lockfile.lock_exclusive()?;
pub(crate) fn acquire_write_lock(sysroot: Dir) -> Result<StateLockGuard> {
sysroot
.atomic_write_with_perms(Self::WRITE_LOCK_PATH, "", Permissions::from_mode(0o644))
.context("Creating lock file")?;

let lockfile = sysroot
.open(Self::WRITE_LOCK_PATH)
.context("Opening lock file")?
.into_std();

lockfile.lock_exclusive().context("Acquiring lock")?;

let guard = StateLockGuard {
sysroot,
termguard: Some(SignalTerminationGuard::new()?),
Expand All @@ -36,7 +47,7 @@ impl SavedState {

/// Use this for cases when the target root isn't booted, which is
/// offline installs.
pub(crate) fn unlocked(sysroot: openat::Dir) -> Result<StateLockGuard> {
pub(crate) fn unlocked(sysroot: Dir) -> Result<StateLockGuard> {
Ok(StateLockGuard {
sysroot,
termguard: None,
Expand All @@ -48,11 +59,11 @@ impl SavedState {
#[context("Loading saved state")]
pub(crate) fn load_from_disk(root_path: impl AsRef<Path>) -> Result<Option<SavedState>> {
let root_path = root_path.as_ref();
let sysroot = openat::Dir::open(root_path)
let sysroot = Dir::open_ambient_dir(root_path, ambient_authority())
.with_context(|| format!("opening sysroot '{}'", root_path.display()))?;

let statefile_path = Path::new(Self::STATEFILE_DIR).join(Self::STATEFILE_NAME);
let saved_state = if let Some(statusf) = sysroot.open_file_optional(&statefile_path)? {
let saved_state = if let Some(statusf) = sysroot.open_optional(&statefile_path)? {
let mut bufr = std::io::BufReader::new(statusf);
let mut s = String::new();
bufr.read_to_string(&mut s)?;
Expand Down Expand Up @@ -92,7 +103,7 @@ impl SavedState {
/// Write-lock guard for statefile, protecting against concurrent state updates.
#[derive(Debug)]
pub(crate) struct StateLockGuard {
pub(crate) sysroot: openat::Dir,
pub(crate) sysroot: Dir,
#[allow(dead_code)]
termguard: Option<SignalTerminationGuard>,
#[allow(dead_code)]
Expand All @@ -102,11 +113,16 @@ pub(crate) struct StateLockGuard {
impl StateLockGuard {
/// Atomically replace the on-disk state with a new version.
pub(crate) fn update_state(&mut self, state: &SavedState) -> Result<()> {
let subdir = self.sysroot.sub_dir(SavedState::STATEFILE_DIR)?;
subdir.write_file_with_sync(SavedState::STATEFILE_NAME, 0o644, |w| -> Result<()> {
serde_json::to_writer(w, state)?;
Ok(())
})?;
let subdir = self.sysroot.open_dir(SavedState::STATEFILE_DIR)?;

subdir
.atomic_write_with_perms(
SavedState::STATEFILE_NAME,
serde_json::to_vec(state).context("Serializing state")?,
Permissions::from_mode(0o644),
)
.context("Writing state file")?;

Ok(())
}
}
18 changes: 10 additions & 8 deletions src/bios.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use anyhow::{bail, Context, Result};
use camino::Utf8PathBuf;
use openat_ext::OpenatDirExt;
use cap_std::ambient_authority;
use cap_std::fs::Dir;
use cap_std_ext::dirext::CapStdExtDirExt;
use std::io::prelude::*;
use std::path::Path;
use std::process::Command;
Expand Down Expand Up @@ -109,7 +111,7 @@ impl Component for Bios {
) -> Result<InstalledContent> {
let device =
device.ok_or_else(|| anyhow::anyhow!("BIOS component requires a target device"))?;
let src_dir = openat::Dir::open(src_root)
let src_dir = Dir::open_ambient_dir(src_root, ambient_authority())
.with_context(|| format!("opening source directory {src_root}"))?;
let Some(meta) = get_component_update(&src_dir, self)? else {
anyhow::bail!("No update metadata for component {} found", self.name());
Expand Down Expand Up @@ -151,11 +153,11 @@ impl Component for Bios {
// - Backup "/boot/loader/grub.cfg" to "/boot/grub2/grub.cfg.bak"
// - Remove symlink "/boot/grub2/grub.cfg"
// - Replace "/boot/grub2/grub.cfg" symlink with new static "grub.cfg"
fn migrate_static_grub_config(&self, sysroot_path: &str, destdir: &openat::Dir) -> Result<()> {
fn migrate_static_grub_config(&self, sysroot_path: &str, destdir: &Dir) -> Result<()> {
let grub = "boot/grub2";
// sysroot_path is /, destdir is Dir of /
let grub_config_path = Utf8PathBuf::from(sysroot_path).join(grub);
let grub_config_dir = destdir.sub_dir(grub).context("Opening boot/grub2")?;
let grub_config_dir = destdir.open_dir(grub).context("Opening boot/grub2")?;

let grub_config = grub_config_path.join(grubconfigs::GRUBCONFIG);

Expand Down Expand Up @@ -201,7 +203,7 @@ impl Component for Bios {
}

// Synchronize the filesystem containing /boot/grub2 to disk.
fsfreeze_thaw_cycle(grub_config_dir.open_file(".")?)?;
fsfreeze_thaw_cycle(grub_config_dir.reopen_as_ownedfd()?)?;

Ok(())
}
Expand Down Expand Up @@ -243,13 +245,13 @@ impl Component for Bios {
}))
}

fn query_update(&self, sysroot: &openat::Dir) -> Result<Option<ContentMetadata>> {
fn query_update(&self, sysroot: &Dir) -> Result<Option<ContentMetadata>> {
get_component_update(sysroot, self)
}

fn query_requires_update(&self, sysroot: &openat::Dir) -> Result<()> {
fn query_requires_update(&self, sysroot: &Dir) -> Result<()> {
// Failed as expected if booted with BIOS and no update metadata
if !sysroot.exists("sys/firmware/efi")? {
if !sysroot.exists("sys/firmware/efi") {
anyhow::bail!("Failed to find BIOS update metadata");
}
Ok(())
Expand Down
Loading