|
@@ -8,38 +8,86 @@ use block_dev::BlockMem;
|
|
|
use clap::Parser;
|
|
|
use fuse_fs::StateExt4FuseFs;
|
|
|
use fuser::MountOption;
|
|
|
-use log::{error, info};
|
|
|
+use log::LevelFilter;
|
|
|
use simple_logger::SimpleLogger;
|
|
|
-use std::sync::Arc;
|
|
|
+use std::sync::{Arc, OnceLock};
|
|
|
|
|
|
#[derive(Parser, Debug)]
|
|
|
+#[command(about = "Another ext4 FUSE Tool")]
|
|
|
struct Args {
|
|
|
/// Fs mount point
|
|
|
#[arg(short, long)]
|
|
|
mountpoint: String,
|
|
|
-
|
|
|
- /// Fs block count
|
|
|
- #[arg(short, long, default_value_t = 4096)]
|
|
|
+ /// Load initial image
|
|
|
+ #[arg(short, long)]
|
|
|
+ image: Option<String>,
|
|
|
+ /// Fs total block number, ignored when [image] is set
|
|
|
+ #[arg(short, long, default_value_t = 8192)]
|
|
|
block: u64,
|
|
|
+ /// Save image on exit
|
|
|
+ #[arg(short, long)]
|
|
|
+ output: Option<String>,
|
|
|
+ /// Log level
|
|
|
+ #[arg(short, long, default_value_t = String::from("info"))]
|
|
|
+ log: String,
|
|
|
}
|
|
|
|
|
|
+fn parse_log_level(level_str: &str) -> LevelFilter {
|
|
|
+ match level_str.to_lowercase().as_str() {
|
|
|
+ "off" => LevelFilter::Off,
|
|
|
+ "error" => LevelFilter::Error,
|
|
|
+ "warn" => LevelFilter::Warn,
|
|
|
+ "info" => LevelFilter::Info,
|
|
|
+ "debug" => LevelFilter::Debug,
|
|
|
+ "trace" => LevelFilter::Trace,
|
|
|
+ _ => LevelFilter::Off,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// Global exit flag
|
|
|
+static EXIT_FLAG: OnceLock<bool> = OnceLock::new();
|
|
|
+
|
|
|
fn main() {
|
|
|
let args = Args::parse();
|
|
|
|
|
|
+ // Initialize logger
|
|
|
+ println!("Log level {}", args.log);
|
|
|
SimpleLogger::new().init().unwrap();
|
|
|
- log::set_max_level(log::LevelFilter::Trace);
|
|
|
- info!("Use mountpoint \"{}\"", args.mountpoint);
|
|
|
+ log::set_max_level(parse_log_level(&args.log));
|
|
|
|
|
|
// Initialize block device and filesystem
|
|
|
- let block_mem = Arc::new(BlockMem::new(args.block));
|
|
|
- block_mem.mkfs();
|
|
|
- let fs = StateExt4FuseFs::new(block_mem);
|
|
|
+ let block_mem = if let Some(image) = &args.image {
|
|
|
+ println!("Load image {}", image);
|
|
|
+ Arc::new(BlockMem::load(&image))
|
|
|
+ } else {
|
|
|
+ println!("Create disk image with {} blocks", args.block);
|
|
|
+ let block_mem = Arc::new(BlockMem::new(args.block));
|
|
|
+ block_mem.mkfs();
|
|
|
+ block_mem
|
|
|
+ };
|
|
|
+ // Create filesystem and init if image is newly created
|
|
|
+ let fs = StateExt4FuseFs::new(block_mem.clone(), args.image.is_none());
|
|
|
|
|
|
// Mount fs and enter session loop
|
|
|
+ println!("Mount ext4fs to {}", args.mountpoint);
|
|
|
let options = Vec::<MountOption>::new();
|
|
|
- info!("Mount ext4fs to \"{}\"", args.mountpoint);
|
|
|
- let res = fuser::mount2(fs, &args.mountpoint, &options);
|
|
|
- if let Err(e) = res {
|
|
|
- error!("Error occured: {:?}", e);
|
|
|
+ let session =
|
|
|
+ fuser::spawn_mount2(fs, &args.mountpoint, &options).expect("Failed to start FUSE session");
|
|
|
+
|
|
|
+ // Set EXIT_FLAG when Ctrl+C is received
|
|
|
+ let _ = ctrlc::set_handler(|| {
|
|
|
+ EXIT_FLAG.get_or_init(|| true);
|
|
|
+ });
|
|
|
+ // Loop until EXIT_FLAG is set
|
|
|
+ loop {
|
|
|
+ if EXIT_FLAG.get().is_some() {
|
|
|
+ println!("Received Ctrl+C, exiting...");
|
|
|
+ if let Some(output) = &args.output {
|
|
|
+ println!("Save image {}", output);
|
|
|
+ block_mem.save(output);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
+ session.join();
|
|
|
}
|