diff --git a/moss/src/client/mod.rs b/moss/src/client/mod.rs index 13c8e373..08f7f10b 100644 --- a/moss/src/client/mod.rs +++ b/moss/src/client/mod.rs @@ -3,7 +3,8 @@ // SPDX-License-Identifier: MPL-2.0 use std::{ - fs, io, + fs::{self, create_dir_all}, + io, os::{fd::RawFd, unix::fs::symlink}, path::{Path, PathBuf}, time::Duration, @@ -219,7 +220,10 @@ impl Client { record_os_release(&self.installation.staging_dir(), Some(state.id))?; // Run all of the transaction triggers - let triggers = postblit::triggers(postblit::TriggerScope::Transaction(&self.installation), &fstree)?; + let triggers = postblit::triggers( + postblit::TriggerScope::Transaction(&self.installation, &self.scope), + &fstree, + )?; create_root_links(&self.installation.isolation_dir())?; for trigger in triggers { trigger.execute()?; @@ -235,7 +239,8 @@ impl Client { } // At this point we're allowed to run system triggers - let sys_triggers = postblit::triggers(postblit::TriggerScope::System(&self.installation), &fstree)?; + let sys_triggers = + postblit::triggers(postblit::TriggerScope::System(&self.installation, &self.scope), &fstree)?; for trigger in sys_triggers { trigger.execute()?; } @@ -245,6 +250,25 @@ impl Client { Scope::Ephemeral { blit_root } => { record_os_release(blit_root, None)?; create_root_links(blit_root)?; + create_root_links(&self.installation.isolation_dir())?; + + let etc = blit_root.join("etc"); + create_dir_all(etc)?; + + // ephemeral tx triggers + let triggers = postblit::triggers( + postblit::TriggerScope::Transaction(&self.installation, &self.scope), + &fstree, + )?; + for trigger in triggers { + trigger.execute()?; + } + // ephemeral system triggers + let sys_triggers = + postblit::triggers(postblit::TriggerScope::System(&self.installation, &self.scope), &fstree)?; + for trigger in sys_triggers { + trigger.execute()?; + } Ok(None) } } @@ -629,6 +653,7 @@ BUG_REPORT_URL="https://github.com/serpent-os""#, Ok(()) } +#[derive(Clone, Debug)] enum Scope { Stateful, Ephemeral { blit_root: PathBuf }, diff --git a/moss/src/client/postblit.rs b/moss/src/client/postblit.rs index d2f8aaaf..4ed11e9a 100644 --- a/moss/src/client/postblit.rs +++ b/moss/src/client/postblit.rs @@ -8,7 +8,10 @@ //! //! Note that currently we only load from `/usr/share/moss/triggers/{tx,sys.d}/*.yaml` //! and do not yet support local triggers -use std::{path::Path, process}; +use std::{ + path::{Path, PathBuf}, + process, +}; use container::Container; use itertools::Itertools; @@ -46,8 +49,52 @@ impl config::Config for SystemTrigger { /// Defines the scope of triggers #[derive(Clone, Copy, Debug)] pub(super) enum TriggerScope<'a> { - Transaction(&'a Installation), - System(&'a Installation), + Transaction(&'a Installation, &'a super::Scope), + System(&'a Installation, &'a super::Scope), +} + +impl<'a> TriggerScope<'a> { + // Determine the correct root directory + fn root_dir(&self) -> PathBuf { + match self { + TriggerScope::Transaction(install, scope) => match scope { + super::Scope::Stateful => install.staging_dir().clone(), + super::Scope::Ephemeral { blit_root } => blit_root.clone(), + }, + TriggerScope::System(install, scope) => match scope { + super::Scope::Stateful => install.root.clone(), + super::Scope::Ephemeral { blit_root } => blit_root.clone(), + }, + } + } + + /// Join "host" paths, outside the staging filesystem. Ensure no sandbox break for ephemeral + fn host_path(&self, path: impl AsRef) -> PathBuf { + match self { + TriggerScope::Transaction(install, scope) => match scope { + super::Scope::Stateful => install.root.join(path), + super::Scope::Ephemeral { blit_root } => blit_root.join(path), + }, + TriggerScope::System(install, scope) => match scope { + super::Scope::Stateful => install.root.join(path), + super::Scope::Ephemeral { blit_root } => blit_root.join(path), + }, + } + } + + /// Join guest paths, inside the staging filesystem. Ensure no sandbox break for ephemeral + fn guest_path(&self, path: impl AsRef) -> PathBuf { + match self { + TriggerScope::Transaction(install, scope) => match scope { + super::Scope::Stateful => install.staging_path(path), + super::Scope::Ephemeral { blit_root } => blit_root.join(path), + }, + TriggerScope::System(install, scope) => match scope { + super::Scope::Stateful => install.root.join(path), + super::Scope::Ephemeral { blit_root } => blit_root.join(path), + }, + } + } } #[derive(Debug)] @@ -66,12 +113,14 @@ pub(super) fn triggers<'a>( // Load appropriate triggers from their locations and convert back to a vec of Trigger let triggers = match scope { - TriggerScope::Transaction(install) => config::Manager::custom(install.staging_dir().join(trigger_root)) - .load::() - .into_iter() - .map(|t| t.0) - .collect_vec(), - TriggerScope::System(install) => config::Manager::custom(install.root.join(trigger_root)) + TriggerScope::Transaction(install, client_scope) => { + config::Manager::custom(scope.root_dir().join(trigger_root)) + .load::() + .into_iter() + .map(|t| t.0) + .collect_vec() + } + TriggerScope::System(install, client_scope) => config::Manager::custom(scope.root_dir().join(trigger_root)) .load::() .into_iter() .map(|t| t.0) @@ -92,18 +141,18 @@ pub(super) fn triggers<'a>( impl<'a> TriggerRunner<'a> { pub fn execute(&self) -> Result<(), Error> { match self.scope { - TriggerScope::Transaction(install) => { + TriggerScope::Transaction(install, client_scope) => { // TODO: Add caching support via /var/ let isolation = Container::new(install.isolation_dir()) .networking(false) .override_accounts(false) - .bind_ro(install.root.join("etc"), "/etc") - .bind_rw(install.staging_path("usr"), "/usr") + .bind_ro(self.scope.host_path("etc"), "/etc") + .bind_rw(self.scope.guest_path("usr"), "/usr") .work_dir("/"); Ok(isolation.run(|| execute_trigger_directly(&self.trigger))?) } - TriggerScope::System(install) => { + TriggerScope::System(install, client_scope) => { // OK, if the root == `/` then we can run directly, otherwise we need to containerise with RW. if install.root.to_string_lossy() == "/" { Ok(execute_trigger_directly(&self.trigger)?) @@ -111,9 +160,10 @@ impl<'a> TriggerRunner<'a> { let isolation = Container::new(install.isolation_dir()) .networking(false) .override_accounts(false) - .bind_rw(install.root.join("etc"), "/etc") - .bind_rw(install.root.join("usr"), "/usr") + .bind_rw(self.scope.host_path("etc"), "/etc") + .bind_rw(self.scope.guest_path("usr"), "/usr") .work_dir("/"); + Ok(isolation.run(|| execute_trigger_directly(&self.trigger))?) } }