Skip to content
Draft
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
2 changes: 1 addition & 1 deletion boulder/src/build/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub fn populate(
let installation = Installation::open(&builder.env.moss_dir, None)?;
let mut moss_client = moss::Client::builder("boulder", installation)
.repositories(repositories)
.ephemeral(rootfs)
.ephemeral(rootfs, moss::fstree::Format::Native)
.build()?;

if update_repos {
Expand Down
2 changes: 1 addition & 1 deletion crates/erofs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const ST_IFLNK: u16 = 0o120_000;
/// a [`vfs::Tree`] of [`StonePayloadLayoutRecord`] entries.
#[derive(Debug, Clone, Copy, Default)]
pub struct MetaImageWriter {
xattr_namespace: XattrNamespace,
pub xattr_namespace: XattrNamespace,
}

impl MetaImageWriter {
Expand Down
7 changes: 6 additions & 1 deletion crates/vfs/src/tree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,14 @@ impl<T> Tree<T> {
}
}

/// Return structured view beginning at the provided `path`
pub fn structured_from(&self, path: &str) -> Option<Element<'_, T>> {
self.resolve_node(path).map(|root| self.structured_children(root))
}

/// Return structured view beginning at `/`
pub fn structured(&self) -> Option<Element<'_, T>> {
self.resolve_node("/").map(|root| self.structured_children(root))
self.structured_from("/")
}

/// For the given node, recursively convert to Element::Directory of Child
Expand Down
11 changes: 10 additions & 1 deletion moss/src/cli/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ pub struct Command {
/// This operation won't be captured as a new state
#[arg(value_name = "dir", long = "to")]
blit_target: Option<PathBuf>,

/// Fstree format used when `--to` is supplied
#[arg(
value_name = "format",
long = "to-format",
default_value = "native",
requires("blit_target")
)]
blit_target_format: super::FstreeFormatArg,
}

/// Handle execution of `moss install`
Expand All @@ -50,7 +59,7 @@ pub fn handle(args: &ArgMatches, installation: Installation) -> Result<(), Error

// Make ephemeral if a blit target was provided
if let Some(blit_target) = command.blit_target {
client = client.ephemeral(blit_target)?;
client = client.ephemeral(blit_target, command.blit_target_format.into())?;
}

client.install(&pkgs, yes, simulate)?;
Expand Down
19 changes: 17 additions & 2 deletions moss/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@

use std::{env, io, path::Path, path::PathBuf};

use clap::{Arg, ArgAction, Command};
use clap::{Arg, ArgAction, Command, ValueEnum};
use clap_complete::{
generate_to,
shells::{Bash, Fish, Zsh},
};
use clap_mangen::Man;
use fs_err as fs;
use moss::{Installation, installation};
use moss::{Installation, fstree, installation};
use thiserror::Error;
use tracing_common::{self, logging::LogConfig, logging::init_log_with_config};
use tui::Styled;
Expand Down Expand Up @@ -295,7 +295,7 @@
Boot(#[source] boot::Error),

#[error("cache")]
Cache(#[source] cache::Error),

Check failure on line 298 in moss/src/cli/mod.rs

View workflow job for this annotation

GitHub Actions / Build & Test Project

[clippy] reported by reviewdog 🐶 error: the `Err`-variant returned from this function is very large --> moss/src/cli/mod.rs:159:21 | 159 | pub fn process() -> Result<(), Error> { | ^^^^^^^^^^^^^^^^^ ... 298 | Cache(#[source] cache::Error), | ----------------------------- the largest variant contains at least 128 bytes | = help: try reducing the size of `cli::Error`, for example by boxing large elements or replacing it with `Box<cli::Error>` = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.94.0/index.html#result_large_err = note: `-D clippy::result-large-err` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::result_large_err)]` Raw Output: moss/src/cli/mod.rs:298:5:e:error: the `Err`-variant returned from this function is very large --> moss/src/cli/mod.rs:159:21 | 159 | pub fn process() -> Result<(), Error> { | ^^^^^^^^^^^^^^^^^ ... 298 | Cache(#[source] cache::Error), | ----------------------------- the largest variant contains at least 128 bytes | = help: try reducing the size of `cli::Error`, for example by boxing large elements or replacing it with `Box<cli::Error>` = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.94.0/index.html#result_large_err = note: `-D clippy::result-large-err` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::result_large_err)]` __END__

#[error("index")]
Index(#[source] index::Error),
Expand Down Expand Up @@ -342,3 +342,18 @@
#[error("I/O error")]
Io(#[from] io::Error),
}

#[derive(Debug, Clone, Copy, ValueEnum)]
enum FstreeFormatArg {
Native,
Overlayimg,
}

impl From<FstreeFormatArg> for fstree::Format {
fn from(value: FstreeFormatArg) -> Self {
match value {
FstreeFormatArg::Native => fstree::Format::Native,
FstreeFormatArg::Overlayimg => fstree::Format::Overlayimg,
}
}
}
11 changes: 10 additions & 1 deletion moss/src/cli/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ pub struct Command {
#[arg(value_name = "dir", long = "to")]
blit_target: Option<PathBuf>,

/// Fstree format used when `--to` is supplied
#[arg(
value_name = "format",
long = "to-format",
default_value = "native",
requires("blit_target")
)]
blit_target_format: super::FstreeFormatArg,

/// Simulate the sync (dry-run)
#[arg(long)]
dry_run: bool,
Expand Down Expand Up @@ -58,7 +67,7 @@ pub fn handle(args: &ArgMatches, installation: Installation) -> Result<(), Error

// Make ephemeral if a blit target was provided
if let Some(blit_target) = command.blit_target {
client_builder = client_builder.ephemeral(blit_target);
client_builder = client_builder.ephemeral(blit_target, command.blit_target_format.into());
}

let mut client = client_builder.build()?;
Expand Down
73 changes: 59 additions & 14 deletions moss/src/client/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ use itertools::Itertools;
use stone::{StonePayloadLayoutFile, StonePayloadLayoutRecord};
use thiserror::{self, Error};

use crate::{Installation, State, db, package::Id};
use crate::{
Installation, State, db,
fstree::{self, Fstree},
package::Id,
state,
};

use super::Client;

Expand Down Expand Up @@ -52,6 +57,12 @@ pub enum Error {

#[error("incomplete kernel tree")]
IncompleteKernel(String),

#[error("failed to find archived fstree for state {0}")]
NoArchivedState(state::Id),

#[error("fstree")]
Fstree(#[from] fstree::DriverError),
}

/// Simple mapping type for kernel discovery paths, retaining the layout reference
Expand Down Expand Up @@ -121,7 +132,7 @@ fn layouts_for_state(client: &Client, state: &State) -> Result<Vec<(Id, StonePay
}

/// Return an additional 4 older states excluding the current state
fn states_except_new(client: &Client, state: &State) -> Result<Vec<State>, db::Error> {
fn states_except_new<'a>(client: &'a Client, state: &State) -> Result<Vec<StateEntry<'a>>, Error> {
let states = client
.state_db
.list_ids()?
Expand All @@ -138,8 +149,14 @@ fn states_except_new(client: &Client, state: &State) -> Result<Vec<State>, db::E
.rev()
.take(4)
.rev()
.filter_map(|(id, _)| client.state_db.get(id).ok())
.collect::<Vec<_>>();
.map(|(id, _)| {
let state = client.state_db.get(id)?;
let fstree = client
.open_archived_state(&state.id)
.map_err(|_| Error::NoArchivedState(id))?;
Ok(StateEntry::Archived { state, fstree })
})
.collect::<Result<Vec<_>, Error>>()?;
Ok(states)
}

Expand Down Expand Up @@ -181,36 +198,43 @@ pub fn synchronize(client: &Client, state: &State) -> Result<(), Error> {
let systemd = Pattern::from_str("lib*/systemd/boot/efi/*.efi")?;
let booty_bits = boot_files_from_new_state(&client.installation, &head_layouts, &systemd);

let mut all_states = states_except_new(client, state)?;

// no fun times without a bootloder
if booty_bits.is_empty() {
return Ok(());
}

let mut all_states = states_except_new(client, state)?;
all_states.push(StateEntry::Active(state.clone()));

// Ensure all archived fstree are brought up so we can read from them
for entry in all_states.iter_mut() {
if let StateEntry::Archived { fstree, .. } = entry {
fstree.bring_up(fstree::Mutability::ReadOnly)?;
}
}

let global_schema = os_schema_for_root(&root)?;

// Grab the entries for the new state
let mut all_kernels = vec![];
all_states.push(state.clone());
for state in all_states.iter() {
let layouts = layouts_for_state(client, state)?;
let layouts = layouts_for_state(client, state.state())?;
let local_kernels = kernel_files_from_state(&layouts, &kernel_pattern);
let mapped = global_schema.discover_system_kernels(local_kernels.into_iter())?;
all_kernels.push((mapped, state.id));
all_kernels.push((mapped, state));
}

// pipe all of our entries into blsforme
let entries = all_kernels
.iter()
.flat_map(|&(ref kernels, state_id)| {
.flat_map(|&(ref kernels, state_entry)| {
let rootref = &root;
let configref = &config;
kernels.iter().filter_map(move |k| {
let sysroot = if state.id == state_id {
rootref.clone()
} else {
client.installation.root_path(state_id.to_string()).to_owned()
let state_id = state_entry.state().id;
let sysroot = match state_entry {
StateEntry::Active(_) => rootref.clone(),
StateEntry::Archived { fstree, .. } => fstree.path.clone(),
};

if !sysroot.exists() {
Expand Down Expand Up @@ -250,6 +274,13 @@ pub fn synchronize(client: &Client, state: &State) -> Result<(), Error> {
manager.sync(&global_schema)?;
}

// And finally bring all archived states back down
for entry in all_states.iter_mut() {
if let StateEntry::Archived { fstree, .. } = entry {
fstree.bring_down()?;
}
}

Ok(())
}

Expand Down Expand Up @@ -291,3 +322,17 @@ pub fn print_status(installation: &Installation) -> Result<(), Error> {

Ok(())
}

enum StateEntry<'a> {
Active(State),
Archived { state: State, fstree: Fstree<'a> },
}

impl StateEntry<'_> {
fn state(&self) -> &State {
match self {
StateEntry::Active(state) => state,
StateEntry::Archived { state, .. } => state,
}
}
}
Loading
Loading