From a36c948db2f21bc2d7209f7a8ba2dfa65a514a10 Mon Sep 17 00:00:00 2001 From: Mathias-Boulay Date: Mon, 22 Apr 2024 15:18:06 +0200 Subject: [PATCH] cleanup(image_builder): error handling with anyhow Signed-off-by: Mathias-Boulay --- src/fs-gen/Cargo.toml | 1 + src/fs-gen/src/image_builder.rs | 120 ++++++++++++++++++-------------- 2 files changed, 68 insertions(+), 53 deletions(-) diff --git a/src/fs-gen/Cargo.toml b/src/fs-gen/Cargo.toml index 2dbeb48..6bccfdb 100644 --- a/src/fs-gen/Cargo.toml +++ b/src/fs-gen/Cargo.toml @@ -18,3 +18,4 @@ serde_json = "1.0.115" signal-hook = "0.3.17" tar = "0.4.40" validator = { version = "0.17.0", features = ["derive"] } +anyhow = "1.0.82" diff --git a/src/fs-gen/src/image_builder.rs b/src/fs-gen/src/image_builder.rs index f723eab..6c5c67f 100644 --- a/src/fs-gen/src/image_builder.rs +++ b/src/fs-gen/src/image_builder.rs @@ -1,11 +1,11 @@ use std::{ fs::{self}, - io::Result, path::{Path, PathBuf}, sync::Arc, thread, }; +use anyhow::{Context, Result}; use fuse_backend_rs::{ api::{filesystem::Layer, server::Server}, overlayfs::{config::Config, OverlayFs}, @@ -21,23 +21,30 @@ pub struct FuseServer { type BoxedLayer = Box + Send + Sync>; /// Initialiazes a passthrough fs for a given layer +/// a passthrough fs is just a dummy implementation to map to the physical disk +/// # Usage +/// ``` +/// let passthrough_layer = new_passthroughfs_layer("/path/to/layer") +/// ``` fn new_passthroughfs_layer(rootdir: &str) -> Result { let mut config = passthrough::Config::default(); config.root_dir = String::from(rootdir); - // enable xattr config.xattr = true; config.do_import = true; let fs = Box::new(PassthroughFs::<()>::new(config)?); - fs.import()?; + fs.import() + .with_context(|| format!("Failed to create the passthrough layer: {}", rootdir))?; Ok(fs as BoxedLayer) } /// Ensure a destination folder is created fn ensure_folder_created(output_folder: &Path) -> Result<()> { - //TODO if there is already a folder, change names/delete it beforehand ? - let _ = fs::create_dir(output_folder); - // TODO actually make sure it works - Ok(()) + fs::create_dir(output_folder).with_context(|| { + format!( + "Failed to ensure folder creation: {}", + output_folder.to_string_lossy() + ) + }) } /// Merges all the layers into a single folder for further manipulation @@ -50,35 +57,39 @@ pub fn merge_layer(blob_paths: &[PathBuf], output_folder: &Path) -> Result<()> { // Stack all lower layers let mut lower_layers = Vec::new(); for lower in blob_paths { - lower_layers.push(Arc::new( - new_passthroughfs_layer(lower.to_str().unwrap()).unwrap(), - )); + lower_layers.push(Arc::new(new_passthroughfs_layer(&lower.to_string_lossy())?)); } let mountpoint = Path::new("/tmp/cloudlet_internal"); let fs_name = "cloudlet_overlay"; - let _ = ensure_folder_created(mountpoint); - let _ = ensure_folder_created(output_folder); + ensure_folder_created(mountpoint)?; + ensure_folder_created(output_folder)?; // Setup the overlay fs config let mut config = Config::default(); config.work = "/work".into(); - config.mountpoint = output_folder.to_str().unwrap().into(); + config.mountpoint = output_folder.to_string_lossy().into(); config.do_import = true; - let fs = OverlayFs::new(None, lower_layers, config).unwrap(); - fs.import().unwrap(); + let fs = OverlayFs::new(None, lower_layers, config) + .with_context(|| "Failed to construct the Overlay fs struct !".to_string())?; + fs.import() + .with_context(|| "Failed to initialize the overlay fs".to_string())?; // Enable a fuse session to make the fs available - let mut se = FuseSession::new(mountpoint, fs_name, "", true).unwrap(); + let mut se = FuseSession::new(mountpoint, fs_name, "", true) + .with_context(|| "Failed to construct the Fuse session")?; se.set_allow_other(false); - se.mount().unwrap(); + se.mount() + .with_context(|| "Failed to mount the overlay fs".to_string())?; // Fuse session let mut server = FuseServer { server: Arc::new(Server::new(Arc::new(fs))), - ch: se.new_channel().unwrap(), + ch: se + .new_channel() + .with_context(|| "Failed to create a new channel".to_string())?, }; let handle = thread::spawn(move || { @@ -87,52 +98,55 @@ pub fn merge_layer(blob_paths: &[PathBuf], output_folder: &Path) -> Result<()> { println!("copy starting !"); //So now we need to copy the files - let copy_res = dircpy::copy_dir(mountpoint, output_folder); - println!("copy finished ?, {:?}", copy_res); - - // main thread - // let mut signals = Signals::new(TERM_SIGNALS).unwrap(); - // for _sig in signals.forever() { - // break; - // } + dircpy::copy_dir(mountpoint, output_folder).with_context(|| { + format!( + "Failed to copy directories into the output folder: {}", + output_folder.to_string_lossy() + ) + })?; + println!("copy finished"); // Unmount sessions so it can be re-used in later executions of the program - se.umount().unwrap(); - se.wake().unwrap(); + se.wake() + .with_context(|| "Failed to exit the fuse session".to_string())?; + se.umount() + .with_context(|| "Failed to unmount the fuse session".to_string())?; let _ = handle.join(); - Ok(()) // TODO proper error handling + Ok(()) } impl FuseServer { + /// Run a loop to execute requests from the FUSE session + /// pub fn svc_loop(&mut self) -> Result<()> { - print!("entering server loop\n"); + println!("entering server loop"); loop { - match self.ch.get_request() { - Ok(value) => { - if let Some((reader, writer)) = value { - if let Err(e) = - self.server - .handle_message(reader, writer.into(), None, None) - { - match e { - fuse_backend_rs::Error::EncodeMessage(_ebadf) => { - break; - } - _ => { - print!("Handling fuse message failed"); - continue; - } - } - } - } else { - print!("fuse server exits"); + let value = self + .ch + .get_request() + .with_context(|| "Failed to get message from fuse session".to_string())?; + + if value.is_none() { + println!("fuse server exits"); + break; + } + + // Technically the unwrap is safe + let (reader, writer) = value.unwrap(); + + if let Err(e) = self + .server + .handle_message(reader, writer.into(), None, None) + { + match e { + fuse_backend_rs::Error::EncodeMessage(_ebadf) => { break; } - } - Err(err) => { - println!("{:?}", err); - break; + _ => { + print!("Handling fuse message failed"); + continue; + } } } }