From 8df1f99f70bbfba8af725e8fb7c91ca901e18ef9 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Fri, 3 Feb 2023 19:12:40 -0500 Subject: [PATCH] Add `md5sum` applet --- Cargo.lock | 61 +++++++++++++++++++++++++ Cargo.toml | 1 + README.md | 1 + src/cmd/md5sum/mod.rs | 102 ++++++++++++++++++++++++++++++++++++++++++ src/cmd/mod.rs | 5 ++- 5 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 src/cmd/md5sum/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 4aaad80..bbaf1bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + [[package]] name = "cc" version = "1.0.78" @@ -76,12 +85,32 @@ dependencies = [ "roff", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "data-encoding" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "errno" version = "0.2.8" @@ -103,6 +132,16 @@ dependencies = [ "libc", ] +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -155,6 +194,15 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest", +] + [[package]] name = "os_str_bytes" version = "6.4.1" @@ -207,6 +255,7 @@ dependencies = [ "clap_mangen", "data-encoding", "libc", + "md-5", "sc", "termcolor", "textwrap", @@ -243,6 +292,18 @@ dependencies = [ "smawk", ] +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "walkdir" version = "2.3.2" diff --git a/Cargo.toml b/Cargo.toml index 4feb26b..68492eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ clap_complete_nushell = "0.1" clap_mangen = "0.2" data-encoding = "2.3" libc = "0.2" +md-5 = "0.10.5" sc = "0.2" termcolor = "1.1" textwrap = { version = "0.16", default-features = false, features = ["smawk"] } diff --git a/README.md b/README.md index 2010407..3dc16b6 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ code between applets, making for an overall smaller binary. - hostname - link - logname +- md5sum - mkfifo - mknod - mountpoint diff --git a/src/cmd/md5sum/mod.rs b/src/cmd/md5sum/mod.rs new file mode 100644 index 0000000..5e6e706 --- /dev/null +++ b/src/cmd/md5sum/mod.rs @@ -0,0 +1,102 @@ +use super::Cmd; +use clap::{Arg, ArgAction, Command}; +use md5::{Digest, Md5}; +use std::{ + fmt::Write, + fs::File, + io::{self, BufRead, BufReader, Read}, + process, error::Error, +}; + +#[derive(Debug, Default)] +pub struct Md5sum; + +impl Cmd for Md5sum { + fn cli(&self) -> clap::Command { + Command::new("md5sum") + .about("compute and check MD5 message digest") + .author("Nathan Fisher") + .version(env!("CARGO_PKG_VERSION")) + .args([ + Arg::new("check") + .help("read checksums from the FILEs and check them") + .short('c') + .long("check") + .action(ArgAction::SetTrue), + Arg::new("file") + .value_name("FILE") + .num_args(1..) + .default_value("-"), + ]) + } + + fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box> { + let Some(matches) = matches else { + return Err(io::Error::new(io::ErrorKind::Other, "no input").into()); + }; + if let Some(files) = matches.get_many::("file") { + let mut erred = 0; + for f in files { + if matches.get_flag("check") { + if f == "-" { + return Err( + io::Error::new(io::ErrorKind::Other, "no file specified").into() + ); + } + let fd = File::open(f)?; + let reader = BufReader::new(fd); + for line in reader.lines() { + let line = line?; + let mut split = line.split_whitespace(); + let sum = split.next().ok_or::>( + io::Error::new(io::ErrorKind::Other, "invalid checksum file").into(), + )?; + let file = split.next().ok_or::>( + io::Error::new(io::ErrorKind::Other, "invalid checksum file").into(), + )?; + let mut hasher = Md5::new(); + let mut buf = vec![]; + let mut fd = File::open(file)?; + let _s = fd.read_to_end(&mut buf)?; + hasher.update(&buf); + let res = hasher.finalize(); + let mut s = String::new(); + for c in res { + write!(s, "{c:x}")?; + } + if s.as_str() == sum { + println!("{file}: OK"); + } else { + println!("{file}: FAILED"); + erred += 1; + } + } + } else { + let mut hasher = Md5::new(); + let mut buf = vec![]; + if f == "-" { + let _s = io::stdin().read_to_end(&mut buf)?; + } else { + let mut fd = File::open(f)?; + let _s = fd.read_to_end(&mut buf)?; + } + hasher.update(&buf); + let res = hasher.finalize(); + for c in res { + print!("{c:x}"); + } + println!(" {f}"); + } + } + if erred > 0 { + println!("md5sum: WARNING: {erred} computed checksum did NOT match"); + process::exit(1); + } + } + Ok(()) + } + + fn path(&self) -> Option { + Some(crate::Path::UsrBin) + } +} diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 0eb3467..d386533 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -29,6 +29,7 @@ mod link; mod ln; mod logname; mod ls; +mod md5sum; mod mkfifo; mod mknod; mod mktemp; @@ -93,6 +94,7 @@ pub fn get(name: &str) -> Option> { "hostname" => Some(Box::new(hostname::Hostname::default())), "link" => Some(Box::new(link::Link::default())), "logname" => Some(Box::new(logname::Logname::default())), + "md5sum" => Some(Box::new(md5sum::Md5sum::default())), "mkfifo" => Some(Box::new(mkfifo::MkFifo::default())), "mknod" => Some(Box::new(mknod::MkNod::default())), "mktemp" => Some(Box::new(mktemp::MkTemp::default())), @@ -119,7 +121,7 @@ pub fn get(name: &str) -> Option> { } } -pub static COMMANDS: [&str; 43] = [ +pub static COMMANDS: [&str; 44] = [ "base32", "base64", "basename", @@ -141,6 +143,7 @@ pub static COMMANDS: [&str; 43] = [ "hostname", "link", "logname", + "md5sum", "mkfifo", "mknod", "mktemp",