Improve error handling by using std lib rust code #4

Open
isa wants to merge 2 commits from isa/citadel-tools:error_handling into master
15 changed files with 372 additions and 317 deletions

View File

@ -4,7 +4,7 @@ use std::fs;
use std::thread::{self,JoinHandle}; use std::thread::{self,JoinHandle};
use std::time::{self,Instant}; use std::time::{self,Instant};
use libcitadel::{Result, UtsName, util}; use libcitadel::{UtsName, util};
use libcitadel::ResourceImage; use libcitadel::ResourceImage;
use crate::boot::disks; use crate::boot::disks;
@ -13,34 +13,33 @@ use crate::install::installer::Installer;
const IMAGE_DIRECTORY: &str = "/run/citadel/images"; const IMAGE_DIRECTORY: &str = "/run/citadel/images";
pub fn live_rootfs() -> Result<()> { pub fn live_rootfs() -> Result<(), Box<dyn std::error::Error>> {
Review

There's a common idiom for using result::Result<T,E> where you create a new type also called Result that specifies the error type you want to use.

for example:

type Result<T> = result::Result<T, Box<dyn std::error::Error>>;
There's a common idiom for using `result::Result<T,E>` where you create a new type also called `Result` that specifies the error type you want to use. for example: ```rust type Result<T> = result::Result<T, Box<dyn std::error::Error>>; ```
copy_artifacts()?; copy_artifacts()?;
let rootfs = find_rootfs_image()?; let rootfs = find_rootfs_image()?;
setup_rootfs_resource(&rootfs) setup_rootfs_resource(&rootfs)
} }
pub fn live_setup() -> Result<()> { pub fn live_setup() -> Result<(), Box<dyn std::error::Error>> {
decompress_images(true)?; decompress_images(true)?;
info!("Starting live setup"); info!("Starting live setup");
let live = Installer::new_livesetup(); let live = Installer::new_livesetup();
live.run() live.run()
} }
fn copy_artifacts() -> Result<()> { fn copy_artifacts() -> Result<(), Box<dyn std::error::Error>> {
for _ in 0..3 { for _ in 0..3 {
if try_copy_artifacts()? { if try_copy_artifacts()? {
//decompress_images()?; //decompress_images()?;
return Ok(()) return Ok(());
} }
// Try again after waiting for more devices to be discovered // Try again after waiting for more devices to be discovered
info!("Failed to find partition with images, trying again in 2 seconds"); info!("Failed to find partition with images, trying again in 2 seconds");
thread::sleep(time::Duration::from_secs(2)); thread::sleep(time::Duration::from_secs(2));
} }
bail!("could not find partition containing resource images") Result::Err("could not find partition containing resource images".into())
} }
fn try_copy_artifacts() -> Result<bool> { fn try_copy_artifacts() -> Result<bool, Box<dyn std::error::Error>> {
let rootfs_image = Path::new("/boot/images/citadel-rootfs.img"); let rootfs_image = Path::new("/boot/images/citadel-rootfs.img");
// Already mounted? // Already mounted?
if rootfs_image.exists() { if rootfs_image.exists() {
@ -60,13 +59,13 @@ fn try_copy_artifacts() -> Result<bool> {
Ok(false) Ok(false)
} }
fn kernel_version() -> String { fn kernel_version() -> Result<String, Box<dyn std::error::Error>> {
Review

What? Why does this need a Result return type?

What? Why does this need a `Result` return type?
let utsname = UtsName::uname(); let utsname = UtsName::uname();
let v = utsname.release().split('-').collect::<Vec<_>>(); let v = utsname.release().split('-').collect::<Vec<_>>();
v[0].to_string() Ok(v[0].to_string())
} }
fn deploy_artifacts() -> Result<()> { fn deploy_artifacts() -> Result<(), Box<dyn std::error::Error>> {
let run_images = Path::new(IMAGE_DIRECTORY); let run_images = Path::new(IMAGE_DIRECTORY);
if !run_images.exists() { if !run_images.exists() {
util::create_dir(run_images)?; util::create_dir(run_images)?;
@ -78,7 +77,7 @@ fn deploy_artifacts() -> Result<()> {
util::copy_file(dent.path(), run_images.join(dent.file_name())) util::copy_file(dent.path(), run_images.join(dent.file_name()))
})?; })?;
let kv = kernel_version(); let kv = kernel_version()?;
println!("Copying bzImage-{} to /run/citadel/images", kv); println!("Copying bzImage-{} to /run/citadel/images", kv);
let from = format!("/boot/bzImage-{}", kv); let from = format!("/boot/bzImage-{}", kv);
let to = format!("/run/citadel/images/bzImage-{}", kv); let to = format!("/run/citadel/images/bzImage-{}", kv);
@ -92,7 +91,7 @@ fn deploy_artifacts() -> Result<()> {
Ok(()) Ok(())
} }
fn deploy_syslinux_artifacts() -> Result<()> { fn deploy_syslinux_artifacts() -> Result<(), Box<dyn std::error::Error>> {
let boot_syslinux = Path::new("/boot/syslinux"); let boot_syslinux = Path::new("/boot/syslinux");
if !boot_syslinux.exists() { if !boot_syslinux.exists() {
@ -112,10 +111,11 @@ fn deploy_syslinux_artifacts() -> Result<()> {
} }
} }
Ok(()) Ok(())
}) })?;
Ok(())
} }
fn find_rootfs_image() -> Result<ResourceImage> { fn find_rootfs_image() -> Result<ResourceImage, Box<dyn std::error::Error>> {
let entries = fs::read_dir(IMAGE_DIRECTORY) let entries = fs::read_dir(IMAGE_DIRECTORY)
.map_err(context!("error reading directory {}", IMAGE_DIRECTORY))?; .map_err(context!("error reading directory {}", IMAGE_DIRECTORY))?;
for entry in entries { for entry in entries {
@ -123,15 +123,21 @@ fn find_rootfs_image() -> Result<ResourceImage> {
if entry.path().extension() == Some(OsStr::new("img")) { if entry.path().extension() == Some(OsStr::new("img")) {
if let Ok(image) = ResourceImage::from_path(&entry.path()) { if let Ok(image) = ResourceImage::from_path(&entry.path()) {
if image.metainfo().image_type() == "rootfs" { if image.metainfo().image_type() == "rootfs" {
return Ok(image) return Ok(image);
} }
} }
} }
} }
bail!("unable to find rootfs resource image in {}", IMAGE_DIRECTORY) Result::Err(
Review

This is an improvement over the current error handling API?

This is an improvement over the current error handling API?
format!(
"unable to find rootfs resource image in {}",
IMAGE_DIRECTORY
)
.into(),
)
} }
fn decompress_images(sync: bool) -> Result<()> { fn decompress_images(sync: bool) -> Result<(), Box<dyn std::error::Error>> {
info!("Decompressing images"); info!("Decompressing images");
let mut threads = Vec::new(); let mut threads = Vec::new();
util::read_directory("/run/citadel/images", |dent| { util::read_directory("/run/citadel/images", |dent| {
@ -140,9 +146,8 @@ fn decompress_images(sync: bool) -> Result<()> {
if image.is_compressed() { if image.is_compressed() {
if sync { if sync {
if let Err(err) = decompress_one_image_sync(image) { if let Err(err) = decompress_one_image_sync(image) {
warn!("Error: {}", err); warn!("Error decompressing image: {}", err);
} }
} else { } else {
threads.push(decompress_one_image(image)); threads.push(decompress_one_image(image));
} }
@ -161,7 +166,9 @@ fn decompress_images(sync: bool) -> Result<()> {
Ok(()) Ok(())
} }
fn decompress_one_image_sync(image: ResourceImage) -> Result<()> { fn decompress_one_image_sync(
image: ResourceImage,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let start = Instant::now(); let start = Instant::now();
info!("Decompressing {}", image.path().display()); info!("Decompressing {}", image.path().display());
image.decompress(true) image.decompress(true)
@ -173,8 +180,7 @@ fn decompress_one_image_sync(image: ResourceImage) -> Result<()> {
Ok(()) Ok(())
} }
fn decompress_one_image(image: ResourceImage) -> JoinHandle<Result<()>> { fn decompress_one_image(image: ResourceImage,) ->
thread::spawn(move || { JoinHandle<Result<(), Box<dyn std::error::Error + Send + Sync>>> {
decompress_one_image_sync(image) thread::spawn(move || decompress_one_image_sync(image))
})
} }

View File

@ -1,7 +1,6 @@
use std::fs; use std::fs;
use std::process::exit;
use libcitadel::{Result, ResourceImage, CommandLine, KeyRing, LogLevel, Logger, util}; use libcitadel::{ResourceImage, CommandLine, KeyRing, LogLevel, Logger, util};
use libcitadel::RealmManager; use libcitadel::RealmManager;
use crate::boot::disks::DiskPartition; use crate::boot::disks::DiskPartition;
use std::path::Path; use std::path::Path;
@ -10,44 +9,40 @@ mod live;
mod disks; mod disks;
mod rootfs; mod rootfs;
pub fn main(args: Vec<String>) { pub fn main(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
if CommandLine::debug() { if CommandLine::debug() {
Logger::set_log_level(LogLevel::Debug); Logger::set_log_level(LogLevel::Debug);
} else if CommandLine::verbose() { } else if CommandLine::verbose() {
Logger::set_log_level(LogLevel::Info); Logger::set_log_level(LogLevel::Info);
} }
let result = match args.get(1) { match args.get(1) {
Some(s) if s == "rootfs" => do_rootfs(), Some(s) if s == "rootfs" => do_rootfs(),
Some(s) if s == "setup" => do_setup(), Some(s) if s == "setup" => do_setup(),
Some(s) if s == "boot-automount" => do_boot_automount(), Some(s) if s == "boot-automount" => do_boot_automount(),
Some(s) if s == "start-realms" => do_start_realms(), Some(s) if s == "start-realms" => do_start_realms(),
_ => Err(format_err!("Bad or missing argument").into()), _ => Err(format_err!("Bad or missing argument").into()),
Review

Ooops, you forgot to replace a libcitadel::error::Error the 'improved' version and everything still works when you return it. Are you still sure rewriting everything was what you needed to do?

Ooops, you forgot to replace a `libcitadel::error::Error` the 'improved' version and everything still works when you return it. Are you still sure rewriting everything was what you needed to do?
}; }?;
if let Err(ref e) = result { Ok(())
warn!("Failed: {}", e);
exit(1);
Review

Is rewriting this to not return a specific exit code the best plan? I don't know if this ever worked as intended to cause the corresponding systemd units to fail and then bring the system into a state where the problem can be understood and diagnosed, but now the utility has an undefined exit code that we may end up relying on.

Is rewriting this to not return a specific exit code the best plan? I don't know if this ever worked as intended to cause the corresponding systemd units to fail and then bring the system into a state where the problem can be understood and diagnosed, but now the utility has an undefined exit code that we may end up relying on.
}
} }
fn do_rootfs() -> Result<(), Box<dyn std::error::Error>> {
fn do_rootfs() -> Result<()> {
if CommandLine::live_mode() || CommandLine::install_mode() { if CommandLine::live_mode() || CommandLine::install_mode() {
live::live_rootfs() live::live_rootfs()
} else { } else {
rootfs::setup_rootfs() Ok(rootfs::setup_rootfs()?)
Review

No, don't do that. It misleads reader to think there is a return value from both of these functions.

Do this:

rootfs::setup_rootfs()?;
Ok(())

No, don't do that. It misleads reader to think there is a return value from both of these functions. Do this: ```rust rootfs::setup_rootfs()?; Ok(()) ```
} }
} }
fn setup_keyring() -> Result<()> { fn setup_keyring() -> Result<(), Box<dyn std::error::Error>> {
ResourceImage::ensure_storage_mounted()?; ResourceImage::ensure_storage_mounted()?;
let keyring = KeyRing::load_with_cryptsetup_passphrase("/sysroot/storage/keyring")?; let keyring = KeyRing::load_with_cryptsetup_passphrase("/sysroot/storage/keyring")?;
keyring.add_keys_to_kernel()?; keyring.add_keys_to_kernel()?;
Ok(()) Ok(())
} }
fn do_setup() -> Result<()> { fn do_setup() -> Result<(), Box<dyn std::error::Error>> {
if CommandLine::live_mode() || CommandLine::install_mode() { if CommandLine::live_mode() || CommandLine::install_mode() {
live::live_setup()?; live::live_setup()?;
} else if let Err(err) = setup_keyring() { } else if let Err(err) = setup_keyring() {
@ -64,8 +59,7 @@ fn do_setup() -> Result<()> {
Ok(()) Ok(())
} }
fn mount_overlay() -> Result<(), Box<dyn std::error::Error>> {
fn mount_overlay() -> Result<()> {
info!("Creating rootfs overlay"); info!("Creating rootfs overlay");
info!("Moving /sysroot mount to /rootfs.ro"); info!("Moving /sysroot mount to /rootfs.ro");
@ -89,13 +83,13 @@ fn mount_overlay() -> Result<()> {
Ok(()) Ok(())
} }
fn do_start_realms() -> Result<()> { fn do_start_realms() -> Result<(), Box<dyn std::error::Error>> {
let manager = RealmManager::load()?; let manager = RealmManager::load()?;
manager.start_boot_realms() Ok(manager.start_boot_realms()?)
} }
// Write automount unit for /boot partition // Write automount unit for /boot partition
fn do_boot_automount() -> Result<()> { fn do_boot_automount() -> Result<(), Box<dyn std::error::Error>> {
Logger::set_log_level(LogLevel::Info); Logger::set_log_level(LogLevel::Info);
if CommandLine::live_mode() || CommandLine::install_mode() { if CommandLine::live_mode() || CommandLine::install_mode() {
@ -105,10 +99,10 @@ fn do_boot_automount() -> Result<()> {
let boot_partition = find_boot_partition()?; let boot_partition = find_boot_partition()?;
info!("Creating /boot automount units for boot partition {}", boot_partition); info!("Creating /boot automount units for boot partition {}", boot_partition);
cmd!("/usr/bin/systemd-mount", "-A --timeout-idle-sec=300 {} /boot", boot_partition) Ok(cmd!("/usr/bin/systemd-mount", "-A --timeout-idle-sec=300 {} /boot", boot_partition)?)
} }
fn find_boot_partition() -> Result<String> { fn find_boot_partition() -> Result<String, Box<dyn std::error::Error>> {
let loader_dev = read_loader_dev_efi_var()?; let loader_dev = read_loader_dev_efi_var()?;
let boot_partitions = DiskPartition::boot_partitions(true)? let boot_partitions = DiskPartition::boot_partitions(true)?
.into_iter() .into_iter()
@ -116,7 +110,7 @@ fn find_boot_partition() -> Result<String> {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if boot_partitions.len() != 1 { if boot_partitions.len() != 1 {
return Err(format_err!("Cannot uniquely determine boot partition")); return Result::Err("Cannot uniquely determine boot partition".into());
} }
Ok(boot_partitions[0].path().display().to_string()) Ok(boot_partitions[0].path().display().to_string())
@ -141,7 +135,7 @@ fn matches_loader_dev(partition: &DiskPartition, dev: &Option<String>) -> bool {
const LOADER_EFI_VAR_PATH: &str = const LOADER_EFI_VAR_PATH: &str =
"/sys/firmware/efi/efivars/LoaderDevicePartUUID-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"; "/sys/firmware/efi/efivars/LoaderDevicePartUUID-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f";
fn read_loader_dev_efi_var() -> Result<Option<String>> { fn read_loader_dev_efi_var() -> Result<Option<String>, Box<dyn std::error::Error>> {
let efi_var = Path::new(LOADER_EFI_VAR_PATH); let efi_var = Path::new(LOADER_EFI_VAR_PATH);
if efi_var.exists() { if efi_var.exists() {
let s = fs::read(efi_var) let s = fs::read(efi_var)

View File

@ -1,10 +1,10 @@
use std::path::Path; use std::path::Path;
use std::process::{Command,Stdio}; use std::process::{Command,Stdio};
use libcitadel::{BlockDev, ResourceImage, CommandLine, ImageHeader, Partition, Result, LoopDevice}; use libcitadel::{BlockDev, ResourceImage, CommandLine, ImageHeader, Partition, LoopDevice};
use libcitadel::verity::Verity; use libcitadel::verity::Verity;
pub fn setup_rootfs() -> Result<()> { pub fn setup_rootfs() -> Result<(), Box<dyn std::error::Error>> {
let mut p = choose_boot_partiton(true, CommandLine::revert_rootfs())?; let mut p = choose_boot_partiton(true, CommandLine::revert_rootfs())?;
if CommandLine::noverity() { if CommandLine::noverity() {
setup_partition_unverified(&p) setup_partition_unverified(&p)
@ -13,7 +13,7 @@ pub fn setup_rootfs() -> Result<()> {
} }
} }
pub fn setup_rootfs_resource(rootfs: &ResourceImage) -> Result<()> { pub fn setup_rootfs_resource(rootfs: &ResourceImage) -> Result<(), Box<dyn std::error::Error>> {
if CommandLine::noverity() { if CommandLine::noverity() {
setup_resource_unverified(&rootfs) setup_resource_unverified(&rootfs)
} else { } else {
@ -21,7 +21,7 @@ pub fn setup_rootfs_resource(rootfs: &ResourceImage) -> Result<()> {
} }
} }
fn setup_resource_unverified(img: &ResourceImage) -> Result<()> { fn setup_resource_unverified(img: &ResourceImage) -> Result<(), Box<dyn std::error::Error>> {
if img.is_compressed() { if img.is_compressed() {
img.decompress(false)?; img.decompress(false)?;
} }
@ -30,25 +30,31 @@ fn setup_resource_unverified(img: &ResourceImage) -> Result<()> {
setup_linear_mapping(loopdev.device()) setup_linear_mapping(loopdev.device())
} }
fn setup_resource_verified(img: &ResourceImage) -> Result<()> { fn setup_resource_verified(img: &ResourceImage) -> Result<(), Box<dyn std::error::Error>> {
let _ = img.setup_verity_device()?; let _ = img.setup_verity_device()?;
Ok(()) Ok(())
} }
fn setup_partition_unverified(p: &Partition) -> Result<()> { fn setup_partition_unverified(p: &Partition) -> Result<(), Box<dyn std::error::Error>> {
info!("Creating /dev/mapper/rootfs device with linear device mapping of partition (no verity)"); info!("Creating /dev/mapper/rootfs device with linear device mapping of partition (no verity)");
setup_linear_mapping(p.path()) setup_linear_mapping(p.path())
} }
fn setup_partition_verified(p: &mut Partition) -> Result<()> { fn setup_partition_verified(p: &mut Partition) -> Result<(), Box<dyn std::error::Error>> {
info!("Creating /dev/mapper/rootfs dm-verity device"); info!("Creating /dev/mapper/rootfs dm-verity device");
if !CommandLine::nosignatures() { if !CommandLine::nosignatures() {
if !p.has_public_key() { if !p.has_public_key() {
bail!("no public key available for channel {}", p.metainfo().channel()) return Result::Err(
format!(
"no public key available for channel {}",
p.metainfo().channel()
)
.into(),
);
} }
if !p.is_signature_valid() { if !p.is_signature_valid() {
p.write_status(ImageHeader::STATUS_BAD_SIG)?; p.write_status(ImageHeader::STATUS_BAD_SIG)?;
bail!("signature verification failed on partition"); return Result::Err("signature verification failed on partition".into());
} }
info!("Image signature is valid for channel {}", p.metainfo().channel()); info!("Image signature is valid for channel {}", p.metainfo().channel());
} }
@ -56,7 +62,7 @@ fn setup_partition_verified(p: &mut Partition) -> Result<()> {
Ok(()) Ok(())
} }
fn setup_linear_mapping(blockdev: &Path) -> Result<()> { fn setup_linear_mapping(blockdev: &Path) -> Result<(), Box<dyn std::error::Error>> {
let dev = BlockDev::open_ro(blockdev)?; let dev = BlockDev::open_ro(blockdev)?;
let table = format!("0 {} linear {} 0", dev.nsectors()?, blockdev.display()); let table = format!("0 {} linear {} 0", dev.nsectors()?, blockdev.display());
@ -70,7 +76,9 @@ fn setup_linear_mapping(blockdev: &Path) -> Result<()> {
.success(); .success();
if !ok { if !ok {
bail!("failed to set up linear identity mapping with /usr/sbin/dmsetup"); return Result::Err(
"failed to set up linear identity mapping with /usr/sbin/dmsetup".into(),
);
} }
Ok(()) Ok(())
} }
@ -94,7 +102,8 @@ fn choose_revert_partition(best: Option<Partition>) -> Option<Partition> {
best best
} }
fn choose_boot_partiton(scan: bool, revert_rootfs: bool) -> Result<Partition> { fn choose_boot_partiton(scan: bool, revert_rootfs: bool,
) -> Result<Partition, Box<dyn std::error::Error>> {
let mut partitions = Partition::rootfs_partitions()?; let mut partitions = Partition::rootfs_partitions()?;
if scan { if scan {
@ -136,14 +145,17 @@ fn compare_boot_partitions(a: Option<Partition>, b: Partition) -> Option<Partiti
} }
// Compare versions and channels // Compare versions and channels
let a_v = a.metainfo().version(); let meta_a = a.metainfo();
Review

Why did you rewrite this? Why is this buried in a massive commit described vaguely as "improve error handling"?

Why did you rewrite this? Why is this buried in a massive commit described vaguely as "improve error handling"?
let b_v = b.metainfo().version(); let meta_b = b.metainfo();
let ver_a = meta_a.version();
let ver_b = meta_b.version();
// Compare versions only if channels match // Compare versions only if channels match
if a.metainfo().channel() == b.metainfo().channel() { if a.metainfo().channel() == b.metainfo().channel() {
if a_v > b_v { if ver_a > ver_b {
return Some(a); return Some(a);
} else if b_v > a_v { } else if ver_b > ver_a {
return Some(b); return Some(b);
} }
} }

View File

@ -3,11 +3,10 @@ use std::process::exit;
use clap::{App,Arg,SubCommand,ArgMatches}; use clap::{App,Arg,SubCommand,ArgMatches};
use clap::AppSettings::*; use clap::AppSettings::*;
use libcitadel::{Result, ResourceImage, Logger, LogLevel, Partition, KeyPair, ImageHeader, util}; use libcitadel::{ResourceImage, Logger, LogLevel, Partition, KeyPair, ImageHeader, util};
use hex; use hex;
pub fn main(args: Vec<String>) { pub fn main(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
let app = App::new("citadel-image") let app = App::new("citadel-image")
.about("Citadel update image builder") .about("Citadel update image builder")
.settings(&[ArgRequiredElseHelp,ColoredHelp, DisableHelpSubcommand, DisableVersion, DeriveDisplayOrder]) .settings(&[ArgRequiredElseHelp,ColoredHelp, DisableHelpSubcommand, DisableVersion, DeriveDisplayOrder])
@ -91,16 +90,18 @@ pub fn main(args: Vec<String>) {
println!("Error: {}", e); println!("Error: {}", e);
exit(1); exit(1);
} }
Ok(())
} }
fn info(arg_matches: &ArgMatches) -> Result<()> { fn info(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = load_image(arg_matches)?; let img = load_image(arg_matches)?;
print!("{}",String::from_utf8(img.header().metainfo_bytes())?); print!("{}", String::from_utf8(img.header().metainfo_bytes())?);
info_signature(&img)?; info_signature(&img)?;
Ok(()) Ok(())
} }
fn info_signature(img: &ResourceImage) -> Result<()> { fn info_signature(img: &ResourceImage) -> Result<(), Box<dyn std::error::Error>> {
if img.header().has_signature() { if img.header().has_signature() {
println!("Signature: {}", hex::encode(&img.header().signature())); println!("Signature: {}", hex::encode(&img.header().signature()));
} else { } else {
@ -118,13 +119,13 @@ fn info_signature(img: &ResourceImage) -> Result<()> {
} }
Ok(()) Ok(())
} }
fn metainfo(arg_matches: &ArgMatches) -> Result<()> { fn metainfo(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = load_image(arg_matches)?; let img = load_image(arg_matches)?;
print!("{}",String::from_utf8(img.header().metainfo_bytes())?); print!("{}", String::from_utf8(img.header().metainfo_bytes())?);
Ok(()) Ok(())
} }
fn generate_verity(arg_matches: &ArgMatches) -> Result<()> { fn generate_verity(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = load_image(arg_matches)?; let img = load_image(arg_matches)?;
if img.has_verity_hashtree() { if img.has_verity_hashtree() {
info!("Image already has dm-verity hashtree appended, doing nothing."); info!("Image already has dm-verity hashtree appended, doing nothing.");
@ -134,7 +135,7 @@ fn generate_verity(arg_matches: &ArgMatches) -> Result<()> {
Ok(()) Ok(())
} }
fn verify(arg_matches: &ArgMatches) -> Result<()> { fn verify(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = load_image(arg_matches)?; let img = load_image(arg_matches)?;
let ok = img.verify_verity()?; let ok = img.verify_verity()?;
if ok { if ok {
@ -145,7 +146,7 @@ fn verify(arg_matches: &ArgMatches) -> Result<()> {
Ok(()) Ok(())
} }
fn verify_shasum(arg_matches: &ArgMatches) -> Result<()> { fn verify_shasum(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = load_image(arg_matches)?; let img = load_image(arg_matches)?;
let shasum = img.generate_shasum()?; let shasum = img.generate_shasum()?;
if shasum == img.metainfo().shasum() { if shasum == img.metainfo().shasum() {
@ -158,22 +159,22 @@ fn verify_shasum(arg_matches: &ArgMatches) -> Result<()> {
Ok(()) Ok(())
} }
fn load_image(arg_matches: &ArgMatches) -> Result<ResourceImage> { fn load_image(arg_matches: &ArgMatches) -> Result<ResourceImage, Box<dyn std::error::Error>> {
let path = arg_matches.value_of("path").expect("path argument missing"); let path = arg_matches.value_of("path").expect("path argument missing");
if !Path::new(path).exists() { if !Path::new(path).exists() {
bail!("Cannot load image {}: File does not exist", path); panic!("Cannot load image {}: File does not exist", path);
Review

Uh..... you want to panic instead of just exiting cleanly and printing an error message?

Uh..... you want to panic instead of just exiting cleanly and printing an error message?
} }
let img = ResourceImage::from_path(path)?; let img = ResourceImage::from_path(path)?;
if !img.is_valid_image() { if !img.is_valid_image() {
bail!("File {} is not a valid image file", path); panic!("File {} is not a valid image file", path);
} }
Ok(img) Ok(img)
} }
fn install_rootfs(arg_matches: &ArgMatches) -> Result<()> { fn install_rootfs(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
if arg_matches.is_present("choose") { if arg_matches.is_present("choose") {
let _ = choose_install_partition(true)?; let _ = choose_install_partition(true)?;
return Ok(()) return Ok(());
} }
let img = load_image(arg_matches)?; let img = load_image(arg_matches)?;
@ -182,7 +183,7 @@ fn install_rootfs(arg_matches: &ArgMatches) -> Result<()> {
info!("Verifying sha256 hash of image"); info!("Verifying sha256 hash of image");
let shasum = img.generate_shasum()?; let shasum = img.generate_shasum()?;
if shasum != img.metainfo().shasum() { if shasum != img.metainfo().shasum() {
bail!("image file does not have expected sha256 value"); panic!("image file does not have expected sha256 value");
} }
} }
@ -196,7 +197,7 @@ fn install_rootfs(arg_matches: &ArgMatches) -> Result<()> {
Ok(()) Ok(())
} }
fn clear_prefer_boot() -> Result<()> { fn clear_prefer_boot() -> Result<(), Box<dyn std::error::Error>> {
for mut p in Partition::rootfs_partitions()? { for mut p in Partition::rootfs_partitions()? {
if p.is_initialized() && p.header().has_flag(ImageHeader::FLAG_PREFER_BOOT) { if p.is_initialized() && p.header().has_flag(ImageHeader::FLAG_PREFER_BOOT) {
p.clear_flag_and_write(ImageHeader::FLAG_PREFER_BOOT)?; p.clear_flag_and_write(ImageHeader::FLAG_PREFER_BOOT)?;
@ -205,13 +206,13 @@ fn clear_prefer_boot() -> Result<()> {
Ok(()) Ok(())
} }
fn sign_image(arg_matches: &ArgMatches) -> Result<()> { fn sign_image(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let _img = load_image(arg_matches)?; let _img = load_image(arg_matches)?;
info!("Not implemented yet"); info!("Not implemented yet");
Ok(()) Ok(())
} }
fn install_image(arg_matches: &ArgMatches) -> Result<()> { fn install_image(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let source = arg_matches.value_of("path").expect("path argument missing"); let source = arg_matches.value_of("path").expect("path argument missing");
let img = load_image(arg_matches)?; let img = load_image(arg_matches)?;
let _hdr = img.header(); let _hdr = img.header();
@ -220,12 +221,12 @@ fn install_image(arg_matches: &ArgMatches) -> Result<()> {
// XXX verify signature? // XXX verify signature?
if !(metainfo.image_type() == "kernel" || metainfo.image_type() == "extra") { if !(metainfo.image_type() == "kernel" || metainfo.image_type() == "extra") {
bail!("Cannot install image type {}", metainfo.image_type()); panic!("Cannot install image type {}", metainfo.image_type());
} }
let shasum = img.generate_shasum()?; let shasum = img.generate_shasum()?;
if shasum != img.metainfo().shasum() { if shasum != img.metainfo().shasum() {
bail!("Image shasum does not match metainfo"); panic!("Image shasum does not match metainfo");
} }
img.generate_verity_hashtree()?; img.generate_verity_hashtree()?;
@ -233,10 +234,10 @@ fn install_image(arg_matches: &ArgMatches) -> Result<()> {
let filename = if metainfo.image_type() == "kernel" { let filename = if metainfo.image_type() == "kernel" {
let kernel_version = match metainfo.kernel_version() { let kernel_version = match metainfo.kernel_version() {
Some(version) => version, Some(version) => version,
None => bail!("Kernel image does not have a kernel version field in metainfo"), None => panic!("Kernel image does not have a kernel version field in metainfo"),
}; };
if kernel_version.chars().any(|c| c == '/') { if kernel_version.chars().any(|c| c == '/') {
bail!("Kernel version field has / char"); panic!("Kernel version field has / char");
} }
format!("citadel-kernel-{}-{:03}.img", kernel_version, metainfo.version()) format!("citadel-kernel-{}-{:03}.img", kernel_version, metainfo.version())
} else { } else {
@ -244,33 +245,38 @@ fn install_image(arg_matches: &ArgMatches) -> Result<()> {
}; };
if !metainfo.channel().chars().all(|c| c.is_ascii_lowercase()) { if !metainfo.channel().chars().all(|c| c.is_ascii_lowercase()) {
bail!("Refusing to build path from strange channel name {}", metainfo.channel()); panic!(
"Refusing to build path from strange channel name {}",
metainfo.channel()
);
} }
let image_dir = Path::new("/storage/resources").join(metainfo.channel()); let image_dir = Path::new("/storage/resources").join(metainfo.channel());
let image_dest = image_dir.join(filename); let image_dest = image_dir.join(filename);
if image_dest.exists() { if image_dest.exists() {
rotate(&image_dest)?; rotate(&image_dest)?;
} }
util::rename(source, &image_dest) util::rename(source, &image_dest)?;
Ok(())
} }
fn rotate(path: &Path) -> Result<()> { fn rotate(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
if !path.exists() || path.file_name().is_none() { if !path.exists() || path.file_name().is_none() {
return Ok(()); return Ok(());
} }
let filename = path.file_name().unwrap(); let filename = path.file_name().unwrap();
let dot_zero = path.with_file_name(format!("{}.0", filename.to_string_lossy())); let dot_zero = path.with_file_name(format!("{}.0", filename.to_string_lossy()));
util::remove_file(&dot_zero)?; util::remove_file(&dot_zero)?;
util::rename(path, &dot_zero) util::rename(path, &dot_zero).unwrap();
Ok(())
} }
fn genkeys() -> Result<()> { fn genkeys() -> Result<(), Box<dyn std::error::Error>> {
let keypair = KeyPair::generate(); let keypair = KeyPair::generate();
println!("keypair = \"{}\"", keypair.to_hex()); println!("keypair = \"{}\"", keypair.to_hex());
Ok(()) Ok(())
} }
fn decompress(arg_matches: &ArgMatches) -> Result<()> { fn decompress(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = load_image(arg_matches)?; let img = load_image(arg_matches)?;
if !img.is_compressed() { if !img.is_compressed() {
info!("Image is not compressed, not decompressing."); info!("Image is not compressed, not decompressing.");
@ -280,7 +286,7 @@ fn decompress(arg_matches: &ArgMatches) -> Result<()> {
Ok(()) Ok(())
} }
fn bless() -> Result<()> { fn bless() -> Result<(), Box<dyn std::error::Error>> {
for mut p in Partition::rootfs_partitions()? { for mut p in Partition::rootfs_partitions()? {
if p.is_initialized() && p.is_mounted() { if p.is_initialized() && p.is_mounted() {
p.bless()?; p.bless()?;
@ -299,7 +305,7 @@ fn bool_to_yesno(val: bool) -> &'static str {
} }
} }
fn choose_install_partition(verbose: bool) -> Result<Partition> { fn choose_install_partition(verbose: bool) -> Result<Partition, Box<dyn std::error::Error>> {
let partitions = Partition::rootfs_partitions()?; let partitions = Partition::rootfs_partitions()?;
if verbose { if verbose {
@ -314,9 +320,12 @@ fn choose_install_partition(verbose: bool) -> Result<Partition> {
for p in &partitions { for p in &partitions {
if !p.is_mounted() && !p.is_initialized() { if !p.is_mounted() && !p.is_initialized() {
if verbose { if verbose {
info!("Choosing {} because it is empty and not mounted", p.path().display()); info!(
"Choosing {} because it is empty and not mounted",
p.path().display()
);
} }
return Ok(p.clone()) return Ok(p.clone());
} }
} }
for p in &partitions { for p in &partitions {
@ -324,10 +333,10 @@ fn choose_install_partition(verbose: bool) -> Result<Partition> {
if verbose { if verbose {
info!("Choosing {} because it is not mounted", p.path().display()); info!("Choosing {} because it is not mounted", p.path().display());
info!("Header metainfo:"); info!("Header metainfo:");
print!("{}",String::from_utf8(p.header().metainfo_bytes())?); print!("{}", String::from_utf8(p.header().metainfo_bytes())?);
} }
return Ok(p.clone()) return Ok(p.clone());
} }
} }
bail!("No suitable install partition found") panic!("No suitable install partition found")
} }

View File

@ -1,6 +1,5 @@
use std::io::{self,Write}; use std::io::{self,Write};
use std::path::Path; use std::path::Path;
use libcitadel::Result;
use super::disk::Disk; use super::disk::Disk;
use rpassword; use rpassword;
use crate::install::installer::Installer; use crate::install::installer::Installer;
@ -8,7 +7,7 @@ use crate::install::installer::Installer;
const CITADEL_PASSPHRASE_PROMPT: &str = "Enter a password for the Citadel user (or 'q' to quit)"; const CITADEL_PASSPHRASE_PROMPT: &str = "Enter a password for the Citadel user (or 'q' to quit)";
const LUKS_PASSPHRASE_PROMPT: &str = "Enter a disk encryption passphrase (or 'q' to quit"; const LUKS_PASSPHRASE_PROMPT: &str = "Enter a disk encryption passphrase (or 'q' to quit";
pub fn run_cli_install() -> Result<bool> { pub fn run_cli_install() -> Result<bool, Box<dyn std::error::Error>> {
let disk = match choose_disk()? { let disk = match choose_disk()? {
Some(disk) => disk, Some(disk) => disk,
None => return Ok(false), None => return Ok(false),
@ -33,7 +32,7 @@ pub fn run_cli_install() -> Result<bool> {
Ok(true) Ok(true)
} }
pub fn run_cli_install_with<P: AsRef<Path>>(target: P) -> Result<bool> { pub fn run_cli_install_with<P: AsRef<Path>>(target: P) -> Result<bool, Box<dyn std::error::Error>> {
let disk = find_disk_by_path(target.as_ref())?; let disk = find_disk_by_path(target.as_ref())?;
display_disk(&disk); display_disk(&disk);
@ -55,11 +54,15 @@ pub fn run_cli_install_with<P: AsRef<Path>>(target: P) -> Result<bool> {
Ok(true) Ok(true)
} }
fn run_install(disk: Disk, citadel_passphrase: String, passphrase: String) -> Result<()> { fn run_install(
disk: Disk,
citadel_passphrase: String,
passphrase: String,
) -> Result<(), Box<dyn std::error::Error>> {
let mut install = Installer::new(disk.path(), &citadel_passphrase, &passphrase); let mut install = Installer::new(disk.path(), &citadel_passphrase, &passphrase);
install.set_install_syslinux(true); install.set_install_syslinux(true);
install.verify()?; install.verify()?;
install.run() Ok(install.run()?)
} }
fn display_disk(disk: &Disk) { fn display_disk(disk: &Disk) {
@ -70,22 +73,22 @@ fn display_disk(disk: &Disk) {
println!(); println!();
} }
fn find_disk_by_path(path: &Path) -> Result<Disk> { fn find_disk_by_path(path: &Path) -> Result<Disk, Box<dyn std::error::Error>> {
if !path.exists() { if !path.exists() {
bail!("Target disk path {} does not exist", path.display()); panic!("Target disk path {} does not exist", path.display());
} }
for disk in Disk::probe_all()? { for disk in Disk::probe_all()? {
if disk.path() == path { if disk.path() == path {
return Ok(disk.clone()); return Ok(disk.clone());
} }
} }
bail!("installation target {} is not a valid disk", path.display()) panic!("installation target {} is not a valid disk", path.display())
} }
fn choose_disk() -> Result<Option<Disk>> { fn choose_disk() -> Result<Option<Disk>, Box<dyn std::error::Error>> {
let disks = Disk::probe_all()?; let disks = Disk::probe_all()?;
if disks.is_empty() { if disks.is_empty() {
bail!("no disks found."); panic!("no disks found.");
} }
loop { loop {
@ -96,7 +99,7 @@ fn choose_disk() -> Result<Option<Disk>> {
} }
if let Ok(n) = line.parse::<usize>() { if let Ok(n) = line.parse::<usize>() {
if n > 0 && n <= disks.len() { if n > 0 && n <= disks.len() {
return Ok(Some(disks[n-1].clone())); return Ok(Some(disks[n - 1].clone()));
} }
} }
} }
@ -111,7 +114,7 @@ fn prompt_choose_disk(disks: &[Disk]) {
let _ = io::stdout().flush(); let _ = io::stdout().flush();
} }
fn read_line() -> Result<String> { fn read_line() -> Result<String, Box<dyn std::error::Error>> {
let mut input = String::new(); let mut input = String::new();
io::stdin().read_line(&mut input) io::stdin().read_line(&mut input)
.map_err(context!("error reading line from stdin"))?; .map_err(context!("error reading line from stdin"))?;
@ -146,7 +149,7 @@ fn read_passphrase(prompt: &str) -> io::Result<Option<String>> {
} }
} }
fn confirm_install(disk: &Disk) -> Result<bool> { fn confirm_install(disk: &Disk) -> Result<bool, Box<dyn std::error::Error>> {
println!("Are you sure you want to completely erase this this device?"); println!("Are you sure you want to completely erase this this device?");
println!(); println!();
println!(" Device: {}", disk.path().display()); println!(" Device: {}", disk.path().display());
@ -158,4 +161,3 @@ fn confirm_install(disk: &Disk) -> Result<bool> {
let answer = read_line()?; let answer = read_line()?;
Ok(answer == "YES") Ok(answer == "YES")
} }

View File

@ -9,7 +9,6 @@ use pwhash::sha512_crypt;
use libcitadel::util; use libcitadel::util;
use libcitadel::RealmFS; use libcitadel::RealmFS;
use libcitadel::Result;
use libcitadel::OsRelease; use libcitadel::OsRelease;
use libcitadel::KeyRing; use libcitadel::KeyRing;
use libcitadel::terminal::Base16Scheme; use libcitadel::terminal::Base16Scheme;
@ -183,7 +182,7 @@ impl Installer {
self.install_syslinux = val; self.install_syslinux = val;
} }
pub fn verify(&self) -> Result<()> { pub fn verify(&self) -> Result<(), Box<dyn std::error::Error>> {
let kernel_img = self.kernel_imagename(); let kernel_img = self.kernel_imagename();
let bzimage = format!("bzImage-{}", self.kernel_version()); let bzimage = format!("bzImage-{}", self.kernel_version());
let artifacts = vec![ let artifacts = vec![
@ -192,26 +191,29 @@ impl Installer {
]; ];
if !self.target().exists() { if !self.target().exists() {
bail!("target device {:?} does not exist", self.target()); panic!("target device {:?} does not exist", self.target());
} }
for a in artifacts { for a in artifacts {
if !self.artifact_path(a).exists() { if !self.artifact_path(a).exists() {
bail!("required install artifact {} does not exist in {}", a, self.artifact_directory); panic!(
"required install artifact {} does not exist in {}",
a, self.artifact_directory
);
} }
} }
Ok(()) Ok(())
} }
pub fn run(&self) -> Result<()> { pub fn run(&self) -> Result<(), Box<dyn std::error::Error>> {
match self._type { match self._type {
InstallType::Install => self.run_install(), InstallType::Install => self.run_install(),
InstallType::LiveSetup => self.run_live_setup(), InstallType::LiveSetup => self.run_live_setup(),
} }
} }
pub fn run_install(&self) -> Result<()> { pub fn run_install(&self) -> Result<(), Box<dyn std::error::Error>> {
let start = Instant::now(); let start = Instant::now();
self.partition_disk()?; self.partition_disk()?;
self.setup_luks()?; self.setup_luks()?;
@ -224,7 +226,7 @@ impl Installer {
Ok(()) Ok(())
} }
pub fn run_live_setup(&self) -> Result<()> { pub fn run_live_setup(&self) -> Result<(), Box<dyn std::error::Error>> {
self.cmd_list(&[ self.cmd_list(&[
"/bin/mount -t tmpfs var-tmpfs /sysroot/var", "/bin/mount -t tmpfs var-tmpfs /sysroot/var",
"/bin/mount -t tmpfs home-tmpfs /sysroot/home", "/bin/mount -t tmpfs home-tmpfs /sysroot/home",
@ -242,8 +244,7 @@ impl Installer {
Ok(()) Ok(())
} }
fn setup_live_realm(&self) -> Result<()> { fn setup_live_realm(&self) -> Result<(), Box<dyn std::error::Error>> {
let realmfs_dir = self.storage().join("realms/realmfs-images"); let realmfs_dir = self.storage().join("realms/realmfs-images");
let base_realmfs = realmfs_dir.join("base-realmfs.img"); let base_realmfs = realmfs_dir.join("base-realmfs.img");
@ -261,14 +262,14 @@ impl Installer {
Ok(()) Ok(())
} }
pub fn partition_disk(&self) -> Result<()> { pub fn partition_disk(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Partitioning target disk")?; self.header("Partitioning target disk")?;
self.cmd_list(PARTITION_COMMANDS, &[ self.cmd_list(PARTITION_COMMANDS, &[
("$TARGET", self.target_str()) ("$TARGET", self.target_str())
]) ])
} }
pub fn setup_luks(&self) -> Result<()> { pub fn setup_luks(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Setting up LUKS disk encryption")?; self.header("Setting up LUKS disk encryption")?;
util::create_dir(INSTALL_MOUNT)?; util::create_dir(INSTALL_MOUNT)?;
util::write_file(LUKS_PASSPHRASE_FILE, self.passphrase().as_bytes())?; util::write_file(LUKS_PASSPHRASE_FILE, self.passphrase().as_bytes())?;
@ -281,15 +282,16 @@ impl Installer {
("$LUKS_PASSFILE", LUKS_PASSPHRASE_FILE), ("$LUKS_PASSFILE", LUKS_PASSPHRASE_FILE),
])?; ])?;
util::remove_file(LUKS_PASSPHRASE_FILE) util::remove_file(LUKS_PASSPHRASE_FILE)?;
Ok(())
} }
pub fn setup_lvm(&self) -> Result<()> { pub fn setup_lvm(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Setting up LVM volumes")?; self.header("Setting up LVM volumes")?;
self.cmd_list(LVM_COMMANDS, &[]) self.cmd_list(LVM_COMMANDS, &[])
} }
pub fn setup_boot(&self) -> Result<()> { pub fn setup_boot(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Setting up /boot partition")?; self.header("Setting up /boot partition")?;
let boot_partition = self.target_partition(1); let boot_partition = self.target_partition(1);
self.cmd(format!("/sbin/mkfs.vfat -F 32 {}", boot_partition))?; self.cmd(format!("/sbin/mkfs.vfat -F 32 {}", boot_partition))?;
@ -323,11 +325,11 @@ impl Installer {
Ok(()) Ok(())
} }
fn setup_syslinux(&self) -> Result<()> { fn setup_syslinux(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Installing syslinux")?; self.header("Installing syslinux")?;
let syslinux_src = self.artifact_path("syslinux"); let syslinux_src = self.artifact_path("syslinux");
if !syslinux_src.exists() { if !syslinux_src.exists() {
bail!("no syslinux directory found in artifact directory, cannot install syslinux"); panic!("no syslinux directory found in artifact directory, cannot install syslinux");
} }
let dst = Path::new(INSTALL_MOUNT).join("syslinux"); let dst = Path::new(INSTALL_MOUNT).join("syslinux");
util::create_dir(&dst)?; util::create_dir(&dst)?;
@ -345,17 +347,17 @@ impl Installer {
self.cmd(format!("/sbin/extlinux --install {}", dst.display())) self.cmd(format!("/sbin/extlinux --install {}", dst.display()))
} }
fn setup_syslinux_post_umount(&self) -> Result<()> { fn setup_syslinux_post_umount(&self) -> Result<(), Box<dyn std::error::Error>> {
let mbrbin = self.artifact_path("syslinux/gptmbr.bin"); let mbrbin = self.artifact_path("syslinux/gptmbr.bin");
if !mbrbin.exists() { if !mbrbin.exists() {
bail!("could not find MBR image: {:?}", mbrbin); panic!("could not find MBR image: {:?}", mbrbin);
} }
self.cmd(format!("/bin/dd bs=440 count=1 conv=notrunc if={} of={}", mbrbin.display(), self.target().display()))?; self.cmd(format!("/bin/dd bs=440 count=1 conv=notrunc if={} of={}", mbrbin.display(), self.target().display()))?;
self.cmd(format!("/sbin/parted -s {} set 1 legacy_boot on", self.target_str())) self.cmd(format!("/sbin/parted -s {} set 1 legacy_boot on", self.target_str()))
} }
pub fn create_storage(&self) -> Result<()> { pub fn create_storage(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Setting up /storage partition")?; self.header("Setting up /storage partition")?;
self.cmd_list(CREATE_STORAGE_COMMANDS, self.cmd_list(CREATE_STORAGE_COMMANDS,
@ -365,7 +367,7 @@ impl Installer {
self.cmd(format!("/bin/umount {}", INSTALL_MOUNT)) self.cmd(format!("/bin/umount {}", INSTALL_MOUNT))
} }
fn setup_storage(&self) -> Result<()> { fn setup_storage(&self) -> Result<(), Box<dyn std::error::Error>> {
if self._type == InstallType::Install { if self._type == InstallType::Install {
self.create_keyring()?; self.create_keyring()?;
self.setup_storage_resources()?; self.setup_storage_resources()?;
@ -389,33 +391,33 @@ impl Installer {
Ok(()) Ok(())
} }
fn create_keyring(&self) -> Result<()> { fn create_keyring(&self) -> Result<(), Box<dyn std::error::Error>> {
self.info("Creating initial keyring")?; self.info("Creating initial keyring")?;
let keyring = KeyRing::create_new(); let keyring = KeyRing::create_new();
keyring.write(self.storage().join("keyring"), self.passphrase.as_ref().unwrap()) Ok(keyring.write(self.storage().join("keyring"), self.passphrase.as_ref().unwrap())?)
} }
fn setup_base_realmfs(&self) -> Result<(), Box<dyn std::error::Error>> {
fn setup_base_realmfs(&self) -> Result<()> {
let realmfs_dir = self.storage().join("realms/realmfs-images"); let realmfs_dir = self.storage().join("realms/realmfs-images");
util::create_dir(&realmfs_dir)?; util::create_dir(&realmfs_dir)?;
self.sparse_copy_artifact("base-realmfs.img", &realmfs_dir)?; self.sparse_copy_artifact("base-realmfs.img", &realmfs_dir)?;
self.cmd(format!("/usr/bin/citadel-image decompress {}/base-realmfs.img", realmfs_dir.display())) self.cmd(format!("/usr/bin/citadel-image decompress {}/base-realmfs.img", realmfs_dir.display()))
} }
fn setup_realm_skel(&self) -> Result<()> { fn setup_realm_skel(&self) -> Result<(), Box<dyn std::error::Error>> {
let realm_skel = self.storage().join("realms/skel"); let realm_skel = self.storage().join("realms/skel");
util::create_dir(&realm_skel)?; util::create_dir(&realm_skel)?;
util::copy_tree_with_chown(&self.skel(), &realm_skel, (1000,1000)) util::copy_tree_with_chown(&self.skel(), &realm_skel, (1000, 1000))?;
Ok(())
} }
fn create_realmlock(&self, dir: &Path) -> Result<()> { fn create_realmlock(&self, dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
fs::File::create(dir.join(".realmlock")) fs::File::create(dir.join(".realmlock"))
.map_err(context!("failed to create {:?}/.realmlock file", dir))?; .map_err(context!("failed to create {:?}/.realmlock file", dir))?;
Ok(()) Ok(())
} }
fn setup_main_realm(&self) -> Result<()> { fn setup_main_realm(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Creating main realm")?; self.header("Creating main realm")?;
let realm = self.storage().join("realms/realm-main"); let realm = self.storage().join("realms/realm-main");
@ -440,7 +442,7 @@ impl Installer {
self.create_realmlock(&realm) self.create_realmlock(&realm)
} }
fn setup_apt_cacher_realm(&self) -> Result<()> { fn setup_apt_cacher_realm(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Creating apt-cacher realm")?; self.header("Creating apt-cacher realm")?;
let realm_base = self.storage().join("realms/realm-apt-cacher"); let realm_base = self.storage().join("realms/realm-apt-cacher");
@ -460,7 +462,7 @@ impl Installer {
self.create_realmlock(&realm_base) self.create_realmlock(&realm_base)
} }
fn setup_storage_resources(&self) -> Result<()> { fn setup_storage_resources(&self) -> Result<(), Box<dyn std::error::Error>> {
let channel = match OsRelease::citadel_channel() { let channel = match OsRelease::citadel_channel() {
Some(channel) => channel, Some(channel) => channel,
None => "dev", None => "dev",
@ -474,7 +476,7 @@ impl Installer {
self.sparse_copy_artifact(&kernel_img, &resources) self.sparse_copy_artifact(&kernel_img, &resources)
} }
fn setup_citadel_passphrase(&self) -> Result<()> { fn setup_citadel_passphrase(&self) -> Result<(), Box<dyn std::error::Error>> {
if self._type == InstallType::LiveSetup { if self._type == InstallType::LiveSetup {
self.info("Creating temporary citadel passphrase file for live mode")?; self.info("Creating temporary citadel passphrase file for live mode")?;
let path = self.storage().join("citadel-state/passwd"); let path = self.storage().join("citadel-state/passwd");
@ -497,17 +499,15 @@ impl Installer {
Ok(()) Ok(())
} }
pub fn install_rootfs_partitions(&self) -> Result<()> { pub fn install_rootfs_partitions(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Installing rootfs partitions")?; self.header("Installing rootfs partitions")?;
let rootfs = self.artifact_path("citadel-rootfs.img"); let rootfs = self.artifact_path("citadel-rootfs.img");
self.cmd(format!("/usr/bin/citadel-image install-rootfs --skip-sha {}", rootfs.display()))?; self.cmd(format!("/usr/bin/citadel-image install-rootfs --skip-sha {}", rootfs.display()))?;
self.cmd(format!("/usr/bin/citadel-image install-rootfs --skip-sha --no-prefer {}", rootfs.display())) self.cmd(format!("/usr/bin/citadel-image install-rootfs --skip-sha --no-prefer {}", rootfs.display()))
} }
pub fn finish_install(&self) -> Result<()> { pub fn finish_install(&self) -> Result<(), Box<dyn std::error::Error>> {
self.cmd_list(FINISH_COMMANDS, &[ self.cmd_list(FINISH_COMMANDS, &[("$TARGET", self.target_str())])
Review

When you mix reformatting changes in with code changes then I have to read the reformatting changes very carefully to make sure you haven't changed anything.

BTW, are you sure this is more readable than the way it was formatted previously?

When you mix reformatting changes in with code changes then I have to read the reformatting changes very carefully to make sure you haven't changed anything. BTW, are you sure this is more readable than the way it was formatted previously?
("$TARGET", self.target_str())
])
} }
fn global_realm_config(&self) -> &str { fn global_realm_config(&self) -> &str {
@ -546,16 +546,33 @@ impl Installer {
Path::new(&self.artifact_directory).join(filename) Path::new(&self.artifact_directory).join(filename)
} }
fn copy_artifact<P: AsRef<Path>>(&self, filename: &str, target: P) -> Result<()> { fn copy_artifact<P: AsRef<Path>>(
&self,
filename: &str,
target: P,
) -> Result<(), Box<dyn std::error::Error>> {
self._copy_artifact(filename, target, false) self._copy_artifact(filename, target, false)
} }
fn sparse_copy_artifact<P: AsRef<Path>>(&self, filename: &str, target: P) -> Result<()> { fn sparse_copy_artifact<P: AsRef<Path>>(
&self,
filename: &str,
target: P,
) -> Result<(), Box<dyn std::error::Error>> {
self._copy_artifact(filename, target, true) self._copy_artifact(filename, target, true)
} }
fn _copy_artifact<P: AsRef<Path>>(&self, filename: &str, target: P, sparse: bool) -> Result<()> { fn _copy_artifact<P: AsRef<Path>>(
self.info(format!("Copying {} to {}", filename, target.as_ref().display()))?; &self,
filename: &str,
target: P,
sparse: bool,
) -> Result<(), Box<dyn std::error::Error>> {
self.info(format!(
"Copying {} to {}",
filename,
target.as_ref().display()
))?;
let src = self.artifact_path(filename); let src = self.artifact_path(filename);
let target = target.as_ref(); let target = target.as_ref();
util::create_dir(target)?; util::create_dir(target)?;
@ -568,20 +585,19 @@ impl Installer {
Ok(()) Ok(())
} }
fn header<S: AsRef<str>>(&self, s: S) -> Result<()> { fn header<S: AsRef<str>>(&self, s: S) -> Result<(), Box<dyn std::error::Error>> {
self.output(format!("\n[+] {}\n", s.as_ref())) self.output(format!("\n[+] {}\n", s.as_ref()))
} }
fn info<S: AsRef<str>>(&self, s: S) -> Result<()> { fn info<S: AsRef<str>>(&self, s: S) -> Result<(), Box<dyn std::error::Error>> {
self.output(format!(" [>] {}", s.as_ref())) self.output(format!(" [>] {}", s.as_ref()))
} }
fn output<S: AsRef<str>>(&self, s: S) -> Result<(), Box<dyn std::error::Error>> {
fn output<S: AsRef<str>>(&self, s: S) -> Result<()> { self.write_output(s.as_ref())
self.write_output(s.as_ref()).map_err(context!("error writing output"))
} }
fn write_output(&self, s: &str) -> io::Result<()> { fn write_output(&self, s: &str) -> Result<(), Box<dyn std::error::Error>> {
println!("{}", s); println!("{}", s);
io::stdout().flush()?; io::stdout().flush()?;
@ -592,7 +608,11 @@ impl Installer {
Ok(()) Ok(())
} }
fn cmd_list<I: IntoIterator<Item=S>, S: AsRef<str>>(&self, cmd_lines: I, subs: &[(&str,&str)]) -> Result<()> { fn cmd_list<I: IntoIterator<Item = S>, S: AsRef<str>>(
&self,
cmd_lines: I,
subs: &[(&str, &str)],
) -> Result<(), Box<dyn std::error::Error>> {
for line in cmd_lines { for line in cmd_lines {
let line = line.as_ref(); let line = line.as_ref();
let line = subs.iter().fold(line.to_string(), |acc, (from,to)| acc.replace(from,to)); let line = subs.iter().fold(line.to_string(), |acc, (from,to)| acc.replace(from,to));
@ -602,12 +622,12 @@ impl Installer {
Ok(()) Ok(())
} }
fn cmd<S: AsRef<str>>(&self, args: S) -> Result<()> { fn cmd<S: AsRef<str>>(&self, args: S) -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<&str> = args.as_ref().split_whitespace().collect::<Vec<_>>(); let args: Vec<&str> = args.as_ref().split_whitespace().collect::<Vec<_>>();
self.run_cmd(args, false) self.run_cmd(args, false)
} }
fn run_cmd(&self, args: Vec<&str>, as_user: bool) -> Result<()> { fn run_cmd(&self, args: Vec<&str>, as_user: bool) -> Result<(), Box<dyn std::error::Error>> {
self.output(format!(" # {}", args.join(" ")))?; self.output(format!(" # {}", args.join(" ")))?;
let mut command = Command::new(args[0]); let mut command = Command::new(args[0]);
@ -632,8 +652,8 @@ impl Installer {
if !result.status.success() { if !result.status.success() {
match result.status.code() { match result.status.code() {
Some(code) => bail!("command {} failed with exit code: {}", args[0], code), Some(code) => panic!("command {} failed with exit code: {}", args[0], code),
None => bail!("command {} failed with no exit code", args[0]), None => panic!("command {} failed with no exit code", args[0]),
} }
} }
Ok(()) Ok(())

View File

@ -4,7 +4,7 @@ pub(crate) mod installer;
mod cli; mod cli;
mod disk; mod disk;
pub fn main(args: Vec<String>) { pub fn main(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
let mut args = args.iter().skip(1); let mut args = args.iter().skip(1);
let result = if let Some(dev) = args.next() { let result = if let Some(dev) = args.next() {
cli::run_cli_install_with(dev) cli::run_cli_install_with(dev)
@ -17,10 +17,11 @@ pub fn main(args: Vec<String>) {
Err(ref err) => { Err(ref err) => {
println!("Install failed: {}", err); println!("Install failed: {}", err);
exit(1); exit(1);
}, }
}; };
if !ok { if !ok {
println!("Install cancelled..."); println!("Install cancelled...");
} }
}
Ok(())
}

View File

@ -7,7 +7,6 @@ use std::sync::mpsc::{Sender};
use dbus::tree::{self, Factory, MTFn, MethodResult, Tree}; use dbus::tree::{self, Factory, MTFn, MethodResult, Tree};
use dbus::{Message}; use dbus::{Message};
use dbus::blocking::LocalConnection; use dbus::blocking::LocalConnection;
use libcitadel::{Result};
// Use local version of disk.rs since we added some methods // Use local version of disk.rs since we added some methods
use crate::install_backend::disk::*; use crate::install_backend::disk::*;
use crate::install::installer::*; use crate::install::installer::*;
@ -15,7 +14,6 @@ use std::fmt;
type MethodInfo<'a> = tree::MethodInfo<'a, MTFn<TData>, TData>; type MethodInfo<'a> = tree::MethodInfo<'a, MTFn<TData>, TData>;
const OBJECT_PATH: &str = "/com/subgraph/installer"; const OBJECT_PATH: &str = "/com/subgraph/installer";
const INTERFACE_NAME: &str = "com.subgraph.installer.Manager"; const INTERFACE_NAME: &str = "com.subgraph.installer.Manager";
const BUS_NAME: &str = "com.subgraph.installer"; const BUS_NAME: &str = "com.subgraph.installer";
@ -37,8 +35,7 @@ pub struct DbusServer {
} }
impl DbusServer { impl DbusServer {
pub fn connect() -> Result<DbusServer, Box<dyn std::error::Error>> {
pub fn connect() -> Result<DbusServer> {
let connection = LocalConnection::new_system() let connection = LocalConnection::new_system()
.map_err(|e| format_err!("Failed to connect to DBUS system bus: {}", e))?; .map_err(|e| format_err!("Failed to connect to DBUS system bus: {}", e))?;
let connection = Arc::new(connection); let connection = Arc::new(connection);
@ -80,7 +77,7 @@ impl DbusServer {
Ok(vec![m.msg.method_return().append1(list)]) Ok(vec![m.msg.method_return().append1(list)])
} }
fn run_install(path: String, citadel_passphrase: String, luks_passphrase: String, sender: Sender<Msg>) -> Result<()> { fn run_install(path: String, citadel_passphrase: String, luks_passphrase: String, sender: Sender<Msg>) -> Result<(), Box<dyn std::error::Error>> {
let mut install = Installer::new(path, &citadel_passphrase, &luks_passphrase); let mut install = Installer::new(path, &citadel_passphrase, &luks_passphrase);
install.set_install_syslinux(true); install.set_install_syslinux(true);
install.verify()?; install.verify()?;
@ -174,12 +171,12 @@ impl DbusServer {
Message::signal(&path, &iface, &member).append1(text) Message::signal(&path, &iface, &member).append1(text)
} }
pub fn start(&self) -> Result<()> { pub fn start(&self) -> Result<(), Box<dyn std::error::Error>> {
let (sender, receiver) = mpsc::channel::<Msg>(); let (sender, receiver) = mpsc::channel::<Msg>();
let sender_clone = sender.clone(); let sender_clone = sender.clone();
let tree = self.build_tree(sender); let tree = self.build_tree(sender);
if let Err(_err) = self.connection.request_name(BUS_NAME, false, true, false) { if let Err(_err) = self.connection.request_name(BUS_NAME, false, true, false) {
bail!("Failed to request name"); panic!("Failed to request name");
} }
tree.start_receive(self.connection.as_ref()); tree.start_receive(self.connection.as_ref());
@ -231,7 +228,6 @@ impl DbusServer {
} }
} }
} }
} }
#[derive(Clone)] #[derive(Clone)]
@ -243,7 +239,6 @@ impl TreeData {
TreeData {} TreeData {}
} }
fn disks(&self) -> HashMap<String, Vec<String>> { fn disks(&self) -> HashMap<String, Vec<String>> {
let disks = Disk::probe_all().unwrap(); let disks = Disk::probe_all().unwrap();
@ -257,7 +252,6 @@ impl TreeData {
} }
disk_map disk_map
} }
} }
impl fmt::Debug for TreeData { impl fmt::Debug for TreeData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

View File

@ -1,24 +1,20 @@
use libcitadel::Result;
use std::process::exit; use std::process::exit;
mod disk; mod disk;
mod dbus; mod dbus;
use libcitadel::CommandLine; use libcitadel::CommandLine;
pub fn main() { pub fn main() -> Result<(), Box<dyn std::error::Error>> {
if CommandLine::install_mode() { if CommandLine::install_mode() {
if let Err(e) = run_dbus_server() { run_dbus_server()
warn!("Error: {}", e);
}
} else { } else {
println!("Citadel installer backend will only run in install or live mode"); println!("Citadel installer backend will only run in install or live mode");
exit(1); exit(1);
} }
} }
fn run_dbus_server() -> Result<()> { fn run_dbus_server() -> Result<(), Box<dyn std::error::Error>> {
let server = dbus::DbusServer::connect()?; let server = dbus::DbusServer::connect()?;
server.start()?; server.start()?;
Ok(()) Ok(())
} }

View File

@ -17,42 +17,37 @@ mod realmfs;
mod sync; mod sync;
mod update; mod update;
fn main() { fn main() -> Result<(), Box<dyn std::error::Error>> {
let exe = match env::current_exe() { let exe = env::current_exe()?;
Ok(path) => path,
Err(_e) => {
return;
},
};
let args = env::args().collect::<Vec<String>>(); let args = env::args().collect::<Vec<String>>();
if exe == Path::new("/usr/libexec/citadel-boot") { if exe == Path::new("/usr/libexec/citadel-boot") {
boot::main(args); boot::main(args)
} else if exe == Path::new("/usr/libexec/citadel-install") { } else if exe == Path::new("/usr/libexec/citadel-install") {
install::main(args); install::main(args)
} else if exe == Path::new("/usr/libexec/citadel-install-backend") { } else if exe == Path::new("/usr/libexec/citadel-install-backend") {
install_backend::main(); install_backend::main()
} else if exe == Path::new("/usr/bin/citadel-image") { } else if exe == Path::new("/usr/bin/citadel-image") {
image::main(args); image::main(args)
} else if exe == Path::new("/usr/bin/citadel-realmfs") { } else if exe == Path::new("/usr/bin/citadel-realmfs") {
realmfs::main(args); realmfs::main(args)
} else if exe == Path::new("/usr/bin/citadel-update") { } else if exe == Path::new("/usr/bin/citadel-update") {
update::main(args); update::main(args)
} else if exe == Path::new("/usr/libexec/citadel-desktop-sync") { } else if exe == Path::new("/usr/libexec/citadel-desktop-sync") {
sync::main(args); sync::main(args)
} else if exe == Path::new("/usr/libexec/citadel-run") { } else if exe == Path::new("/usr/libexec/citadel-run") {
do_citadel_run(args); do_citadel_run(args)
} else if exe.file_name() == Some(OsStr::new("citadel-mkimage")) { } else if exe.file_name() == Some(OsStr::new("citadel-mkimage")) {
mkimage::main(args); mkimage::main(args)
} else if exe.file_name() == Some(OsStr::new("citadel-tool")) { } else if exe.file_name() == Some(OsStr::new("citadel-tool")) {
dispatch_command(args); dispatch_command(args)
} else { } else {
println!("Error: unknown executable {}", exe.display()); Result::Err(format!("Error: unknown executable {}", exe.display()).into())
} }
} }
fn dispatch_command(args: Vec<String>) { fn dispatch_command(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
if let Some(command) = args.get(1) { if let Some(command) = args.get(1) {
match command.as_str() { match command.as_str() {
"boot" => boot::main(rebuild_args("citadel-boot", args)), "boot" => boot::main(rebuild_args("citadel-boot", args)),
@ -63,22 +58,33 @@ fn dispatch_command(args: Vec<String>) {
"mkimage" => mkimage::main(rebuild_args("citadel-mkimage", args)), "mkimage" => mkimage::main(rebuild_args("citadel-mkimage", args)),
"sync" => sync::main(rebuild_args("citadel-desktop-sync", args)), "sync" => sync::main(rebuild_args("citadel-desktop-sync", args)),
"run" => do_citadel_run(rebuild_args("citadel-run", args)), "run" => do_citadel_run(rebuild_args("citadel-run", args)),
_ => println!("Error: unknown command {}", command), _ => throw_err(command),
} }
} else { } else {
println!("Must provide an argument"); Result::Err("Must provide an argument".into())
} }
} }
fn throw_err(command: &String) -> Result<(), Box<dyn std::error::Error>> {
Result::Err(format!("Error: unknown command {}", command).into())
}
fn rebuild_args(command: &str, args: Vec<String>) -> Vec<String> { fn rebuild_args(command: &str, args: Vec<String>) -> Vec<String> {
iter::once(command.to_string()) iter::once(command.to_string())
.chain(args.into_iter().skip(2)) .chain(args.into_iter().skip(2))
.collect() .collect()
} }
fn do_citadel_run(args: Vec<String>) { fn do_citadel_run(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
if let Err(e) = RealmManager::run_in_current(&args[1..], true) { if let Err(e) = RealmManager::run_in_current(&args[1..], true) {
println!("RealmManager::run_in_current({:?}) failed: {}", &args[1..], e); return Result::Err(
format!(
"RealmManager::run_in_current({:?}) failed: {}",
&args[1..],
e
)
.into(),
);
} }
Ok(())
} }

View File

@ -1,13 +1,10 @@
use std::process::exit; use std::process::exit;
use libcitadel::Result;
mod config; mod config;
mod build; mod build;
pub fn main(args: Vec<String>) { pub fn main(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
let config_path = match args.get(1) { let config_path = match args.get(1) {
Some(arg) => arg, Some(arg) => arg,
None => { None => {
@ -21,11 +18,11 @@ pub fn main(args: Vec<String>) {
exit(1); exit(1);
} }
Ok(())
} }
fn build_image(config_path: &str) -> Result<()> { fn build_image(config_path: &str) -> Result<(), Box<dyn std::error::Error>> {
let conf = config::BuildConfig::load(config_path)?; let conf = config::BuildConfig::load(config_path)?;
let mut builder = build::UpdateBuilder::new(conf); let mut builder = build::UpdateBuilder::new(conf);
builder.build() Ok(builder.build()?)
} }

View File

@ -1,16 +1,14 @@
use clap::App; use clap::App;
use clap::ArgMatches; use clap::ArgMatches;
use libcitadel::{Result,RealmFS,Logger,LogLevel}; use libcitadel::{RealmFS, Logger, LogLevel};
use libcitadel::util::is_euid_root; use libcitadel::util::is_euid_root;
use clap::SubCommand; use clap::SubCommand;
use clap::AppSettings::*; use clap::AppSettings::*;
use clap::Arg; use clap::Arg;
use libcitadel::ResizeSize; use libcitadel::ResizeSize;
use std::process::exit;
pub fn main(args: Vec<String>) {
pub fn main(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
Logger::set_log_level(LogLevel::Debug); Logger::set_log_level(LogLevel::Debug);
let app = App::new("citadel-realmfs") let app = App::new("citadel-realmfs")
@ -83,18 +81,15 @@ is the final absolute size of the image.")
("activate", Some(m)) => activate(m), ("activate", Some(m)) => activate(m),
("deactivate", Some(m)) => deactivate(m), ("deactivate", Some(m)) => deactivate(m),
_ => image_info(&matches), _ => image_info(&matches),
}; }?;
if let Err(ref e) = result { Ok(())
eprintln!("Error: {}", e);
exit(1);
}
} }
fn realmfs_image(arg_matches: &ArgMatches) -> Result<RealmFS> { fn realmfs_image(arg_matches: &ArgMatches) -> Result<RealmFS, Box<dyn std::error::Error>> {
let image = match arg_matches.value_of("image") { let image = match arg_matches.value_of("image") {
Some(s) => s, Some(s) => s,
None => bail!("Image argument required."), None => panic!("Image argument required."),
}; };
let realmfs = if RealmFS::is_valid_name(image) { let realmfs = if RealmFS::is_valid_name(image) {
@ -102,93 +97,100 @@ fn realmfs_image(arg_matches: &ArgMatches) -> Result<RealmFS> {
} else if RealmFS::is_valid_realmfs_image(image) { } else if RealmFS::is_valid_realmfs_image(image) {
RealmFS::load_from_path(image)? RealmFS::load_from_path(image)?
} else { } else {
bail!("Not a valid realmfs name or path to realmfs image file: {}", image); panic!(
"Not a valid realmfs name or path to realmfs image file: {}",
image
);
}; };
Ok(realmfs) Ok(realmfs)
} }
fn image_info(arg_matches: &ArgMatches) -> Result<()> { fn image_info(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = realmfs_image(arg_matches)?; let img = realmfs_image(arg_matches)?;
print!("{}", String::from_utf8(img.header().metainfo_bytes())?); print!("{}", String::from_utf8(img.header().metainfo_bytes())?);
Ok(()) Ok(())
} }
fn parse_resize_size(s: &str) -> Result<ResizeSize> { fn parse_resize_size(s: &str) -> Result<ResizeSize, Box<dyn std::error::Error>> {
let unit = s.chars().last().filter(|c| c.is_alphabetic()); let unit = s.chars().last().filter(|c| c.is_alphabetic());
let skip = if s.starts_with('+') { 1 } else { 0 }; let skip = if s.starts_with('+') { 1 } else { 0 };
let size = s.chars() let size = s
.chars()
.skip(skip) .skip(skip)
.take_while(|c| c.is_numeric()) .take_while(|c| c.is_numeric())
.collect::<String>() .collect::<String>()
.parse::<usize>() .parse::<usize>()
.map_err(|_| format_err!("Unable to parse size value '{}'",s))?; .map_err(|_| format_err!("Unable to parse size value '{}'", s))?;
let sz = match unit { let sz = match unit {
Some('g') | Some('G') => ResizeSize::gigs(size), Some('g') | Some('G') => ResizeSize::gigs(size),
Some('m') | Some('M') => ResizeSize::megs(size), Some('m') | Some('M') => ResizeSize::megs(size),
Some(c) => bail!("Unknown size unit '{}'", c), Some(c) => panic!("Unknown size unit '{}'", c),
None => ResizeSize::blocks(size), None => ResizeSize::blocks(size),
}; };
Ok(sz) Ok(sz)
} }
fn resize(arg_matches: &ArgMatches) -> Result<()> { fn resize(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = realmfs_image(arg_matches)?; let img = realmfs_image(arg_matches)?;
info!("image is {}", img.path().display()); info!("image is {}", img.path().display());
let size_arg = match arg_matches.value_of("size") { let size_arg = match arg_matches.value_of("size") {
Some(size) => size, Some(size) => size,
None => "No size argument", None => "No size argument",
}; };
info!("Size is {}", size_arg); info!("Size is {}", size_arg);
let mode_add = size_arg.starts_with('+'); let mode_add = size_arg.starts_with('+');
let size = parse_resize_size(size_arg)?; let size = parse_resize_size(size_arg)?;
if mode_add { if mode_add {
img.resize_grow_by(size) img.resize_grow_by(size)?;
} else { } else {
img.resize_grow_to(size) img.resize_grow_to(size)?;
} }
Ok(())
} }
fn autoresize(arg_matches: &ArgMatches) -> Result<()> { fn autoresize(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = realmfs_image(arg_matches)?; let img = realmfs_image(arg_matches)?;
if let Some(size) = img.auto_resize_size() { if let Some(size) = img.auto_resize_size() {
img.resize_grow_to(size) img.resize_grow_to(size)?;
} else { } else {
info!("RealmFS image {} has sufficient free space, doing nothing", img.path().display()); info!(
Ok(()) "RealmFS image {} has sufficient free space, doing nothing",
img.path().display()
);
} }
Ok(())
} }
fn fork(arg_matches: &ArgMatches) -> Result<()> { fn fork(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = realmfs_image(arg_matches)?; let img = realmfs_image(arg_matches)?;
let forkname = match arg_matches.value_of("forkname") { let forkname = match arg_matches.value_of("forkname") {
Some(name) => name, Some(name) => name,
None => bail!("No fork name argument"), None => panic!("No fork name argument"),
}; };
if !RealmFS::is_valid_name(forkname) { if !RealmFS::is_valid_name(forkname) {
bail!("Not a valid RealmFS image name '{}'", forkname); panic!("Not a valid RealmFS image name '{}'", forkname);
} }
if RealmFS::named_image_exists(forkname) { if RealmFS::named_image_exists(forkname) {
bail!("A RealmFS image named '{}' already exists", forkname); panic!("A RealmFS image named '{}' already exists", forkname);
} }
img.fork(forkname)?; img.fork(forkname)?;
Ok(()) Ok(())
} }
fn update(arg_matches: &ArgMatches) -> Result<()> { fn update(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
if !is_euid_root() { if !is_euid_root() {
bail!("RealmFS updates must be run as root"); panic!("RealmFS updates must be run as root");
} }
let img = realmfs_image(arg_matches)?; let img = realmfs_image(arg_matches)?;
img.interactive_update(Some("icy"))?; img.interactive_update(Some("icy"))?;
Ok(()) Ok(())
} }
fn activate(arg_matches: &ArgMatches) -> Result<()> { fn activate(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = realmfs_image(arg_matches)?; let img = realmfs_image(arg_matches)?;
let img_arg = arg_matches.value_of("image").unwrap(); let img_arg = arg_matches.value_of("image").unwrap();
@ -201,7 +203,7 @@ fn activate(arg_matches: &ArgMatches) -> Result<()> {
Ok(()) Ok(())
} }
fn deactivate(arg_matches: &ArgMatches) -> Result<()> { fn deactivate(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = realmfs_image(arg_matches)?; let img = realmfs_image(arg_matches)?;
let img_arg = arg_matches.value_of("image").unwrap(); let img_arg = arg_matches.value_of("image").unwrap();
if !img.is_activated() { if !img.is_activated() {

View File

@ -1,9 +1,9 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::path::{Path,PathBuf}; use std::path::{Path, PathBuf};
use std::time::SystemTime; use std::time::SystemTime;
use libcitadel::{Realm, Realms, Result, util}; use libcitadel::{Realm, Realms, util};
use crate::sync::parser::DesktopFileParser; use crate::sync::parser::DesktopFileParser;
use std::fs::DirEntry; use std::fs::DirEntry;
use crate::sync::desktop_file::DesktopFile; use crate::sync::desktop_file::DesktopFile;
@ -17,14 +17,13 @@ pub struct DesktopFileSync {
icons: Option<IconSync>, icons: Option<IconSync>,
} }
#[derive(Eq,PartialEq,Hash)] #[derive(Eq, PartialEq, Hash)]
struct DesktopItem { struct DesktopItem {
path: PathBuf, path: PathBuf,
mtime: SystemTime, mtime: SystemTime,
} }
impl DesktopItem { impl DesktopItem {
fn new(path: PathBuf, mtime: SystemTime) -> Self { fn new(path: PathBuf, mtime: SystemTime) -> Self {
DesktopItem { path, mtime } DesktopItem { path, mtime }
} }
@ -46,7 +45,7 @@ impl DesktopItem {
impl DesktopFileSync { impl DesktopFileSync {
pub const CITADEL_APPLICATIONS: &'static str = "/home/citadel/.local/share/applications"; pub const CITADEL_APPLICATIONS: &'static str = "/home/citadel/.local/share/applications";
pub fn sync_active_realms() -> Result<()> { pub fn sync_active_realms() -> Result<(), Box<dyn std::error::Error>> {
let realms = Realms::load()?; let realms = Realms::load()?;
for realm in realms.active(true) { for realm in realms.active(true) {
let mut sync = DesktopFileSync::new(realm); let mut sync = DesktopFileSync::new(realm);
@ -72,7 +71,7 @@ impl DesktopFileSync {
DesktopFileSync { realm, items: HashSet::new(), icons } DesktopFileSync { realm, items: HashSet::new(), icons }
} }
pub fn run_sync(&mut self, clear: bool) -> Result<()> { pub fn run_sync(&mut self, clear: bool) -> Result<(), Box<dyn std::error::Error>> {
IconSync::ensure_theme_index_exists()?; IconSync::ensure_theme_index_exists()?;
@ -98,7 +97,7 @@ impl DesktopFileSync {
Ok(()) Ok(())
} }
fn collect_source_files(&mut self, directory: impl AsRef<Path>) -> Result<()> { fn collect_source_files(&mut self, directory: impl AsRef<Path>) -> Result<(), Box<dyn std::error::Error>> {
let mut directory = Realms::current_realm_symlink().join(directory.as_ref()); let mut directory = Realms::current_realm_symlink().join(directory.as_ref());
directory.push("share/applications"); directory.push("share/applications");
if directory.exists() { if directory.exists() {
@ -119,16 +118,16 @@ impl DesktopFileSync {
} }
} }
pub fn clear_target_files() -> Result<()> { pub fn clear_target_files() -> Result<(), Box<dyn std::error::Error>> {
util::read_directory(Self::CITADEL_APPLICATIONS, |dent| { Ok(util::read_directory(Self::CITADEL_APPLICATIONS, |dent| {
util::remove_file(dent.path()) util::remove_file(dent.path())
}) })?)
} }
fn remove_missing_target_files(&mut self) -> Result<()> { fn remove_missing_target_files(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let sources = self.source_filenames(); let sources = self.source_filenames();
let prefix = format!("realm-{}.", self.realm.name()); let prefix = format!("realm-{}.", self.realm.name());
util::read_directory(Self::CITADEL_APPLICATIONS, |dent| { Ok(util::read_directory(Self::CITADEL_APPLICATIONS, |dent| {
if let Some(filename) = dent.file_name().to_str() { if let Some(filename) = dent.file_name().to_str() {
if filename.starts_with(&prefix) && !sources.contains(filename) { if filename.starts_with(&prefix) && !sources.contains(filename) {
let path = dent.path(); let path = dent.path();
@ -137,7 +136,7 @@ impl DesktopFileSync {
} }
} }
Ok(()) Ok(())
}) })?)
} }
fn mtime(path: &Path) -> Option<SystemTime> { fn mtime(path: &Path) -> Option<SystemTime> {
@ -156,7 +155,7 @@ impl DesktopFileSync {
.collect() .collect()
} }
fn synchronize_items(&self) -> Result<()> { fn synchronize_items(&self) -> Result<(), Box<dyn std::error::Error>> {
for item in &self.items { for item in &self.items {
let target = Path::new(Self::CITADEL_APPLICATIONS).join(item.filename()); let target = Path::new(Self::CITADEL_APPLICATIONS).join(item.filename());
if item.is_newer_than(&target) { if item.is_newer_than(&target) {
@ -180,7 +179,7 @@ impl DesktopFileSync {
} }
} }
fn sync_item(&self, item: &DesktopItem) -> Result<()> { fn sync_item(&self, item: &DesktopItem) -> Result<(), Box<dyn std::error::Error>> {
let mut dfp = DesktopFileParser::parse_from_path(&item.path, "/usr/libexec/citadel-run ")?; let mut dfp = DesktopFileParser::parse_from_path(&item.path, "/usr/libexec/citadel-run ")?;
if dfp.is_showable() { if dfp.is_showable() {
self.sync_item_icon(&mut dfp); self.sync_item_icon(&mut dfp);

View File

@ -1,4 +1,4 @@
use libcitadel::{Result, Logger, LogLevel}; use libcitadel::{Logger, LogLevel};
mod desktop_file; mod desktop_file;
mod parser; mod parser;
@ -19,8 +19,7 @@ pub const REALM_BASE_PATHS:&[&str] = &[
"home/.local/share/flatpak/exports" "home/.local/share/flatpak/exports"
]; ];
pub fn main(args: Vec<String>) { pub fn main(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
if has_arg(&args, "-v") { if has_arg(&args, "-v") {
Logger::set_log_level(LogLevel::Debug); Logger::set_log_level(LogLevel::Debug);
} else { } else {
@ -37,12 +36,14 @@ pub fn main(args: Vec<String>) {
println!("Desktop file sync failed: {}", e); println!("Desktop file sync failed: {}", e);
} }
} }
Ok(())
} }
fn sync(clear: bool) -> Result<()> { fn sync(clear: bool) -> Result<(), Box<dyn std::error::Error>> {
if let Some(mut sync) = DesktopFileSync::new_current() { if let Some(mut sync) = DesktopFileSync::new_current() {
sync.run_sync(clear) sync.run_sync(clear)?
} else { } else {
DesktopFileSync::clear_target_files() DesktopFileSync::clear_target_files()?
} }
Ok(())
} }

View File

@ -1,6 +1,6 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use libcitadel::{Result, Partition, ResourceImage, ImageHeader, LogLevel, Logger, util}; use libcitadel::{Partition, ResourceImage, ImageHeader, LogLevel, Logger, util};
use crate::update::kernel::{KernelInstaller, KernelVersion}; use crate::update::kernel::{KernelInstaller, KernelVersion};
use std::collections::HashSet; use std::collections::HashSet;
use std::fs::{DirEntry, File}; use std::fs::{DirEntry, File};
@ -16,7 +16,7 @@ const FLAG_QUIET: u32 = 0x04;
const RESOURCES_DIRECTORY: &str = "/storage/resources"; const RESOURCES_DIRECTORY: &str = "/storage/resources";
const TEMP_DIRECTORY: &str = "/storage/resources/tmp"; const TEMP_DIRECTORY: &str = "/storage/resources/tmp";
pub fn main(args: Vec<String>) { pub fn main(args: Vec<String>) -> std::result::Result<(), Box<dyn std::error::Error>> {
let mut args = args.iter().skip(1); let mut args = args.iter().skip(1);
let mut flags = 0; let mut flags = 0;
@ -34,7 +34,7 @@ pub fn main(args: Vec<String>) {
Logger::set_log_level(LogLevel::Debug); Logger::set_log_level(LogLevel::Debug);
} else if arg == "--choose-rootfs" { } else if arg == "--choose-rootfs" {
let _ = choose_install_partition(true); let _ = choose_install_partition(true);
return; return Ok(())
} else { } else {
let path = Path::new(arg); let path = Path::new(arg);
if let Err(e) = install_image(path, flags) { if let Err(e) = install_image(path, flags) {
@ -42,12 +42,13 @@ pub fn main(args: Vec<String>) {
} }
} }
} }
Ok(())
} }
// Search directory containing installed image files for an // Search directory containing installed image files for an
// image file that has an identical shasum and abort the installation // image file that has an identical shasum and abort the installation
// if a duplicate is found. // if a duplicate is found.
fn detect_duplicates(header: &ImageHeader) -> Result<()> { fn detect_duplicates(header: &ImageHeader) -> Result<(), Box<dyn std::error::Error>> {
let metainfo = header.metainfo(); let metainfo = header.metainfo();
let channel = metainfo.channel(); let channel = metainfo.channel();
let shasum = metainfo.shasum(); let shasum = metainfo.shasum();
@ -61,17 +62,20 @@ fn detect_duplicates(header: &ImageHeader) -> Result<()> {
return Ok(()) return Ok(())
} }
util::read_directory(&resource_dir, |dent| { Ok(util::read_directory(&resource_dir, |dent| {
if let Ok(hdr) = ImageHeader::from_file(dent.path()) { if let Ok(hdr) = ImageHeader::from_file(dent.path()) {
if hdr.metainfo().shasum() == shasum { if hdr.metainfo().shasum() == shasum {
bail!("A duplicate image file with the same shasum already exists at {}", dent.path().display()); panic!(
"A duplicate image file with the same shasum already exists at {}",
dent.path().display()
);
} }
} }
Ok(()) Ok(())
}) })?)
} }
fn create_tmp_copy(path: &Path) -> Result<PathBuf> { fn create_tmp_copy(path: &Path) -> Result<PathBuf, Box<dyn std::error::Error>> {
if !Path::new(TEMP_DIRECTORY).exists() { if !Path::new(TEMP_DIRECTORY).exists() {
util::create_dir(TEMP_DIRECTORY)?; util::create_dir(TEMP_DIRECTORY)?;
} }
@ -93,12 +97,12 @@ fn create_tmp_copy(path: &Path) -> Result<PathBuf> {
Ok(path) Ok(path)
} }
fn install_image(path: &Path, flags: u32) -> Result<()> { fn install_image(path: &Path, flags: u32) -> Result<(), Box<dyn std::error::Error>> {
if !path.exists() || path.file_name().is_none() { if !path.exists() || path.file_name().is_none() {
bail!("file path {} does not exist", path.display()); panic!("file path {} does not exist", path.display());
} }
if !util::is_euid_root() { if !util::is_euid_root() {
bail!("Image updates must be installed by root user"); panic!("Image updates must be installed by root user");
} }
let header = ImageHeader::from_file(path)?; let header = ImageHeader::from_file(path)?;
@ -114,13 +118,13 @@ fn install_image(path: &Path, flags: u32) -> Result<()> {
"kernel" => install_kernel_image(&mut image), "kernel" => install_kernel_image(&mut image),
"extra" => install_extra_image(&image), "extra" => install_extra_image(&image),
"rootfs" => install_rootfs_image(&image, flags), "rootfs" => install_rootfs_image(&image, flags),
image_type => bail!("Unknown image type: {}", image_type), image_type => panic!("Unknown image type: {}", image_type),
} }
} }
// Prepare the image file for installation by decompressing and generating // Prepare the image file for installation by decompressing and generating
// dmverity hash tree. // dmverity hash tree.
fn prepare_image(image: &ResourceImage, flags: u32) -> Result<()> { fn prepare_image(image: &ResourceImage, flags: u32) -> Result<(), Box<dyn std::error::Error>> {
if image.is_compressed() { if image.is_compressed() {
image.decompress(false)?; image.decompress(false)?;
} }
@ -129,7 +133,7 @@ fn prepare_image(image: &ResourceImage, flags: u32) -> Result<()> {
info!("Verifying sha256 hash of image"); info!("Verifying sha256 hash of image");
let shasum = image.generate_shasum()?; let shasum = image.generate_shasum()?;
if shasum != image.metainfo().shasum() { if shasum != image.metainfo().shasum() {
bail!("image file does not have expected sha256 value"); panic!("image file does not have expected sha256 value");
} }
} }
@ -139,24 +143,28 @@ fn prepare_image(image: &ResourceImage, flags: u32) -> Result<()> {
Ok(()) Ok(())
} }
fn install_extra_image(image: &ResourceImage) -> Result<()> { fn install_extra_image(image: &ResourceImage) -> Result<(), Box<dyn std::error::Error>> {
let filename = format!("citadel-extra-{:03}.img", image.header().metainfo().version()); let filename = format!("citadel-extra-{:03}.img", image.header().metainfo().version());
install_image_file(image, filename.as_str())?; install_image_file(image, filename.as_str())?;
remove_old_extra_images(image)?; remove_old_extra_images(image)?;
Ok(()) Ok(())
} }
fn remove_old_extra_images(image: &ResourceImage) -> Result<()> { fn remove_old_extra_images(image: &ResourceImage) -> Result<(), Box<dyn std::error::Error>> {
let new_meta = image.header().metainfo(); let new_meta = image.header().metainfo();
let shasum = new_meta.shasum(); let shasum = new_meta.shasum();
let target_dir = target_directory(image)?; let target_dir = target_directory(image)?;
util::read_directory(&target_dir, |dent| { Ok(util::read_directory(&target_dir, |dent| {
let path = dent.path(); let path = dent.path();
maybe_remove_old_extra_image(&path, shasum) maybe_remove_old_extra_image(&path, shasum).unwrap();
}) Ok(())
})?)
} }
fn maybe_remove_old_extra_image(path: &Path, shasum: &str) -> Result<()> { fn maybe_remove_old_extra_image(
path: &Path,
shasum: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let header = ImageHeader::from_file(&path)?; let header = ImageHeader::from_file(&path)?;
if !header.is_magic_valid() { if !header.is_magic_valid() {
return Ok(()); return Ok(());
@ -172,16 +180,16 @@ fn maybe_remove_old_extra_image(path: &Path, shasum: &str) -> Result<()> {
Ok(()) Ok(())
} }
fn install_kernel_image(image: &mut ResourceImage) -> Result<()> { fn install_kernel_image(image: &mut ResourceImage) -> Result<(), Box<dyn std::error::Error>> {
if !Path::new("/boot/loader/loader.conf").exists() { if !Path::new("/boot/loader/loader.conf").exists() {
bail!("failed to automount /boot partition. Please manually mount correct partition."); panic!("failed to automount /boot partition. Please manually mount correct partition.");
} }
let metainfo = image.header().metainfo(); let metainfo = image.header().metainfo();
let version = metainfo.version(); let version = metainfo.version();
let kernel_version = match metainfo.kernel_version() { let kernel_version = match metainfo.kernel_version() {
Some(kv) => kv, Some(kv) => kv,
None => bail!("kernel image does not have kernel version field"), None => panic!("kernel image does not have kernel version field"),
}; };
info!("kernel version is {}", kernel_version); info!("kernel version is {}", kernel_version);
install_kernel_file(image, &kernel_version)?; install_kernel_file(image, &kernel_version)?;
@ -194,7 +202,7 @@ fn install_kernel_image(image: &mut ResourceImage) -> Result<()> {
let mut remove_paths = Vec::new(); let mut remove_paths = Vec::new();
util::read_directory(&image_dir, |dent| { util::read_directory(&image_dir, |dent| {
let path = dent.path(); let path = dent.path();
if is_unused_kernel_image(&path, &all_versions)? { if is_unused_kernel_image(&path, &all_versions).unwrap() {
remove_paths.push(path); remove_paths.push(path);
} }
Ok(()) Ok(())
@ -206,7 +214,7 @@ fn install_kernel_image(image: &mut ResourceImage) -> Result<()> {
Ok(()) Ok(())
} }
fn is_unused_kernel_image(path: &Path, versions: &HashSet<String>) -> Result<bool> { fn is_unused_kernel_image(path: &Path, versions: &HashSet<String>) -> Result<bool, Box<dyn std::error::Error>> {
let header = ImageHeader::from_file(path)?; let header = ImageHeader::from_file(path)?;
if !header.is_magic_valid() { if !header.is_magic_valid() {
return Ok(false); return Ok(false);
@ -226,23 +234,25 @@ fn is_unused_kernel_image(path: &Path, versions: &HashSet<String>) -> Result<boo
Ok(false) Ok(false)
} }
fn install_kernel_file(image: &mut ResourceImage, kernel_version: &str) -> Result<()> { fn install_kernel_file(
image: &mut ResourceImage,
kernel_version: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let mountpoint = Path::new("/run/citadel/images/kernel-install.mountpoint"); let mountpoint = Path::new("/run/citadel/images/kernel-install.mountpoint");
info!("Temporarily mounting kernel resource image"); info!("Temporarily mounting kernel resource image");
let mut handle = image.mount_at(mountpoint)?; let mut handle = image.mount_at(mountpoint)?;
let kernel_path = mountpoint.join("kernel/bzImage"); let kernel_path = mountpoint.join("kernel/bzImage");
if !kernel_path.exists() { if !kernel_path.exists() {
handle.unmount()?; handle.unmount()?;
bail!("kernel not found in kernel resource image at /kernel/bzImage") panic!("kernel not found in kernel resource image at /kernel/bzImage")
} }
let result = KernelInstaller::install_kernel(&kernel_path, kernel_version); KernelInstaller::install_kernel(&kernel_path, kernel_version)?;
info!("Unmounting kernel resource image"); info!("Unmounting kernel resource image");
handle.unmount()?; Ok(handle.unmount()?)
result
} }
fn all_boot_kernel_versions() -> Result<HashSet<String>> { fn all_boot_kernel_versions() -> Result<HashSet<String>, Box<dyn std::error::Error>> {
let mut result = HashSet::new(); let mut result = HashSet::new();
util::read_directory("/boot", |dent| { util::read_directory("/boot", |dent| {
if is_kernel_dirent(&dent) { if is_kernel_dirent(&dent) {
@ -264,7 +274,10 @@ fn is_kernel_dirent(dirent: &DirEntry) -> bool {
} }
} }
fn install_image_file(image: &ResourceImage, filename: &str) -> Result<()> { fn install_image_file(
image: &ResourceImage,
filename: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let image_dir = target_directory(image)?; let image_dir = target_directory(image)?;
let image_dest = image_dir.join(filename); let image_dest = image_dir.join(filename);
if image_dest.exists() { if image_dest.exists() {
@ -275,14 +288,14 @@ fn install_image_file(image: &ResourceImage, filename: &str) -> Result<()> {
Ok(()) Ok(())
} }
fn target_directory(image: &ResourceImage) -> Result<PathBuf> { fn target_directory(image: &ResourceImage) -> Result<PathBuf, Box<dyn std::error::Error>> {
let metainfo = image.header().metainfo(); let metainfo = image.header().metainfo();
let channel = metainfo.channel(); let channel = metainfo.channel();
validate_channel_name(channel)?; validate_channel_name(channel)?;
Ok(Path::new("/storage/resources").join(channel)) Ok(Path::new("/storage/resources").join(channel))
} }
fn rotate(path: &Path) -> Result<()> { fn rotate(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
if !path.exists() || path.file_name().is_none() { if !path.exists() || path.file_name().is_none() {
return Ok(()); return Ok(());
} }
@ -293,14 +306,17 @@ fn rotate(path: &Path) -> Result<()> {
Ok(()) Ok(())
} }
fn validate_channel_name(channel: &str) -> Result<()> { fn validate_channel_name(channel: &str) -> Result<(), Box<dyn std::error::Error>> {
if !channel.chars().all(|c| c.is_ascii_lowercase()) { if !channel.chars().all(|c| c.is_ascii_lowercase()) {
bail!("image has invalid channel name '{}'", channel); panic!("image has invalid channel name '{}'", channel);
} }
Ok(()) Ok(())
} }
fn install_rootfs_image(image: &ResourceImage, flags: u32) -> Result<()> { fn install_rootfs_image(
image: &ResourceImage,
flags: u32,
) -> Result<(), Box<dyn std::error::Error>> {
let quiet = flags & FLAG_QUIET != 0; let quiet = flags & FLAG_QUIET != 0;
let partition = choose_install_partition(!quiet)?; let partition = choose_install_partition(!quiet)?;
@ -315,7 +331,7 @@ fn install_rootfs_image(image: &ResourceImage, flags: u32) -> Result<()> {
Ok(()) Ok(())
} }
fn clear_prefer_boot() -> Result<()> { fn clear_prefer_boot() -> Result<(), Box<dyn std::error::Error>> {
for mut p in Partition::rootfs_partitions()? { for mut p in Partition::rootfs_partitions()? {
if p.is_initialized() && p.header().has_flag(ImageHeader::FLAG_PREFER_BOOT) { if p.is_initialized() && p.header().has_flag(ImageHeader::FLAG_PREFER_BOOT) {
p.clear_flag_and_write(ImageHeader::FLAG_PREFER_BOOT)?; p.clear_flag_and_write(ImageHeader::FLAG_PREFER_BOOT)?;
@ -332,7 +348,7 @@ fn bool_to_yesno(val: bool) -> &'static str {
} }
} }
fn choose_install_partition(verbose: bool) -> Result<Partition> { fn choose_install_partition(verbose: bool) -> Result<Partition, Box<dyn std::error::Error>> {
let partitions = Partition::rootfs_partitions()?; let partitions = Partition::rootfs_partitions()?;
if verbose { if verbose {
@ -362,5 +378,5 @@ fn choose_install_partition(verbose: bool) -> Result<Partition> {
return Ok(p.clone()) return Ok(p.clone())
} }
} }
bail!("no suitable install partition found") panic!("no suitable install partition found")
} }