summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHermes Agent <hermes@localhost>2026-08-12 07:48:21 +0000
committerHermes Agent <hermes@localhost>2026-08-12 07:48:21 +0000
commitf967d698efd44473a9285e02d0109f3bb0641622 (patch)
treec94975571db9426cf71b15c9e6d16346e438c7c6 /src
parent5e6cae20331f8e7cfd551b4656f0de852a4ad44c (diff)
Format native builder and satisfy clippy
Diffstat (limited to 'src')
-rw-r--r--src/archive.rs34
-rw-r--r--src/build.rs34
-rw-r--r--src/cli.rs13
-rw-r--r--src/compare.rs105
-rw-r--r--src/files.rs7
-rw-r--r--src/hasher.rs2
-rw-r--r--src/initramfs.rs45
-rw-r--r--src/lib.rs4
-rw-r--r--src/main.rs2
-rw-r--r--src/manifest.rs202
-rw-r--r--src/model.rs8
-rw-r--r--src/package_installer.rs7
-rw-r--r--src/rootfs.rs34
13 files changed, 375 insertions, 122 deletions
diff --git a/src/archive.rs b/src/archive.rs
index c0a2195..fd4d899 100644
--- a/src/archive.rs
+++ b/src/archive.rs
@@ -3,7 +3,7 @@ use std::fs::{self, File, OpenOptions};
use std::os::unix::fs::MetadataExt;
use std::path::{Path, PathBuf};
-use anyhow::{bail, Context, Result};
+use anyhow::{Context, Result, bail};
use tar::{Builder, EntryType, Header};
/// Writes a deterministic rootfs tar archive using numeric ownership metadata.
@@ -28,7 +28,10 @@ impl NativeTarWriter {
let output = output.as_ref();
reject_output_inside_rootfs(rootfs, output)?;
if output.exists() {
- bail!("archive output already exists; refusing to overwrite it: {}", output.display());
+ bail!(
+ "archive output already exists; refusing to overwrite it: {}",
+ output.display()
+ );
}
let temporary = temporary_output_path(output)?;
let result = (|| {
@@ -67,9 +70,12 @@ fn reject_output_inside_rootfs(rootfs: &Path, output: &Path) -> Result<()> {
.canonicalize()
.with_context(|| format!("canonicalize rootfs {}", rootfs.display()))?;
let output_parent = output.parent().unwrap_or_else(|| Path::new("."));
- let output_parent = output_parent
- .canonicalize()
- .with_context(|| format!("canonicalize archive output parent {}", output_parent.display()))?;
+ let output_parent = output_parent.canonicalize().with_context(|| {
+ format!(
+ "canonicalize archive output parent {}",
+ output_parent.display()
+ )
+ })?;
if output_parent.starts_with(&rootfs) {
bail!(
"archive output must not be inside rootfs: {} is below {}",
@@ -81,9 +87,9 @@ fn reject_output_inside_rootfs(rootfs: &Path, output: &Path) -> Result<()> {
}
fn temporary_output_path(output: &Path) -> Result<PathBuf> {
- let name = output
- .file_name()
- .ok_or_else(|| anyhow::anyhow!("archive output path has no filename: {}", output.display()))?;
+ let name = output.file_name().ok_or_else(|| {
+ anyhow::anyhow!("archive output path has no filename: {}", output.display())
+ })?;
let mut temporary_name = name.to_os_string();
temporary_name.push(".partial");
Ok(output.with_file_name(temporary_name))
@@ -139,7 +145,11 @@ fn deterministic_header(metadata: &fs::Metadata, entry_type: EntryType, size: u6
header
}
-fn append_directory(archive: &mut Builder<File>, path: &Path, metadata: &fs::Metadata) -> Result<()> {
+fn append_directory(
+ archive: &mut Builder<File>,
+ path: &Path,
+ metadata: &fs::Metadata,
+) -> Result<()> {
let mut header = deterministic_header(metadata, EntryType::Directory, 0);
archive
.append_data(&mut header, path, std::io::empty())
@@ -158,7 +168,8 @@ fn append_file(
return append_hardlink(archive, path, target, metadata);
}
let mut header = deterministic_header(metadata, EntryType::Regular, metadata.len());
- let input = File::open(source).with_context(|| format!("open rootfs file {}", source.display()))?;
+ let input =
+ File::open(source).with_context(|| format!("open rootfs file {}", source.display()))?;
archive
.append_data(&mut header, path, input)
.with_context(|| format!("append file {}", path.display()))?;
@@ -184,7 +195,8 @@ fn append_symlink(
path: &Path,
metadata: &fs::Metadata,
) -> Result<()> {
- let target = fs::read_link(source).with_context(|| format!("read rootfs symlink {}", source.display()))?;
+ let target = fs::read_link(source)
+ .with_context(|| format!("read rootfs symlink {}", source.display()))?;
let mut header = deterministic_header(metadata, EntryType::Symlink, 0);
archive
.append_link(&mut header, path, target)
diff --git a/src/build.rs b/src/build.rs
index 1fd7720..7fd3989 100644
--- a/src/build.rs
+++ b/src/build.rs
@@ -1,13 +1,13 @@
use std::path::{Path, PathBuf};
-use anyhow::{bail, Context, Result};
+use anyhow::{Context, Result, bail};
+use crate::BuildPlan;
use crate::archive::NativeTarWriter;
use crate::initramfs::{InitramfsBuilder, InitramfsRequest, InitramfsResult};
use crate::manifest::{ArtifactManifest, InitrdRecord};
use crate::package_installer::PackageInstaller;
use crate::rootfs::{InitrdOem, RootfsFinalization};
-use crate::BuildPlan;
/// Executes the native stages after a plan has been validated.
///
@@ -22,7 +22,11 @@ pub struct BuildExecutor<I, B> {
impl<I, B> BuildExecutor<I, B> {
pub fn new(installer: I, initramfs_builder: B) -> Self {
- Self { installer, initramfs_builder, tar_writer: NativeTarWriter::new() }
+ Self {
+ installer,
+ initramfs_builder,
+ tar_writer: NativeTarWriter::new(),
+ }
}
}
@@ -36,20 +40,31 @@ impl<I: PackageInstaller, B: InitramfsBuilder> BuildExecutor<I, B> {
) -> Result<BuildResult> {
let workspace = workspace.as_ref();
if workspace.exists() {
- bail!("workspace already exists; refusing to reuse it: {}", workspace.display());
+ bail!(
+ "workspace already exists; refusing to reuse it: {}",
+ workspace.display()
+ );
}
let artifact = artifact.as_ref();
if artifact.exists() {
- bail!("output artifact already exists; refusing to overwrite it: {}", artifact.display());
+ bail!(
+ "output artifact already exists; refusing to overwrite it: {}",
+ artifact.display()
+ );
}
let manifest = ArtifactManifest::path_beside(artifact)?;
if manifest.exists() {
- bail!("output manifest already exists; refusing to overwrite it: {}", manifest.display());
+ bail!(
+ "output manifest already exists; refusing to overwrite it: {}",
+ manifest.display()
+ );
}
let parent = artifact
.parent()
.filter(|parent| !parent.as_os_str().is_empty())
- .ok_or_else(|| anyhow::anyhow!("artifact path has no parent: {}", artifact.display()))?;
+ .ok_or_else(|| {
+ anyhow::anyhow!("artifact path has no parent: {}", artifact.display())
+ })?;
if parent.exists() && !parent.is_dir() {
bail!("artifact parent is not a directory: {}", parent.display());
}
@@ -67,7 +82,10 @@ impl<I: PackageInstaller, B: InitramfsBuilder> BuildExecutor<I, B> {
if !rootfs.is_dir() {
return Self::fail_and_remove_workspace(
workspace,
- anyhow::anyhow!("package installer did not create rootfs: {}", rootfs.display()),
+ anyhow::anyhow!(
+ "package installer did not create rootfs: {}",
+ rootfs.display()
+ ),
);
}
if let Err(error) = RootfsFinalization::new(
diff --git a/src/cli.rs b/src/cli.rs
index f34f485..ffa9bba 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -1,6 +1,6 @@
use std::path::PathBuf;
-use anyhow::{bail, Result};
+use anyhow::{Result, bail};
use clap::{Parser, Subcommand};
use crate::build::BuildExecutor;
@@ -77,7 +77,11 @@ pub fn run(cli: Cli) -> Result<()> {
println!("artifact: {}", result.artifact().display());
println!("manifest: {}", result.manifest().display());
if let Some(initrd) = result.initrd() {
- println!("initrd: {} sha256:{}", initrd.initrd_path().display(), initrd.sha256());
+ println!(
+ "initrd: {} sha256:{}",
+ initrd.initrd_path().display(),
+ initrd.sha256()
+ );
}
Ok(())
}
@@ -101,7 +105,10 @@ pub fn run(cli: Cli) -> Result<()> {
}
fn load_artifact(path: &std::path::Path) -> Result<ArtifactManifest> {
- if matches!(path.extension().and_then(|extension| extension.to_str()), Some("toml")) {
+ if matches!(
+ path.extension().and_then(|extension| extension.to_str()),
+ Some("toml")
+ ) {
return ArtifactManifest::load(path);
}
diff --git a/src/compare.rs b/src/compare.rs
index 8b59c1c..3382d95 100644
--- a/src/compare.rs
+++ b/src/compare.rs
@@ -50,11 +50,21 @@ impl ComparisonReport {
let mut output = String::new();
for difference in &self.differences {
match difference {
- SemanticDifference::Package { name, change } => writeln!(output, "{} package {name}", change.verb()),
- SemanticDifference::File { path, change } => writeln!(output, "{} file {path}", change.verb()),
- SemanticDifference::Initrd { change } => writeln!(output, "{} initrd", change.verb()),
- SemanticDifference::Service { name, change } => writeln!(output, "{} service {name}", change.verb()),
- SemanticDifference::ArchiveMember { path, change } => writeln!(output, "{} archive member {path}", change.verb()),
+ SemanticDifference::Package { name, change } => {
+ writeln!(output, "{} package {name}", change.verb())
+ }
+ SemanticDifference::File { path, change } => {
+ writeln!(output, "{} file {path}", change.verb())
+ }
+ SemanticDifference::Initrd { change } => {
+ writeln!(output, "{} initrd", change.verb())
+ }
+ SemanticDifference::Service { name, change } => {
+ writeln!(output, "{} service {name}", change.verb())
+ }
+ SemanticDifference::ArchiveMember { path, change } => {
+ writeln!(output, "{} archive member {path}", change.verb())
+ }
}
.expect("writing to String cannot fail");
}
@@ -64,26 +74,60 @@ impl ComparisonReport {
pub fn compare(left: &ArtifactManifest, right: &ArtifactManifest) -> ComparisonReport {
let mut differences = Vec::new();
- compare_records(&left.packages, &right.packages, |record| &record.name, |name, change| {
- SemanticDifference::Package { name: name.to_owned(), change }
- }, &mut differences);
- compare_records(&left.files, &right.files, |record| &record.path, |path, change| {
- SemanticDifference::File { path: path.to_owned(), change }
- }, &mut differences);
+ compare_records(
+ &left.packages,
+ &right.packages,
+ |record| &record.name,
+ |name, change| SemanticDifference::Package {
+ name: name.to_owned(),
+ change,
+ },
+ &mut differences,
+ );
+ compare_records(
+ &left.files,
+ &right.files,
+ |record| &record.path,
+ |path, change| SemanticDifference::File {
+ path: path.to_owned(),
+ change,
+ },
+ &mut differences,
+ );
match (&left.initrd, &right.initrd) {
- (None, Some(_)) => differences.push(SemanticDifference::Initrd { change: Change::Added }),
- (Some(_), None) => differences.push(SemanticDifference::Initrd { change: Change::Removed }),
+ (None, Some(_)) => differences.push(SemanticDifference::Initrd {
+ change: Change::Added,
+ }),
+ (Some(_), None) => differences.push(SemanticDifference::Initrd {
+ change: Change::Removed,
+ }),
(Some(left), Some(right)) if left != right => {
- differences.push(SemanticDifference::Initrd { change: Change::Changed });
+ differences.push(SemanticDifference::Initrd {
+ change: Change::Changed,
+ });
}
_ => {}
}
- compare_records(&left.services, &right.services, |record| &record.name, |name, change| {
- SemanticDifference::Service { name: name.to_owned(), change }
- }, &mut differences);
- compare_records(&left.archive_members, &right.archive_members, |record| &record.path, |path, change| {
- SemanticDifference::ArchiveMember { path: path.to_owned(), change }
- }, &mut differences);
+ compare_records(
+ &left.services,
+ &right.services,
+ |record| &record.name,
+ |name, change| SemanticDifference::Service {
+ name: name.to_owned(),
+ change,
+ },
+ &mut differences,
+ );
+ compare_records(
+ &left.archive_members,
+ &right.archive_members,
+ |record| &record.path,
+ |path, change| SemanticDifference::ArchiveMember {
+ path: path.to_owned(),
+ change,
+ },
+ &mut differences,
+ );
differences.sort();
ComparisonReport { differences }
}
@@ -95,13 +139,26 @@ fn compare_records<T: PartialEq>(
difference: impl Fn(&str, Change) -> SemanticDifference,
output: &mut Vec<SemanticDifference>,
) {
- let left = left.iter().map(|record| (key(record), record)).collect::<BTreeMap<_, _>>();
- let right = right.iter().map(|record| (key(record), record)).collect::<BTreeMap<_, _>>();
- for name in left.keys().chain(right.keys()).copied().collect::<std::collections::BTreeSet<_>>() {
+ let left = left
+ .iter()
+ .map(|record| (key(record), record))
+ .collect::<BTreeMap<_, _>>();
+ let right = right
+ .iter()
+ .map(|record| (key(record), record))
+ .collect::<BTreeMap<_, _>>();
+ for name in left
+ .keys()
+ .chain(right.keys())
+ .copied()
+ .collect::<std::collections::BTreeSet<_>>()
+ {
match (left.get(name), right.get(name)) {
(None, Some(_)) => output.push(difference(name, Change::Added)),
(Some(_), None) => output.push(difference(name, Change::Removed)),
- (Some(left), Some(right)) if left != right => output.push(difference(name, Change::Changed)),
+ (Some(left), Some(right)) if left != right => {
+ output.push(difference(name, Change::Changed))
+ }
_ => {}
}
}
diff --git a/src/files.rs b/src/files.rs
index f49bd5e..97521f8 100644
--- a/src/files.rs
+++ b/src/files.rs
@@ -1,6 +1,6 @@
use std::path::{Component, Path, PathBuf};
-use anyhow::{bail, Result};
+use anyhow::{Result, bail};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RootfsPath(PathBuf);
@@ -9,7 +9,10 @@ impl RootfsPath {
pub fn new(path: impl AsRef<Path>) -> Result<Self> {
let path = path.as_ref();
if path.as_os_str().is_empty() || path.is_absolute() {
- bail!("rootfs path must be a non-empty relative path: {}", path.display());
+ bail!(
+ "rootfs path must be a non-empty relative path: {}",
+ path.display()
+ );
}
let mut normalized = PathBuf::new();
diff --git a/src/hasher.rs b/src/hasher.rs
index 0be1808..da00778 100644
--- a/src/hasher.rs
+++ b/src/hasher.rs
@@ -1,7 +1,7 @@
use std::ffi::OsString;
use std::process::Command;
-use anyhow::{bail, Context, Result};
+use anyhow::{Context, Result, bail};
use crate::package_installer::{PackageInstaller, PackageRequest};
diff --git a/src/initramfs.rs b/src/initramfs.rs
index 4475aed..a108b85 100644
--- a/src/initramfs.rs
+++ b/src/initramfs.rs
@@ -4,7 +4,7 @@ use std::os::unix::fs::symlink;
use std::path::{Path, PathBuf};
use std::process::Command;
-use anyhow::{bail, Context, Result};
+use anyhow::{Context, Result, bail};
use sha2::{Digest, Sha256};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -32,7 +32,11 @@ impl KernelVersion {
match kernels.as_slice() {
[] => bail!("no RT kernel image found in {}", boot.display()),
[version] => Ok(Self(version.clone())),
- _ => bail!("multiple RT kernel images found in {}: {}", boot.display(), kernels.join(", ")),
+ _ => bail!(
+ "multiple RT kernel images found in {}: {}",
+ boot.display(),
+ kernels.join(", ")
+ ),
}
}
@@ -70,7 +74,10 @@ impl InitramfsRecipe {
}
}
-fn normalize(kind: &str, values: impl IntoIterator<Item = impl Into<String>>) -> Result<Vec<String>> {
+fn normalize(
+ kind: &str,
+ values: impl IntoIterator<Item = impl Into<String>>,
+) -> Result<Vec<String>> {
values
.into_iter()
.map(Into::into)
@@ -117,8 +124,14 @@ pub struct Invocation {
}
impl Invocation {
- pub fn new(program: impl Into<OsString>, arguments: impl IntoIterator<Item = impl Into<OsString>>) -> Self {
- Self { program: program.into(), arguments: arguments.into_iter().map(Into::into).collect() }
+ pub fn new(
+ program: impl Into<OsString>,
+ arguments: impl IntoIterator<Item = impl Into<OsString>>,
+ ) -> Self {
+ Self {
+ program: program.into(),
+ arguments: arguments.into_iter().map(Into::into).collect(),
+ }
}
}
@@ -136,7 +149,10 @@ impl CommandRunner for ProcessRunner {
.status()
.with_context(|| format!("run {}", invocation.program.to_string_lossy()))?;
if !status.success() {
- bail!("{} exited with {status}", invocation.program.to_string_lossy());
+ bail!(
+ "{} exited with {status}",
+ invocation.program.to_string_lossy()
+ );
}
Ok(())
}
@@ -193,11 +209,17 @@ fn write_boot_alias(rootfs: &Path, alias: &str, target: &Path) -> Result<()> {
let path = rootfs.join("boot").join(alias);
match fs::symlink_metadata(&path) {
Ok(metadata) if metadata.file_type().is_symlink() => {
- fs::remove_file(&path).with_context(|| format!("remove existing boot alias {}", path.display()))?;
+ fs::remove_file(&path)
+ .with_context(|| format!("remove existing boot alias {}", path.display()))?;
}
- Ok(_) => bail!("refusing to replace non-symlink boot alias: {}", path.display()),
+ Ok(_) => bail!(
+ "refusing to replace non-symlink boot alias: {}",
+ path.display()
+ ),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
- Err(error) => return Err(error).with_context(|| format!("inspect boot alias {}", path.display())),
+ Err(error) => {
+ return Err(error).with_context(|| format!("inspect boot alias {}", path.display()));
+ }
}
symlink(target, &path).with_context(|| format!("create boot alias {}", path.display()))
}
@@ -214,7 +236,10 @@ impl InitramfsResult {
let initrd_path = PathBuf::from(format!("boot/initrd-{}.img", kernel.as_str()));
let contents = fs::read(rootfs.join(&initrd_path))
.with_context(|| format!("read generated initrd {}", initrd_path.display()))?;
- Ok(Self { initrd_path, sha256: format!("{:x}", Sha256::digest(contents)) })
+ Ok(Self {
+ initrd_path,
+ sha256: format!("{:x}", Sha256::digest(contents)),
+ })
}
pub fn initrd_path(&self) -> &Path {
diff --git a/src/lib.rs b/src/lib.rs
index d896ac6..0ec354d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -12,6 +12,8 @@ pub mod plan;
pub mod rootfs;
pub mod stage;
-pub use model::{Architecture, BootSpec, ImageSpec, OutputFormat, PackageSelector, PackageSpec, Target};
+pub use model::{
+ Architecture, BootSpec, ImageSpec, OutputFormat, PackageSelector, PackageSpec, Target,
+};
pub use plan::BuildPlan;
pub use stage::Stage;
diff --git a/src/main.rs b/src/main.rs
index 1e06263..3ae6b3e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,4 @@
-use alt_controller_image::cli::{run, Cli};
+use alt_controller_image::cli::{Cli, run};
use clap::Parser;
fn main() {
diff --git a/src/manifest.rs b/src/manifest.rs
index 067dc9a..0821fab 100644
--- a/src/manifest.rs
+++ b/src/manifest.rs
@@ -4,7 +4,7 @@ use std::io::{Read, Write};
use std::os::unix::fs::MetadataExt;
use std::path::{Path, PathBuf};
-use anyhow::{bail, Context, Result};
+use anyhow::{Context, Result, bail};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use tar::{Archive, EntryType};
@@ -34,18 +34,41 @@ impl ArtifactManifest {
validate_file_records(&files)?;
validate_initrd_record(initrd.as_ref())?;
validate_manifest_paths(&files, initrd.as_ref(), &archive_members)?;
- ensure_unique("package", packages.iter().map(|record| record.name.as_str()))?;
+ ensure_unique(
+ "package",
+ packages.iter().map(|record| record.name.as_str()),
+ )?;
ensure_unique("file", files.iter().map(|record| record.path.as_str()))?;
- ensure_unique("service", services.iter().map(|record| record.name.as_str()))?;
- ensure_unique("archive member", archive_members.iter().map(|record| record.path.as_str()))?;
- Ok(Self { packages, files, initrd, services, archive_members })
+ ensure_unique(
+ "service",
+ services.iter().map(|record| record.name.as_str()),
+ )?;
+ ensure_unique(
+ "archive member",
+ archive_members.iter().map(|record| record.path.as_str()),
+ )?;
+ Ok(Self {
+ packages,
+ files,
+ initrd,
+ services,
+ archive_members,
+ })
}
pub fn load(path: impl AsRef<Path>) -> Result<Self> {
let path = path.as_ref();
- let text = fs::read_to_string(path).with_context(|| format!("read artifact manifest {}", path.display()))?;
- let manifest: Self = toml::from_str(&text).with_context(|| format!("parse artifact manifest {}", path.display()))?;
- Self::new(manifest.packages, manifest.files, manifest.initrd, manifest.services, manifest.archive_members)
+ let text = fs::read_to_string(path)
+ .with_context(|| format!("read artifact manifest {}", path.display()))?;
+ let manifest: Self = toml::from_str(&text)
+ .with_context(|| format!("parse artifact manifest {}", path.display()))?;
+ Self::new(
+ manifest.packages,
+ manifest.files,
+ manifest.initrd,
+ manifest.services,
+ manifest.archive_members,
+ )
}
pub fn write_beside(&self, artifact: impl AsRef<Path>) -> Result<PathBuf> {
@@ -67,9 +90,9 @@ impl ArtifactManifest {
/// directory.
pub fn path_beside(artifact: impl AsRef<Path>) -> Result<PathBuf> {
let artifact = artifact.as_ref();
- let name = artifact
- .file_name()
- .ok_or_else(|| anyhow::anyhow!("artifact path has no filename: {}", artifact.display()))?;
+ let name = artifact.file_name().ok_or_else(|| {
+ anyhow::anyhow!("artifact path has no filename: {}", artifact.display())
+ })?;
let mut manifest_name = name.to_os_string();
manifest_name.push(".manifest.toml");
Ok(artifact.with_file_name(manifest_name))
@@ -134,7 +157,10 @@ impl ArtifactManifest {
EntryType::Directory => "directory",
EntryType::Symlink => "symlink",
EntryType::Link => "hardlink",
- other => bail!("unsupported archive member type {other:?} in {}", archive_path.display()),
+ other => bail!(
+ "unsupported archive member type {other:?} in {}",
+ archive_path.display()
+ ),
};
archive_members.push(ArchiveMemberRecord::new(path.clone(), kind));
@@ -144,7 +170,10 @@ impl ArtifactManifest {
if path.starts_with("boot/initrd-") && path.ends_with(".img") {
let record = InitrdRecord::new(path.clone(), digest.clone());
if initrd.replace(record).is_some() {
- bail!("multiple initrd images found in archive {}", archive_path.display());
+ bail!(
+ "multiple initrd images found in archive {}",
+ archive_path.display()
+ );
}
}
if !path.starts_with("boot/initrd-") || !path.ends_with(".img") {
@@ -194,7 +223,10 @@ fn collect_files(rootfs: &Path, initrd: Option<&InitrdRecord>) -> Result<Vec<Fil
entries.sort_by_key(|entry| entry.path().to_path_buf());
let mut hardlink_targets: BTreeMap<(u64, u64), String> = BTreeMap::new();
for entry in entries {
- let relative = entry.path().strip_prefix(rootfs).expect("walk entry is below rootfs");
+ let relative = entry
+ .path()
+ .strip_prefix(rootfs)
+ .expect("walk entry is below rootfs");
if is_internal_metadata_path(&portable_path(relative)?) {
continue;
}
@@ -204,7 +236,9 @@ fn collect_files(rootfs: &Path, initrd: Option<&InitrdRecord>) -> Result<Vec<Fil
}
let file_type = entry.file_type();
if file_type.is_file() {
- let metadata = entry.metadata().with_context(|| format!("inspect rootfs file {}", entry.path().display()))?;
+ let metadata = entry
+ .metadata()
+ .with_context(|| format!("inspect rootfs file {}", entry.path().display()))?;
let key = (metadata.dev(), metadata.ino());
if let Some(target) = hardlink_targets.get(&key) {
files.push(FileRecord::hardlink(path, target.clone()));
@@ -230,7 +264,8 @@ fn collect_services(rootfs: &Path) -> Result<Vec<ServiceRecord>> {
}
let mut services = Vec::new();
for entry in WalkDir::new(&system).follow_links(false).min_depth(2) {
- let entry = entry.with_context(|| format!("walk service state directory {}", system.display()))?;
+ let entry =
+ entry.with_context(|| format!("walk service state directory {}", system.display()))?;
let parent_is_wants_directory = entry
.path()
.parent()
@@ -239,10 +274,9 @@ fn collect_services(rootfs: &Path) -> Result<Vec<ServiceRecord>> {
if !parent_is_wants_directory || !entry.file_type().is_symlink() {
continue;
}
- let name = entry
- .file_name()
- .to_str()
- .ok_or_else(|| anyhow::anyhow!("non-UTF-8 service name in {}", entry.path().display()))?;
+ let name = entry.file_name().to_str().ok_or_else(|| {
+ anyhow::anyhow!("non-UTF-8 service name in {}", entry.path().display())
+ })?;
if name.ends_with(".service") {
services.push(ServiceRecord::new(name, true));
}
@@ -257,11 +291,16 @@ fn coalesce_enabled_services(mut services: Vec<ServiceRecord>) -> Vec<ServiceRec
}
fn collect_archive_members(archive_path: &Path) -> Result<Vec<ArchiveMemberRecord>> {
- let file = fs::File::open(archive_path).with_context(|| format!("open archive {}", archive_path.display()))?;
+ let file = fs::File::open(archive_path)
+ .with_context(|| format!("open archive {}", archive_path.display()))?;
let mut archive = Archive::new(file);
let mut members = Vec::new();
- for entry in archive.entries().with_context(|| format!("read archive {}", archive_path.display()))? {
- let entry = entry.with_context(|| format!("read archive member from {}", archive_path.display()))?;
+ for entry in archive
+ .entries()
+ .with_context(|| format!("read archive {}", archive_path.display()))?
+ {
+ let entry = entry
+ .with_context(|| format!("read archive member from {}", archive_path.display()))?;
let path = portable_path(&entry.path()?)?;
if is_archive_root_path(&path) || is_internal_metadata_path(&path) {
continue;
@@ -271,7 +310,10 @@ fn collect_archive_members(archive_path: &Path) -> Result<Vec<ArchiveMemberRecor
EntryType::Directory => "directory",
EntryType::Symlink => "symlink",
EntryType::Link => "hardlink",
- other => bail!("unsupported archive member type {other:?} in {}", archive_path.display()),
+ other => bail!(
+ "unsupported archive member type {other:?} in {}",
+ archive_path.display()
+ ),
};
members.push(ArchiveMemberRecord::new(path, kind));
}
@@ -279,7 +321,8 @@ fn collect_archive_members(archive_path: &Path) -> Result<Vec<ArchiveMemberRecor
}
fn sha256_file(path: &Path) -> Result<String> {
- let mut file = fs::File::open(path).with_context(|| format!("open rootfs file {}", path.display()))?;
+ let mut file =
+ fs::File::open(path).with_context(|| format!("open rootfs file {}", path.display()))?;
sha256_reader(&mut file, path)
}
@@ -287,7 +330,9 @@ fn sha256_reader(reader: &mut impl Read, source: &Path) -> Result<String> {
let mut hasher = Sha256::new();
let mut buffer = [0; 8192];
loop {
- let read = reader.read(&mut buffer).with_context(|| format!("read {}", source.display()))?;
+ let read = reader
+ .read(&mut buffer)
+ .with_context(|| format!("read {}", source.display()))?;
if read == 0 {
break;
}
@@ -298,9 +343,17 @@ fn sha256_reader(reader: &mut impl Read, source: &Path) -> Result<String> {
fn portable_path(path: &Path) -> Result<String> {
if path.is_absolute() {
- bail!("absolute path is not valid in an artifact manifest: {}", path.display());
+ bail!(
+ "absolute path is not valid in an artifact manifest: {}",
+ path.display()
+ );
}
- let value = path.to_str().ok_or_else(|| anyhow::anyhow!("non-UTF-8 path is not valid in an artifact manifest: {}", path.display()))?;
+ let value = path.to_str().ok_or_else(|| {
+ anyhow::anyhow!(
+ "non-UTF-8 path is not valid in an artifact manifest: {}",
+ path.display()
+ )
+ })?;
if value.is_empty() {
bail!("empty path is not valid in an artifact manifest");
}
@@ -310,19 +363,28 @@ fn portable_path(path: &Path) -> Result<String> {
std::path::Component::Normal(part) => normalized.push(part),
std::path::Component::CurDir => {}
std::path::Component::ParentDir => {
- bail!("parent path is not valid in an artifact manifest: {}", path.display());
+ bail!(
+ "parent path is not valid in an artifact manifest: {}",
+ path.display()
+ );
}
std::path::Component::RootDir | std::path::Component::Prefix(_) => {
- bail!("absolute path is not valid in an artifact manifest: {}", path.display());
+ bail!(
+ "absolute path is not valid in an artifact manifest: {}",
+ path.display()
+ );
}
}
}
if normalized.as_os_str().is_empty() {
return Ok(".".into());
}
- let normalized = normalized
- .to_str()
- .ok_or_else(|| anyhow::anyhow!("non-UTF-8 path is not valid in an artifact manifest: {}", path.display()))?;
+ let normalized = normalized.to_str().ok_or_else(|| {
+ anyhow::anyhow!(
+ "non-UTF-8 path is not valid in an artifact manifest: {}",
+ path.display()
+ )
+ })?;
Ok(normalized.to_owned())
}
@@ -349,7 +411,8 @@ fn is_enabled_service(path: &str) -> bool {
}
fn is_internal_metadata_path(path: &str) -> bool {
- path.split('/').any(|component| matches!(component, ".host" | ".fakedata"))
+ path.split('/')
+ .any(|component| matches!(component, ".host" | ".fakedata"))
}
fn is_archive_root_path(path: &str) -> bool {
@@ -364,7 +427,12 @@ fn service_name(path: &str) -> Result<&str> {
}
fn link_target(path: &Path) -> Result<String> {
- let value = path.to_str().ok_or_else(|| anyhow::anyhow!("non-UTF-8 symlink target is not valid in an artifact manifest: {}", path.display()))?;
+ let value = path.to_str().ok_or_else(|| {
+ anyhow::anyhow!(
+ "non-UTF-8 symlink target is not valid in an artifact manifest: {}",
+ path.display()
+ )
+ })?;
if value.is_empty() {
bail!("empty symlink target is not valid in an artifact manifest");
}
@@ -376,7 +444,11 @@ fn validate_manifest_paths(
initrd: Option<&InitrdRecord>,
archive_members: &[ArchiveMemberRecord],
) -> Result<()> {
- for path in files.iter().map(|record| record.path.as_str()).chain(archive_members.iter().map(|record| record.path.as_str())) {
+ for path in files
+ .iter()
+ .map(|record| record.path.as_str())
+ .chain(archive_members.iter().map(|record| record.path.as_str()))
+ {
portable_path(Path::new(path))?;
}
if let Some(initrd) = initrd {
@@ -386,10 +458,11 @@ fn validate_manifest_paths(
}
fn validate_initrd_record(record: Option<&InitrdRecord>) -> Result<()> {
- if let Some(record) = record {
- if record.sha256.is_empty() {
+ match record {
+ Some(record) if record.sha256.is_empty() => {
bail!("initrd record requires a sha256 digest: {}", record.path);
}
+ _ => {}
}
Ok(())
}
@@ -407,13 +480,21 @@ fn validate_file_records(records: &[FileRecord]) -> Result<()> {
}
"symlink" | "hardlink" => {
if record.digest.is_some() {
- bail!("{} record cannot carry a digest: {}", record.kind, record.path);
+ bail!(
+ "{} record cannot carry a digest: {}",
+ record.kind,
+ record.path
+ );
}
if record.target.as_deref().is_none_or(str::is_empty) {
bail!("{} record requires a target: {}", record.kind, record.path);
}
}
- _ => bail!("unsupported file record kind {}: {}", record.kind, record.path),
+ _ => bail!(
+ "unsupported file record kind {}: {}",
+ record.kind,
+ record.path
+ ),
}
}
Ok(())
@@ -437,7 +518,10 @@ pub struct PackageRecord {
impl PackageRecord {
pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
- Self { name: name.into(), version: version.into() }
+ Self {
+ name: name.into(),
+ version: version.into(),
+ }
}
}
@@ -451,15 +535,30 @@ pub struct FileRecord {
impl FileRecord {
pub fn file(path: impl Into<String>, digest: impl Into<String>) -> Self {
- Self { path: path.into(), kind: "file".into(), digest: Some(digest.into()), target: None }
+ Self {
+ path: path.into(),
+ kind: "file".into(),
+ digest: Some(digest.into()),
+ target: None,
+ }
}
pub fn symlink(path: impl Into<String>, target: impl Into<String>) -> Self {
- Self { path: path.into(), kind: "symlink".into(), digest: None, target: Some(target.into()) }
+ Self {
+ path: path.into(),
+ kind: "symlink".into(),
+ digest: None,
+ target: Some(target.into()),
+ }
}
pub fn hardlink(path: impl Into<String>, target: impl Into<String>) -> Self {
- Self { path: path.into(), kind: "hardlink".into(), digest: None, target: Some(target.into()) }
+ Self {
+ path: path.into(),
+ kind: "hardlink".into(),
+ digest: None,
+ target: Some(target.into()),
+ }
}
}
@@ -471,7 +570,10 @@ pub struct InitrdRecord {
impl InitrdRecord {
pub fn new(path: impl Into<String>, sha256: impl Into<String>) -> Self {
- Self { path: path.into(), sha256: sha256.into() }
+ Self {
+ path: path.into(),
+ sha256: sha256.into(),
+ }
}
}
@@ -483,7 +585,10 @@ pub struct ServiceRecord {
impl ServiceRecord {
pub fn new(name: impl Into<String>, enabled: bool) -> Self {
- Self { name: name.into(), enabled }
+ Self {
+ name: name.into(),
+ enabled,
+ }
}
}
@@ -495,6 +600,9 @@ pub struct ArchiveMemberRecord {
impl ArchiveMemberRecord {
pub fn new(path: impl Into<String>, kind: impl Into<String>) -> Self {
- Self { path: path.into(), kind: kind.into() }
+ Self {
+ path: path.into(),
+ kind: kind.into(),
+ }
}
}
diff --git a/src/model.rs b/src/model.rs
index 13fa5e8..a0bc51c 100644
--- a/src/model.rs
+++ b/src/model.rs
@@ -2,7 +2,7 @@ use std::collections::HashSet;
use std::fs;
use std::path::Path;
-use anyhow::{bail, Context, Result};
+use anyhow::{Context, Result, bail};
use serde::Deserialize;
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
@@ -130,9 +130,9 @@ impl ImageSpec {
for module in &self.boot.initrd_modules {
if module.is_empty()
|| module.contains('/')
- || !module
- .chars()
- .all(|character| character.is_ascii_alphanumeric() || matches!(character, '.' | '_' | '-'))
+ || !module.chars().all(|character| {
+ character.is_ascii_alphanumeric() || matches!(character, '.' | '_' | '-')
+ })
{
bail!("invalid initrd module: {module}");
}
diff --git a/src/package_installer.rs b/src/package_installer.rs
index 03e429f..ec84add 100644
--- a/src/package_installer.rs
+++ b/src/package_installer.rs
@@ -1,6 +1,6 @@
use std::path::{Path, PathBuf};
-use anyhow::{bail, Result};
+use anyhow::{Result, bail};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AptConfig(PathBuf);
@@ -12,7 +12,10 @@ impl AptConfig {
bail!("APT configuration must be a file path: {}", path.display());
}
if !path.is_file() {
- bail!("APT configuration does not exist or is not a regular file: {}", path.display());
+ bail!(
+ "APT configuration does not exist or is not a regular file: {}",
+ path.display()
+ );
}
Ok(Self(path.to_path_buf()))
}
diff --git a/src/rootfs.rs b/src/rootfs.rs
index ea7d87b..79406a7 100644
--- a/src/rootfs.rs
+++ b/src/rootfs.rs
@@ -3,9 +3,9 @@ use std::fs;
use std::os::unix::fs::symlink;
use std::path::{Path, PathBuf};
-use anyhow::{bail, Context, Result};
+use anyhow::{Context, Result, bail};
-use crate::files::{relative_symlink_is_safe, RootfsPath};
+use crate::files::{RootfsPath, relative_symlink_is_safe};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CopyTree {
@@ -127,7 +127,13 @@ fn copy_tree(rootfs: &Path, copy: &CopyTree, manifest: &mut MutationManifest) ->
if !copy.source.is_dir() {
bail!("copy source is not a directory: {}", copy.source.display());
}
- copy_entry(rootfs, &copy.source, copy.destination.as_path(), Path::new(""), manifest)
+ copy_entry(
+ rootfs,
+ &copy.source,
+ copy.destination.as_path(),
+ Path::new(""),
+ manifest,
+ )
}
fn copy_entry(
@@ -196,7 +202,11 @@ fn write_file(
Ok(())
}
-fn enable_service(rootfs: &Path, service: &ServiceName, manifest: &mut MutationManifest) -> Result<()> {
+fn enable_service(
+ rootfs: &Path,
+ service: &ServiceName,
+ manifest: &mut MutationManifest,
+) -> Result<()> {
let destination = RootfsPath::new(format!(
"etc/systemd/system/multi-user.target.wants/{}",
service.0
@@ -217,20 +227,28 @@ fn replace_with_file(source: &Path, output: &Path) -> Result<()> {
Ok(metadata) if metadata.file_type().is_dir() => {
bail!("cannot replace directory with file: {}", output.display());
}
- Ok(_) => fs::remove_file(output).with_context(|| format!("replace {}", output.display()))?,
+ Ok(_) => {
+ fs::remove_file(output).with_context(|| format!("replace {}", output.display()))?
+ }
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
Err(error) => return Err(error).with_context(|| format!("inspect {}", output.display())),
}
- fs::copy(source, output).with_context(|| format!("copy {} to {}", source.display(), output.display()))?;
+ fs::copy(source, output)
+ .with_context(|| format!("copy {} to {}", source.display(), output.display()))?;
Ok(())
}
fn replace_with_symlink(target: &Path, output: &Path) -> Result<()> {
match fs::symlink_metadata(output) {
Ok(metadata) if metadata.file_type().is_dir() => {
- bail!("cannot replace directory with symlink: {}", output.display());
+ bail!(
+ "cannot replace directory with symlink: {}",
+ output.display()
+ );
+ }
+ Ok(_) => {
+ fs::remove_file(output).with_context(|| format!("replace {}", output.display()))?
}
- Ok(_) => fs::remove_file(output).with_context(|| format!("replace {}", output.display()))?,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
Err(error) => return Err(error).with_context(|| format!("inspect {}", output.display())),
}