| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| anyhow::Error, |
| argh::FromArgs, |
| fuchsia_async as fasync, |
| fxfs::{ |
| crypt::{insecure::InsecureCrypt, Crypt}, |
| filesystem::{mkfs, FxFilesystem, OpenOptions}, |
| fsck, |
| }, |
| std::{io::Read, path::Path, sync::Arc}, |
| storage_device::{file_backed_device::FileBackedDevice, DeviceHolder}, |
| tools::ops, |
| }; |
| |
| #[derive(FromArgs, PartialEq, Debug)] |
| /// fxfs |
| struct TopLevel { |
| /// whether to run the tool verbosely |
| #[argh(switch, short = 'v')] |
| verbose: bool, |
| #[argh(subcommand)] |
| subcommand: SubCommand, |
| } |
| |
| #[derive(FromArgs, PartialEq, Debug)] |
| #[argh(subcommand)] |
| enum SubCommand { |
| ImageEdit(ImageEditCommand), |
| CreateGolden(CreateGoldenSubCommand), |
| CheckGolden(CheckGoldenSubCommand), |
| } |
| |
| #[derive(FromArgs, PartialEq, Debug)] |
| #[argh(subcommand, name = "image", description = "disk image manipulation commands")] |
| struct ImageEditCommand { |
| /// path to the image file to read or write |
| #[argh(option, short = 'f')] |
| file: String, |
| #[argh(subcommand)] |
| subcommand: ImageSubCommand, |
| } |
| |
| #[derive(FromArgs, PartialEq, Debug)] |
| #[argh(subcommand)] |
| enum ImageSubCommand { |
| Format(FormatSubCommand), |
| Fsck(FsckSubCommand), |
| Get(GetSubCommand), |
| Ls(LsSubCommand), |
| Mkdir(MkdirSubCommand), |
| Put(PutSubCommand), |
| Rm(RmSubCommand), |
| Rmdir(RmdirSubCommand), |
| } |
| |
| #[derive(FromArgs, PartialEq, Debug)] |
| /// copies files from the image to the host filesystem, overwriting existing files. |
| #[argh(subcommand, name = "get")] |
| struct GetSubCommand { |
| /// source file in image. |
| #[argh(positional)] |
| src: String, |
| /// destination filename on host filesystem. |
| #[argh(positional)] |
| dst: String, |
| } |
| #[derive(FromArgs, PartialEq, Debug)] |
| /// copies files from the host filesystem to the image, overwriting existing files. |
| #[argh(subcommand, name = "put")] |
| struct PutSubCommand { |
| /// source file on host filesystem. |
| #[argh(positional)] |
| src: String, |
| /// destination filename in image. |
| #[argh(positional)] |
| dst: String, |
| } |
| |
| #[derive(FromArgs, PartialEq, Debug)] |
| /// copies files from the host filesystem to the image, overwriting existing files. |
| #[argh(subcommand, name = "rm")] |
| struct RmSubCommand { |
| /// path to remove from image. |
| #[argh(positional)] |
| path: String, |
| } |
| |
| #[derive(FromArgs, PartialEq, Debug)] |
| /// format the file or block device as an empty Fxfs filesystem |
| #[argh(subcommand, name = "mkfs")] |
| struct FormatSubCommand {} |
| |
| #[derive(FromArgs, PartialEq, Debug)] |
| /// verify the integrity of the filesystem image |
| #[argh(subcommand, name = "fsck")] |
| struct FsckSubCommand {} |
| |
| #[derive(FromArgs, PartialEq, Debug)] |
| /// List all files |
| #[argh(subcommand, name = "ls")] |
| struct LsSubCommand { |
| #[argh(positional)] |
| /// path to list. |
| path: String, |
| } |
| |
| #[derive(FromArgs, PartialEq, Debug)] |
| /// Create a new directory |
| #[argh(subcommand, name = "mkdir")] |
| struct MkdirSubCommand { |
| #[argh(positional)] |
| /// path to create. |
| path: String, |
| } |
| |
| #[derive(FromArgs, PartialEq, Debug)] |
| /// Create a new directory |
| #[argh(subcommand, name = "rmdir")] |
| struct RmdirSubCommand { |
| #[argh(positional)] |
| /// path to create. |
| path: String, |
| } |
| |
| #[derive(FromArgs, PartialEq, Debug)] |
| /// Create a golden image at current filesystem version. |
| #[argh(subcommand, name = "create_golden")] |
| struct CreateGoldenSubCommand {} |
| |
| #[derive(FromArgs, PartialEq, Debug)] |
| /// Check all golden images at current filesystem version. |
| #[argh(subcommand, name = "check_golden")] |
| struct CheckGoldenSubCommand { |
| #[argh(option)] |
| /// path to golden images directory. derived from FUCHSIA_DIR if not set. |
| images_dir: Option<String>, |
| } |
| |
| struct SimpleLogger; |
| |
| impl log::Log for SimpleLogger { |
| fn enabled(&self, _metadata: &log::Metadata<'_>) -> bool { |
| true |
| } |
| |
| fn log(&self, record: &log::Record<'_>) { |
| if self.enabled(record.metadata()) { |
| if record.level() <= log::Level::Warn { |
| eprintln!("[{}] {}", record.level(), record.args()); |
| } else { |
| println!("[{}] {}", record.level(), record.args()); |
| } |
| } |
| } |
| |
| fn flush(&self) {} |
| } |
| |
| const LOGGER: SimpleLogger = SimpleLogger {}; |
| |
| #[fasync::run(10)] |
| async fn main() -> Result<(), Error> { |
| log::set_logger(&LOGGER)?; |
| log::set_max_level(log::LevelFilter::Info); |
| log::debug!("fxfs {:?}", std::env::args()); |
| |
| let args: TopLevel = argh::from_env(); |
| match args.subcommand { |
| SubCommand::ImageEdit(cmd) => { |
| // TODO(fxbug.dev/95403): Add support for side-loaded encryption keys. |
| let crypt: Arc<dyn Crypt> = Arc::new(InsecureCrypt::new()); |
| match cmd.subcommand { |
| ImageSubCommand::Rm(rmargs) => { |
| let device = DeviceHolder::new(FileBackedDevice::new( |
| std::fs::OpenOptions::new().read(true).write(true).open(cmd.file)?, |
| )); |
| let fs = FxFilesystem::open(device).await?; |
| let vol = ops::open_volume(&fs, crypt.clone()).await?; |
| ops::unlink(&fs, &vol, &Path::new(&rmargs.path)).await?; |
| fs.close().await?; |
| ops::fsck(&fs, crypt, args.verbose).await |
| } |
| ImageSubCommand::Get(getargs) => { |
| let device = DeviceHolder::new(FileBackedDevice::new( |
| std::fs::OpenOptions::new().read(true).open(cmd.file)?, |
| )); |
| let fs = FxFilesystem::open_with_options( |
| device, |
| OpenOptions { read_only: true, ..OpenOptions::default() }, |
| ) |
| .await?; |
| let vol = ops::open_volume(&fs, crypt).await?; |
| let data = ops::get(&vol, &Path::new(&getargs.src)).await?; |
| let mut reader = std::io::Cursor::new(&data); |
| let mut writer = std::fs::File::create(getargs.dst)?; |
| std::io::copy(&mut reader, &mut writer)?; |
| Ok(()) |
| } |
| ImageSubCommand::Put(putargs) => { |
| let device = DeviceHolder::new(FileBackedDevice::new( |
| std::fs::OpenOptions::new().read(true).write(true).open(cmd.file)?, |
| )); |
| let fs = FxFilesystem::open(device).await?; |
| let vol = ops::open_volume(&fs, crypt.clone()).await?; |
| let mut data = Vec::new(); |
| std::fs::File::open(&putargs.src)?.read_to_end(&mut data)?; |
| ops::put(&fs, &vol, &Path::new(&putargs.dst), data).await?; |
| fs.close().await?; |
| ops::fsck(&fs, crypt, args.verbose).await |
| } |
| ImageSubCommand::Format(_) => { |
| let device = DeviceHolder::new(FileBackedDevice::new( |
| std::fs::OpenOptions::new().read(true).write(true).open(cmd.file)?, |
| )); |
| log::set_max_level(log::LevelFilter::Info); |
| mkfs(device, Some(crypt)).await?; |
| Ok(()) |
| } |
| ImageSubCommand::Fsck(_) => { |
| let device = DeviceHolder::new(FileBackedDevice::new( |
| std::fs::OpenOptions::new().read(true).open(cmd.file)?, |
| )); |
| log::set_max_level(log::LevelFilter::Info); |
| let fs = FxFilesystem::open_with_options( |
| device, |
| OpenOptions { read_only: true, ..OpenOptions::default() }, |
| ) |
| .await?; |
| let options = fsck::FsckOptions { |
| fail_on_warning: false, |
| halt_on_error: false, |
| do_slow_passes: true, |
| on_error: |err| eprintln!("{:?}", err.to_string()), |
| verbose: args.verbose, |
| }; |
| fsck::fsck_with_options(&fs, Some(crypt), options).await |
| } |
| ImageSubCommand::Ls(lsargs) => { |
| let device = DeviceHolder::new(FileBackedDevice::new( |
| std::fs::OpenOptions::new().read(true).open(cmd.file)?, |
| )); |
| let fs = FxFilesystem::open_with_options( |
| device, |
| OpenOptions { read_only: true, ..OpenOptions::default() }, |
| ) |
| .await?; |
| let vol = ops::open_volume(&fs, crypt).await?; |
| let dir = ops::walk_dir(&vol, &Path::new(&lsargs.path)).await?; |
| ops::print_ls(&dir).await?; |
| Ok(()) |
| } |
| ImageSubCommand::Mkdir(mkdirargs) => { |
| let device = DeviceHolder::new(FileBackedDevice::new( |
| std::fs::OpenOptions::new().read(true).write(true).open(cmd.file)?, |
| )); |
| let fs = FxFilesystem::open(device).await?; |
| let vol = ops::open_volume(&fs, crypt.clone()).await?; |
| ops::mkdir(&fs, &vol, &Path::new(&mkdirargs.path)).await?; |
| fs.close().await?; |
| ops::fsck(&fs, crypt, args.verbose).await |
| } |
| ImageSubCommand::Rmdir(rmdirargs) => { |
| let device = DeviceHolder::new(FileBackedDevice::new( |
| std::fs::OpenOptions::new().read(true).write(true).open(cmd.file)?, |
| )); |
| let fs = FxFilesystem::open(device).await?; |
| let vol = ops::open_volume(&fs, crypt.clone()).await?; |
| ops::unlink(&fs, &vol, &Path::new(&rmdirargs.path)).await?; |
| fs.close().await?; |
| ops::fsck(&fs, crypt, args.verbose).await |
| } |
| } |
| } |
| SubCommand::CreateGolden(_) => tools::golden::create_image().await, |
| SubCommand::CheckGolden(args) => tools::golden::check_images(args.images_dir).await, |
| } |
| } |