blob: f038ecaf6d91279394756d283c574cfa820c1cb4 [file] [log] [blame]
// 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,
}
}