diff --git a/src/main.rs b/src/main.rs index a06b81e..d55abdb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -67,8 +67,8 @@ struct Cli { /// Number of worker threads for AEAD work. Defaults to the number of /// available CPUs. Set to 1 for fully serial encrypt/decrypt. - #[clap(short = 'j', long)] - threads: Option, + #[clap(short = 'j', long, value_parser = clap::value_parser!(u32).range(1..))] + threads: Option, /// Random-access decrypt: byte offset of the slice to read. /// Requires `--decrypt`, an `--input-file` whose header has the @@ -175,7 +175,7 @@ fn run(mut cli: Cli) -> Result<(), FcryError> { let argon_memory = cli.argon_memory; let argon_passes = cli.argon_passes; let argon_parallelism = cli.argon_parallelism; - let threads = cli.threads.unwrap_or_else(|| { + let threads = cli.threads.map(|n| n as usize).unwrap_or_else(|| { std::thread::available_parallelism() .map(|n| n.get()) .unwrap_or(1) diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs index db87514..58e7349 100644 --- a/tests/roundtrip.rs +++ b/tests/roundtrip.rs @@ -723,6 +723,27 @@ fn random_access_tampered_length_fails() { ); } +#[test] +fn rejects_zero_threads() { + // -j 0 is almost certainly a user mistake. Clap should reject it before + // we ever reach the pipeline. + let dir = TempDir::new().unwrap(); + let plain = dir.path().join("p.bin"); + fs::write(&plain, b"hello").unwrap(); + let out = fcry() + .arg("-i") + .arg(&plain) + .arg("-o") + .arg(dir.path().join("c.bin")) + .arg("--raw-key") + .arg(KEY_STR) + .arg("-j") + .arg("0") + .output() + .unwrap(); + assert!(!out.status.success(), "-j 0 should be rejected"); +} + #[test] fn header_chunk_size_is_authoritative_on_decrypt() { // Encrypt with a non-default chunk size; decrypt without specifying one.