Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,31 @@ Utilities to work with CD/DVD media, ISO 9660 and UDF images.
| iso-ls | List files of ISO 9660 and UDF filesystems. |
| mmc-cli | Issue SCSI MMC commands to a drive. |

## iso-cp
Copies files from ISO 9660 or UDF filesystem.
```console
$ iso-cp -h
Copy files from an ISO 9660 or UDF filesystem

Usage: iso-cp <IMAGE> <SOURCE> <DESTINATION>

Arguments:
<IMAGE> Path to an ISO 9660 or UDF image
<SOURCE> Path to a source file in the image
<DESTINATION> Path to a destination file or directory

Options:
-h, --help Print help (see more with '--help')
-V, --version Print version
$ # Copying a license file from a UDF filesystem
$ iso-cp tests/data/udf1.iso licenses/COPYING.LESSER ./lgpl
$ cat lgpl | head -2
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
```

Copying whole directories is currently not supported.

## Install
- Install [Rust][rust-install].
- Install [clang][bindgen-reqs].
Expand Down
12 changes: 12 additions & 0 deletions src/drive-info/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,15 @@ pub struct DriveArg {
#[arg(value_name = "DRIVE")]
pub positional: Option<PathBuf>,
}

#[cfg(test)]
mod tests {
use clap::CommandFactory;

use super::*;

#[test]
fn verify_cli() {
Cli::command().debug_assert();
}
}
50 changes: 24 additions & 26 deletions src/iso-cp/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,35 @@

use std::path::PathBuf;

use clap::{Args, Parser};
use clap::Parser;

/// Extract files from ISO 9660 and UDF files.
/// Copy files from an ISO 9660 or UDF filesystem.
#[derive(Parser)]
#[command(arg_required_else_help = true, version)]
pub struct Cli {
/// Path to the file in the image to extract
#[arg(short, long, value_name = "FILE")]
pub extract: String,

/// Path to an ISO9660 and/or UDF image
#[command(flatten)]
pub image: FileArg,

/// Path of the output file. Defaults to name of the extracted file.
#[arg(short, long, value_name = "FILE")]
pub output_file: Option<PathBuf>,

/// Use UDF
#[arg(short = 'U', long)]
pub udf: bool,
/// Path to an ISO 9660 or UDF image.
#[arg(value_name = "IMAGE")]
pub image: PathBuf,

/// Path to a source file in the image.
///
/// Directories are currently not supported.
#[arg(value_name = "SOURCE")]
pub source: String,

/// Path to a destination file or directory.
#[arg(value_name = "DESTINATION")]
pub destination: PathBuf,
}

#[derive(Args)]
#[group(required = true, multiple = false)]
pub struct FileArg {
/// Path to an ISO9660 and/or UDF image
#[arg(short = 'i', long = "image", value_name = "FILE")]
pub option: Option<PathBuf>,
#[cfg(test)]
mod tests {
use clap::CommandFactory;

use super::*;

/// Path to an ISO9660 and/or UDF image
#[arg(value_name = "FILE")]
pub positional: Option<PathBuf>,
#[test]
fn verify_cli() {
Cli::command().debug_assert();
}
}
43 changes: 28 additions & 15 deletions src/iso-cp/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,38 +31,51 @@ fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.init();
let image = cli.image.positional.or(cli.image.option)
.expect( "the cli logic must ensure that the file argument is provided either as a positional or as an option");
let image = cli.image;
if !image.exists() {
bail!("could not open input file at {}", image.display());
}

let output = cli.output_file.unwrap_or(PathBuf::from(&cli.extract));
let output = if cli.destination.is_dir() {
let source = PathBuf::from(&cli.source);
let source_file = source.file_name().context("invalid source file name")?;
cli.destination.join(source_file)
} else {
cli.destination
};
let mut output = File::create(output).context("could not create output file")?;

if cli.udf {
udf_extract(image, cli.extract, &mut output)?;
} else {
iso9660_extract(image, cli.extract, &mut output)?;
}
let iso_err = match Iso::new(image.clone()) {
Ok(iso) => return iso9660_extract(&iso, cli.source, &mut output),
Err(err) => err,
};

Ok(())
match Udf::new(image) {
Ok(udf) => udf_extract(&udf, cli.source, &mut output),
Err(udf_err) => bail!(
"could not open file as ISO 9660 or UDF\n ISO error: {iso_err:?}\nUDF error: {udf_err:?}",
),
}
}

/// Extract given file from a UDF image.
fn udf_extract(image: PathBuf, extract: String, output: &mut File) -> Result<()> {
let udf = Udf::new(image)?;
let entry = udf.entry(extract)?;
fn udf_extract(udf: &Udf, source: String, output: &mut File) -> Result<()> {
let entry = udf.entry(source)?;
if entry.is_dir() {
bail!("copying directories is currently not supported");
}

io::copy(&mut entry.reader(), output)?;

Ok(())
}

/// Extract given file from an ISO 9660 image.
fn iso9660_extract(image: PathBuf, extract: String, output: &mut File) -> Result<()> {
let iso = Iso::new(image.clone())?;
let entry = iso.entry(extract)?;
fn iso9660_extract(iso: &Iso, source: String, output: &mut File) -> Result<()> {
let entry = iso.entry(source)?;
if entry.is_dir() {
bail!("copying directories is currently not supported");
}

io::copy(&mut entry.reader(), output)?;

Expand Down
12 changes: 12 additions & 0 deletions src/iso-ls/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,15 @@ pub struct FileArg {
#[arg(value_name = "FILE")]
pub positional: Option<PathBuf>,
}

#[cfg(test)]
mod tests {
use clap::CommandFactory;

use super::*;

#[test]
fn verify_cli() {
Cli::command().debug_assert();
}
}
12 changes: 12 additions & 0 deletions src/mmc-cli/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,15 @@ pub struct MmcActions {
#[arg(short = 'S', long)]
pub speed: Option<u16>,
}

#[cfg(test)]
mod tests {
use clap::CommandFactory;

use super::*;

#[test]
fn verify_cli() {
Cli::command().debug_assert();
}
}
11 changes: 2 additions & 9 deletions tests/iso-cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,9 @@ static UDF_FILE: &str = "tests/data/udf1.iso";
fn extract_udf() {
let output = NamedTempFile::new("out").unwrap();
cmd()
.arg("-e")
.arg("licenses/COPYING")
.arg("-i")
.arg(UDF_FILE)
.arg("-o")
.arg("licenses/COPYING")
.arg(output.path())
.arg("-U")
.assert()
.success();

Expand All @@ -31,11 +27,8 @@ static ISO9660_FILE: &str = "tests/data/xa.iso";
fn extract_iso9660() {
let output = NamedTempFile::new("out").unwrap();
cmd()
.arg("-e")
.arg("copying")
.arg("-i")
.arg(ISO9660_FILE)
.arg("-o")
.arg("copying")
.arg(output.path())
.assert()
.success();
Expand Down