From f967d698efd44473a9285e02d0109f3bb0641622 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Wed, 12 Aug 2026 07:48:21 +0000 Subject: Format native builder and satisfy clippy --- src/archive.rs | 34 +++++--- src/build.rs | 34 ++++++-- src/cli.rs | 13 ++- src/compare.rs | 105 ++++++++++++++++++------ src/files.rs | 7 +- src/hasher.rs | 2 +- src/initramfs.rs | 45 ++++++++--- src/lib.rs | 4 +- src/main.rs | 2 +- src/manifest.rs | 202 ++++++++++++++++++++++++++++++++++++----------- src/model.rs | 8 +- src/package_installer.rs | 7 +- src/rootfs.rs | 34 ++++++-- 13 files changed, 375 insertions(+), 122 deletions(-) (limited to 'src') 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 { - 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, path: &Path, metadata: &fs::Metadata) -> Result<()> { +fn append_directory( + archive: &mut Builder, + 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 { impl BuildExecutor { 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 BuildExecutor { ) -> Result { 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 BuildExecutor { 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 { - 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( difference: impl Fn(&str, Change) -> SemanticDifference, output: &mut Vec, ) { - let left = left.iter().map(|record| (key(record), record)).collect::>(); - let right = right.iter().map(|record| (key(record), record)).collect::>(); - for name in left.keys().chain(right.keys()).copied().collect::>() { + let left = left + .iter() + .map(|record| (key(record), record)) + .collect::>(); + let right = right + .iter() + .map(|record| (key(record), record)) + .collect::>(); + for name in left + .keys() + .chain(right.keys()) + .copied() + .collect::>() + { 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) -> Result { 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>) -> Result> { +fn normalize( + kind: &str, + values: impl IntoIterator>, +) -> Result> { values .into_iter() .map(Into::into) @@ -117,8 +124,14 @@ pub struct Invocation { } impl Invocation { - pub fn new(program: impl Into, arguments: impl IntoIterator>) -> Self { - Self { program: program.into(), arguments: arguments.into_iter().map(Into::into).collect() } + pub fn new( + program: impl Into, + arguments: impl IntoIterator>, + ) -> 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) -> Result { 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) -> Result { @@ -67,9 +90,9 @@ impl ArtifactManifest { /// directory. pub fn path_beside(artifact: impl AsRef) -> Result { 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 = 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 Result> { } 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> { 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) -> Vec Result> { - 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 "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 Result { - 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 { 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 { fn portable_path(path: &Path) -> Result { 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 { 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 { - 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, version: impl Into) -> 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, digest: impl Into) -> 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, target: impl Into) -> 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, target: impl Into) -> 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, sha256: impl Into) -> 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, 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, kind: impl Into) -> 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, ©.source, copy.destination.as_path(), Path::new(""), manifest) + copy_entry( + rootfs, + ©.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())), } -- cgit v1.2.3