diff --git a/Cargo.lock b/Cargo.lock index 59070ec..249b90d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,24 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "arrayref" version = "0.3.6" @@ -25,12 +43,39 @@ dependencies = [ "winapi", ] +[[package]] +name = "bindgen" +version = "0.58.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f8523b410d7187a43085e7e064416ea32ded16bd0a4e6fc025e21616d01258f" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "clap 2.34.0", + "env_logger", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "which", +] + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags-mini" +version = "0.1.0" + [[package]] name = "blake2b_simd" version = "1.0.0" @@ -42,6 +87,30 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "blkid" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f68440dfd06d6a97a56f8e67729316f33c7e52c31f59f16f6e9641777702bb7" +dependencies = [ + "bitflags", + "blkid-sys", + "libc", + "pkg-config", + "strum", + "strum_macros", + "thiserror", +] + +[[package]] +name = "blkid-sys" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d31e0255bb872b7d67a444b670eb55b5b2b46d59c70ab0376e2e04737c7a489e" +dependencies = [ + "bindgen", +] + [[package]] name = "block-buffer" version = "0.10.3" @@ -57,12 +126,47 @@ version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +[[package]] +name = "cexpr" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clang-sys" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim 0.8.0", + "textwrap 0.11.0", + "unicode-width", + "vec_map", +] + [[package]] name = "clap" version = "4.1.4" @@ -72,7 +176,7 @@ dependencies = [ "bitflags", "clap_lex", "is-terminal", - "strsim", + "strsim 0.10.0", "termcolor", ] @@ -82,7 +186,7 @@ version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6540eedc41f8a5a76cf3d8d458057dcdf817be4158a55b5f861f7a5483de75" dependencies = [ - "clap", + "clap 4.1.4", ] [[package]] @@ -91,7 +195,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7fa41f5e6aa83bd151b70fd0ceaee703d68cd669522795dc812df9edad1252c" dependencies = [ - "clap", + "clap 4.1.4", "clap_complete", ] @@ -110,7 +214,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb258c6232b4d728d13d6072656627924c16707aae6267cd5a1ea05abff9a25c" dependencies = [ - "clap", + "clap 4.1.4", "roff", ] @@ -155,6 +259,19 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "errno" version = "0.2.8" @@ -186,6 +303,21 @@ dependencies = [ "version_check", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -201,6 +333,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "io-lifetimes" version = "1.0.5" @@ -223,18 +361,49 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "linux-raw-sys" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + [[package]] name = "md-5" version = "0.10.5" @@ -244,18 +413,102 @@ dependencies = [ "digest", ] +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mount" +version = "0.1.0" +dependencies = [ + "blkid", + "sc", +] + +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "memchr", + "version_check", +] + [[package]] name = "os_str_bytes" version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pw" +version = "0.1.0" +dependencies = [ + "libc", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + [[package]] name = "roff" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustix" version = "0.36.8" @@ -270,6 +523,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rustversion" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" + [[package]] name = "same-file" version = "1.0.6" @@ -312,8 +571,9 @@ name = "shitbox" version = "0.1.0" dependencies = [ "atty", + "bitflags-mini", "blake2b_simd", - "clap", + "clap 4.1.4", "clap_complete", "clap_complete_nushell", "clap_mangen", @@ -321,26 +581,72 @@ dependencies = [ "digest", "libc", "md-5", + "mount", + "pw", "sc", "sha1", "sha2", "termcolor", - "textwrap", + "textwrap 0.16.0", + "unistd", + "unix-mode", "walkdir", ] +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + [[package]] name = "smawk" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cae14b91c7d11c9a851d3fbc80a963198998c2a64eec840477fa92d8ce9b70bb" + +[[package]] +name = "strum_macros" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -350,6 +656,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "textwrap" version = "0.16.0" @@ -359,12 +674,72 @@ dependencies = [ "smawk", ] +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "typenum" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unistd" +version = "0.1.0" +dependencies = [ + "libc", + "sc", +] + +[[package]] +name = "unix-mode" +version = "0.1.0" +dependencies = [ + "bitflags-mini", + "libc", +] + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" @@ -382,6 +757,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "which" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" +dependencies = [ + "libc", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 800ece7..c82269f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,20 @@ name = "shitbox" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[workspace] +members = [ "bitflags-mini", "mount", "pw", "unistd", "unix-mode" ] + +[[bin]] +name = "corebox" +path = "corebox/main.rs" + +[[bin]] +name = "hashbox" +path = "hashbox/main.rs" + +[workspace.dependencies] +libc = "0.2" +sc = "0.2" [dependencies] atty = "0.2" @@ -14,15 +27,33 @@ clap_complete_nushell = "0.1" clap_mangen = "0.2" data-encoding = "2.3" digest = "0.10.6" -libc = "0.2" -md-5 = "0.10" -sc = "0.2" +libc = { workspace = true } +md5 = { version = "0.10", package = "md-5" } +sc = { workspace = true } sha1 = "0.10" sha2 = "0.10" termcolor = "1.1" textwrap = { version = "0.16", default-features = false, features = ["smawk"] } walkdir = "2.3" +[dependencies.bitflags] +package = "bitflags-mini" +path = "bitflags-mini" + +[dependencies.unistd] +package = "unistd" +path = "unistd" + +[dependencies.mode] +package = "unix-mode" +path = "unix-mode" + +[dependencies.mount] +path = "mount" + +[dependencies.pw] +path = "pw" + [profile.release] codegen-units = 1 strip = true diff --git a/bitflags-mini/Cargo.toml b/bitflags-mini/Cargo.toml new file mode 100644 index 0000000..362abe7 --- /dev/null +++ b/bitflags-mini/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "bitflags-mini" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/bitflags/mod.rs b/bitflags-mini/src/lib.rs similarity index 100% rename from src/bitflags/mod.rs rename to bitflags-mini/src/lib.rs diff --git a/src/cmd/base32/mod.rs b/corebox/commands/base32/mod.rs similarity index 97% rename from src/cmd/base32/mod.rs rename to corebox/commands/base32/mod.rs index ee48c7e..83fee48 100644 --- a/src/cmd/base32/mod.rs +++ b/corebox/commands/base32/mod.rs @@ -1,5 +1,5 @@ use super::Cmd; -use crate::args; +use shitbox::args; use clap::{value_parser, Arg, ArgAction, Command}; use data_encoding::BASE32; use std::{ @@ -68,8 +68,8 @@ impl Cmd for Base32 { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/base64/mod.rs b/corebox/commands/base64/mod.rs similarity index 97% rename from src/cmd/base64/mod.rs rename to corebox/commands/base64/mod.rs index 8e176ca..95b01e7 100644 --- a/src/cmd/base64/mod.rs +++ b/corebox/commands/base64/mod.rs @@ -63,8 +63,8 @@ impl Cmd for Base64 { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/basename/mod.rs b/corebox/commands/basename/mod.rs similarity index 94% rename from src/cmd/basename/mod.rs rename to corebox/commands/basename/mod.rs index 3fb0a45..356e41f 100644 --- a/src/cmd/basename/mod.rs +++ b/corebox/commands/basename/mod.rs @@ -41,7 +41,7 @@ impl Cmd for Basename { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/bootstrap/mod.rs b/corebox/commands/bootstrap/mod.rs similarity index 94% rename from src/cmd/bootstrap/mod.rs rename to corebox/commands/bootstrap/mod.rs index 4b2cc73..9380c87 100644 --- a/src/cmd/bootstrap/mod.rs +++ b/corebox/commands/bootstrap/mod.rs @@ -154,7 +154,7 @@ impl Cmd for Bootstrap { fn run(&self, matches: &ArgMatches) -> Result<(), Box> { if let Some(prefix) = matches.get_one::("prefix") { let usr = matches.get_flag("usr"); - if let Some(progpath) = crate::progpath() { + if let Some(progpath) = shitbox::progpath() { let mut outpath = PathBuf::from(prefix); outpath.push("bin"); println!("Installing binary:"); @@ -191,7 +191,7 @@ impl Cmd for Bootstrap { Ok(()) } - fn path(&self) -> Option { + fn path(&self) -> Option { None } } @@ -239,16 +239,16 @@ impl BootstrapCmd for dyn Cmd { if let Some(linkpath) = self.linkpath(prefix, usr) { if soft { let binpath = match self.path().unwrap() { - crate::Path::Bin => "shitbox", - crate::Path::Sbin => "../bin/shitbox", - crate::Path::UsrBin => { + shitbox::Path::Bin => "shitbox", + shitbox::Path::Sbin => "../bin/shitbox", + shitbox::Path::UsrBin => { if usr { "../../bin/shitbox" } else { "shitbox" } } - crate::Path::UsrSbin => { + shitbox::Path::UsrSbin => { if usr { "../../bin/shitbox" } else { @@ -294,7 +294,7 @@ fn manpages(prefix: &str) -> Result<(), io::Error> { println!("Generating Unix man pages:"); COMMANDS .iter() - .try_for_each(|cmd| crate::cmd::get(cmd).unwrap().manpage(prefix))?; + .try_for_each(|cmd| super::get(cmd).unwrap().manpage(prefix))?; Ok(()) } @@ -305,35 +305,35 @@ fn completions(prefix: &str, matches: &ArgMatches) -> Result<(), io::Error> { .iter() .collect(); COMMANDS.iter().try_for_each(|cmd| { - let cmd = crate::cmd::get(cmd).unwrap(); + let cmd = super::get(cmd).unwrap(); cmd.completion(&outdir, "bash") })?; } if matches.get_flag("fish") || matches.get_flag("all") { let outdir: PathBuf = [prefix, "share", "fish", "completions"].iter().collect(); COMMANDS.iter().try_for_each(|cmd| { - let cmd = crate::cmd::get(cmd).unwrap(); + let cmd = super::get(cmd).unwrap(); cmd.completion(&outdir, "fish") })?; } if matches.get_flag("nu") || matches.get_flag("all") { let outdir: PathBuf = [prefix, "share", "nu", "completions"].iter().collect(); COMMANDS.iter().try_for_each(|cmd| { - let cmd = crate::cmd::get(cmd).unwrap(); + let cmd = super::get(cmd).unwrap(); cmd.completion(&outdir, "nu") })?; } if matches.get_flag("pwsh") || matches.get_flag("all") { let outdir: PathBuf = [prefix, "share", "pwsh", "completions"].iter().collect(); COMMANDS.iter().try_for_each(|cmd| { - let cmd = crate::cmd::get(cmd).unwrap(); + let cmd = super::get(cmd).unwrap(); cmd.completion(&outdir, "pwsh") })?; } if matches.get_flag("zsh") || matches.get_flag("all") { let outdir: PathBuf = [prefix, "share", "zsh", "site-functions"].iter().collect(); COMMANDS.iter().try_for_each(|cmd| { - let cmd = crate::cmd::get(cmd).unwrap(); + let cmd = super::get(cmd).unwrap(); cmd.completion(&outdir, "zsh") })?; } @@ -371,7 +371,7 @@ fn links(prefix: &str, usr: bool, cmd: &ArgMatches) -> Result<(), Box } let soft = cmd.get_flag("soft"); COMMANDS.iter().try_for_each(|c| { - let cmd = crate::cmd::get(c).unwrap(); + let cmd = super::get(c).unwrap(); cmd.link(prefix, usr, soft) })?; Ok(()) diff --git a/src/cmd/cat/mod.rs b/corebox/commands/cat/mod.rs similarity index 100% rename from src/cmd/cat/mod.rs rename to corebox/commands/cat/mod.rs diff --git a/src/cmd/chmod/mod.rs b/corebox/commands/chmod/mod.rs similarity index 97% rename from src/cmd/chmod/mod.rs rename to corebox/commands/chmod/mod.rs index ed7fd8b..679b6e9 100644 --- a/src/cmd/chmod/mod.rs +++ b/corebox/commands/chmod/mod.rs @@ -1,9 +1,7 @@ use super::{Cmd, Feedback}; -use crate::{ - args, - mode::{Mode, Parser}, -}; +use shitbox::args; use clap::{Arg, ArgAction, Command}; +use mode::{Mode, Parser}; use std::{ error::Error, fs::{self, File, Permissions}, @@ -78,8 +76,8 @@ impl Cmd for Chmod { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::Bin) + fn path(&self) -> Option { + Some(shitbox::Path::Bin) } } diff --git a/src/cmd/chown/chgrp.rs b/corebox/commands/chown/chgrp.rs similarity index 96% rename from src/cmd/chown/chgrp.rs rename to corebox/commands/chown/chgrp.rs index 8613475..e219715 100644 --- a/src/cmd/chown/chgrp.rs +++ b/corebox/commands/chown/chgrp.rs @@ -1,6 +1,5 @@ -use super::{Group, Recurse, Traversal}; -use crate::cmd::{Cmd, Feedback}; -use crate::pw; +use super::{Feedback, Group, Recurse, Traversal}; +use shitbox::Cmd; use clap::{Arg, ArgGroup, Command, ValueHint}; use std::{ error::Error, @@ -71,8 +70,8 @@ impl Cmd for Chgrp { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::Bin) + fn path(&self) -> Option { + Some(shitbox::Path::Bin) } } diff --git a/src/cmd/chown/mod.rs b/corebox/commands/chown/mod.rs similarity index 98% rename from src/cmd/chown/mod.rs rename to corebox/commands/chown/mod.rs index 0a7b8fd..7113639 100644 --- a/src/cmd/chown/mod.rs +++ b/corebox/commands/chown/mod.rs @@ -1,5 +1,5 @@ use super::{Cmd, Feedback}; -use crate::{args, pw}; +use shitbox::args; use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command, ValueHint}; use std::{ error::Error, @@ -82,8 +82,8 @@ impl Cmd for Chown { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::Bin) + fn path(&self) -> Option { + Some(shitbox::Path::Bin) } } diff --git a/src/cmd/chroot/mod.rs b/corebox/commands/chroot/mod.rs similarity index 96% rename from src/cmd/chroot/mod.rs rename to corebox/commands/chroot/mod.rs index 8c66c87..4a19b12 100644 --- a/src/cmd/chroot/mod.rs +++ b/corebox/commands/chroot/mod.rs @@ -62,7 +62,7 @@ impl Cmd for Chroot { return Err(command.exec().into()); } - fn path(&self) -> Option { - Some(crate::Path::UsrSbin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrSbin) } } diff --git a/src/cmd/clear/mod.rs b/corebox/commands/clear/mod.rs similarity index 85% rename from src/cmd/clear/mod.rs rename to corebox/commands/clear/mod.rs index b576b04..eb1dfe8 100644 --- a/src/cmd/clear/mod.rs +++ b/corebox/commands/clear/mod.rs @@ -18,7 +18,7 @@ impl Cmd for Clear { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/shitbox/mod.rs b/corebox/commands/corebox/mod.rs similarity index 76% rename from src/cmd/shitbox/mod.rs rename to corebox/commands/corebox/mod.rs index 664db57..42b8b6b 100644 --- a/src/cmd/shitbox/mod.rs +++ b/corebox/commands/corebox/mod.rs @@ -1,25 +1,26 @@ -use super::{Cmd, COMMANDS}; +use super::COMMANDS; use clap::Command; +use shitbox::Cmd; use std::{error::Error, process}; #[derive(Debug, Default)] -pub struct Shitbox; +pub struct Corebox; -impl Cmd for Shitbox { +impl Cmd for Corebox { fn cli(&self) -> clap::Command { let subcommands: Vec = { let mut s = vec![]; for c in COMMANDS { - if c == "shitbox" { + if c == "corebox" { continue; } - if let Some(cmd) = crate::cmd::get(c) { + if let Some(cmd) = crate::commands::get(c) { s.push(cmd.cli()); } } s }; - Command::new("shitbox") + Command::new("corebox") .about("The box store multitool of embedded Linux") .version(env!("CARGO_PKG_VERSION")) .propagate_version(true) @@ -31,7 +32,7 @@ impl Cmd for Shitbox { fn run(&self, matches: &clap::ArgMatches) -> Result<(), Box> { if let Some((name, matches)) = matches.subcommand() { - if let Some(command) = crate::cmd::get(name) { + if let Some(command) = crate::commands::get(name) { if let Err(e) = command.run(matches) { eprintln!("Error: {name}: {e}"); process::exit(1); @@ -41,7 +42,7 @@ impl Cmd for Shitbox { Ok(()) } - fn path(&self) -> Option { + fn path(&self) -> Option { None } } diff --git a/src/cmd/cp/mod.rs b/corebox/commands/cp/mod.rs similarity index 100% rename from src/cmd/cp/mod.rs rename to corebox/commands/cp/mod.rs diff --git a/src/cmd/cut/mod.rs b/corebox/commands/cut/mod.rs similarity index 99% rename from src/cmd/cut/mod.rs rename to corebox/commands/cut/mod.rs index 47c361e..58c5ee6 100644 --- a/src/cmd/cut/mod.rs +++ b/corebox/commands/cut/mod.rs @@ -100,8 +100,8 @@ impl Cmd for Cut { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/date/mod.rs b/corebox/commands/date/mod.rs similarity index 100% rename from src/cmd/date/mod.rs rename to corebox/commands/date/mod.rs diff --git a/src/cmd/dd/mod.rs b/corebox/commands/dd/mod.rs similarity index 100% rename from src/cmd/dd/mod.rs rename to corebox/commands/dd/mod.rs diff --git a/src/cmd/dirname/mod.rs b/corebox/commands/dirname/mod.rs similarity index 94% rename from src/cmd/dirname/mod.rs rename to corebox/commands/dirname/mod.rs index 2201cbd..48f56d6 100644 --- a/src/cmd/dirname/mod.rs +++ b/corebox/commands/dirname/mod.rs @@ -42,7 +42,7 @@ impl Cmd for Dirname { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/echo/mod.rs b/corebox/commands/echo/mod.rs similarity index 90% rename from src/cmd/echo/mod.rs rename to corebox/commands/echo/mod.rs index 1e0bdeb..28feae1 100644 --- a/src/cmd/echo/mod.rs +++ b/corebox/commands/echo/mod.rs @@ -21,7 +21,7 @@ impl Cmd for Echo { fn run(&self, _matches: &clap::ArgMatches) -> Result<(), Box> { let args: Vec = env::args().collect(); - let idx = match crate::progname() { + let idx = match shitbox::progname() { Some(s) if s.as_str() == "echo" => 1, Some(_) => 2, None => unreachable!(), @@ -42,7 +42,7 @@ impl Cmd for Echo { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::Bin) + fn path(&self) -> Option { + Some(shitbox::Path::Bin) } } diff --git a/src/cmd/expand/mod.rs b/corebox/commands/expand/mod.rs similarity index 95% rename from src/cmd/expand/mod.rs rename to corebox/commands/expand/mod.rs index 03a4ef5..0da3847 100644 --- a/src/cmd/expand/mod.rs +++ b/corebox/commands/expand/mod.rs @@ -42,7 +42,7 @@ impl Cmd for Expand { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/factor/mod.rs b/corebox/commands/factor/mod.rs similarity index 95% rename from src/cmd/factor/mod.rs rename to corebox/commands/factor/mod.rs index afa4086..9b2a6bb 100644 --- a/src/cmd/factor/mod.rs +++ b/corebox/commands/factor/mod.rs @@ -40,8 +40,8 @@ impl Cmd for Factor { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/false/mod.rs b/corebox/commands/false/mod.rs similarity index 86% rename from src/cmd/false/mod.rs rename to corebox/commands/false/mod.rs index b35ef58..a3c7d2b 100644 --- a/src/cmd/false/mod.rs +++ b/corebox/commands/false/mod.rs @@ -17,7 +17,7 @@ impl Cmd for False { process::exit(1); } - fn path(&self) -> Option { - Some(crate::Path::Bin) + fn path(&self) -> Option { + Some(shitbox::Path::Bin) } } diff --git a/src/cmd/fold/mod.rs b/corebox/commands/fold/mod.rs similarity index 97% rename from src/cmd/fold/mod.rs rename to corebox/commands/fold/mod.rs index e613a07..55c25dd 100644 --- a/src/cmd/fold/mod.rs +++ b/corebox/commands/fold/mod.rs @@ -64,8 +64,8 @@ impl Cmd for Fold { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/getty/mod.rs b/corebox/commands/getty/mod.rs similarity index 100% rename from src/cmd/getty/mod.rs rename to corebox/commands/getty/mod.rs diff --git a/src/cmd/groups/mod.rs b/corebox/commands/groups/mod.rs similarity index 93% rename from src/cmd/groups/mod.rs rename to corebox/commands/groups/mod.rs index 34cd807..3bc3f31 100644 --- a/src/cmd/groups/mod.rs +++ b/corebox/commands/groups/mod.rs @@ -1,5 +1,4 @@ use super::Cmd; -use crate::pw; use clap::{Arg, Command}; #[derive(Debug, Default)] @@ -37,7 +36,7 @@ impl Cmd for Groups { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/head/mod.rs b/corebox/commands/head/mod.rs similarity index 97% rename from src/cmd/head/mod.rs rename to corebox/commands/head/mod.rs index 5a9a068..ea2b272 100644 --- a/src/cmd/head/mod.rs +++ b/corebox/commands/head/mod.rs @@ -1,5 +1,5 @@ use super::Cmd; -use crate::{args, Path}; +use shitbox::{args, Path}; use clap::{value_parser, Arg, ArgAction, ArgMatches, Command}; use std::{ env, @@ -59,7 +59,7 @@ impl Cmd for Head { fn run(&self, matches: &ArgMatches) -> Result<(), Box> { let args: Vec<_> = env::args().collect(); - let idx = match crate::progname() { + let idx = match shitbox::progname() { Some(s) if s.as_str() == "head" => 1, _ => 2, }; @@ -109,7 +109,7 @@ impl Cmd for Head { } fn path(&self) -> Option { - Some(crate::Path::Bin) + Some(shitbox::Path::Bin) } } diff --git a/src/cmd/hostid/mod.rs b/corebox/commands/hostid/mod.rs similarity index 88% rename from src/cmd/hostid/mod.rs rename to corebox/commands/hostid/mod.rs index b813c2c..4a29e3b 100644 --- a/src/cmd/hostid/mod.rs +++ b/corebox/commands/hostid/mod.rs @@ -19,7 +19,7 @@ impl Cmd for Hostid { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/hostname/mod.rs b/corebox/commands/hostname/mod.rs similarity index 94% rename from src/cmd/hostname/mod.rs rename to corebox/commands/hostname/mod.rs index 56d982d..796586c 100644 --- a/src/cmd/hostname/mod.rs +++ b/corebox/commands/hostname/mod.rs @@ -1,5 +1,4 @@ use super::Cmd; -use crate::unistd; use clap::{Arg, ArgAction, ArgMatches, Command}; use std::{error::Error, io}; @@ -48,7 +47,7 @@ impl Cmd for Hostname { } } - fn path(&self) -> Option { - Some(crate::Path::Bin) + fn path(&self) -> Option { + Some(shitbox::Path::Bin) } } diff --git a/src/cmd/link/mod.rs b/corebox/commands/link/mod.rs similarity index 92% rename from src/cmd/link/mod.rs rename to corebox/commands/link/mod.rs index 46e66db..6fa2d8e 100644 --- a/src/cmd/link/mod.rs +++ b/corebox/commands/link/mod.rs @@ -1,5 +1,5 @@ use super::Cmd; -use crate::args; +use shitbox::args; use clap::{Arg, Command}; use std::{fs, io}; @@ -39,7 +39,7 @@ impl Cmd for Link { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/ln/mod.rs b/corebox/commands/ln/mod.rs similarity index 97% rename from src/cmd/ln/mod.rs rename to corebox/commands/ln/mod.rs index fabbf61..c4cb558 100644 --- a/src/cmd/ln/mod.rs +++ b/corebox/commands/ln/mod.rs @@ -1,5 +1,4 @@ use super::Cmd; -use crate::unistd; use clap::{Arg, ArgAction, Command}; use std::{error::Error, fs, io, os, path::PathBuf}; @@ -88,8 +87,8 @@ impl Cmd for Ln { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::Bin) + fn path(&self) -> Option { + Some(shitbox::Path::Bin) } } diff --git a/src/cmd/logname/mod.rs b/corebox/commands/logname/mod.rs similarity index 87% rename from src/cmd/logname/mod.rs rename to corebox/commands/logname/mod.rs index 22be191..14be6b8 100644 --- a/src/cmd/logname/mod.rs +++ b/corebox/commands/logname/mod.rs @@ -21,7 +21,7 @@ impl Cmd for Logname { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/ls/mod.rs b/corebox/commands/ls/mod.rs similarity index 100% rename from src/cmd/ls/mod.rs rename to corebox/commands/ls/mod.rs diff --git a/src/cmd/mkfifo/mod.rs b/corebox/commands/mkfifo/mod.rs similarity index 94% rename from src/cmd/mkfifo/mod.rs rename to corebox/commands/mkfifo/mod.rs index d086b04..62dc171 100644 --- a/src/cmd/mkfifo/mod.rs +++ b/corebox/commands/mkfifo/mod.rs @@ -1,6 +1,7 @@ use super::Cmd; -use crate::{args, mode::Parser, stat}; +use shitbox::{args, stat}; use clap::{Arg, ArgMatches, Command}; +use mode::Parser; use std::error::Error; #[derive(Debug, Default)] @@ -55,7 +56,7 @@ impl Cmd for MkFifo { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/mknod/mod.rs b/corebox/commands/mknod/mod.rs similarity index 96% rename from src/cmd/mknod/mod.rs rename to corebox/commands/mknod/mod.rs index 4c38fb6..1e5b6d1 100644 --- a/src/cmd/mknod/mod.rs +++ b/corebox/commands/mknod/mod.rs @@ -1,10 +1,7 @@ use super::Cmd; -use crate::{ - args, - mode::{get_umask, Parser}, - stat, -}; +use shitbox::{args, stat}; use clap::{value_parser, Arg, ArgMatches, Command}; +use mode::{get_umask, Parser}; use std::{convert::Infallible, error::Error, io, str::FromStr}; #[derive(Debug, Default)] @@ -92,8 +89,8 @@ impl Cmd for MkNod { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::Sbin) + fn path(&self) -> Option { + Some(shitbox::Path::Sbin) } } diff --git a/src/cmd/mktemp/mod.rs b/corebox/commands/mktemp/mod.rs similarity index 97% rename from src/cmd/mktemp/mod.rs rename to corebox/commands/mktemp/mod.rs index 56e7cf4..b6fb032 100644 --- a/src/cmd/mktemp/mod.rs +++ b/corebox/commands/mktemp/mod.rs @@ -79,7 +79,7 @@ impl Cmd for MkTemp { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/corebox/commands/mod.rs b/corebox/commands/mod.rs new file mode 100644 index 0000000..42bbc59 --- /dev/null +++ b/corebox/commands/mod.rs @@ -0,0 +1,172 @@ +use clap::ArgMatches; +use shitbox::Cmd; + +mod corebox; +mod base32; +mod base64; +mod basename; +mod bootstrap; +mod cat; +mod chmod; +mod chown; +mod chroot; +mod clear; +mod cp; +mod cut; +mod date; +mod dd; +mod dirname; +mod echo; +mod expand; +mod factor; +mod r#false; +mod fold; +mod getty; +mod groups; +mod head; +mod hostid; +mod hostname; +mod link; +mod ln; +mod logname; +mod ls; +mod mkfifo; +mod mknod; +mod mktemp; +mod mountpoint; +mod mv; +mod nologin; +mod nproc; +mod printenv; +mod pwd; +mod readlink; +mod realpath; +mod rev; +mod rm; +mod rmdir; +mod sleep; +mod sync; +mod r#true; +mod unlink; +mod wc; +mod which; +mod whoami; +mod yes; + +/// Parses a string into a command to run +#[must_use] +#[allow(clippy::box_default)] +pub fn get(name: &str) -> Option> { + match name { + "base64" => Some(Box::new(base64::Base64::default())), + "base32" => Some(Box::new(base32::Base32::default())), + "basename" => Some(Box::new(basename::Basename::default())), + "bootstrap" => Some(Box::new(bootstrap::Bootstrap::default())), + "chmod" => Some(Box::new(chmod::Chmod::default())), + "chgrp" => Some(Box::new(chown::Chgrp::default())), + "chown" => Some(Box::new(chown::Chown::default())), + "chroot" => Some(Box::new(chroot::Chroot::default())), + "clear" => Some(Box::new(clear::Clear::default())), + "corebox" => Some(Box::new(corebox::Corebox::default())), + "cut" => Some(Box::new(cut::Cut::default())), + "dirname" => Some(Box::new(dirname::Dirname::default())), + "echo" => Some(Box::new(echo::Echo::default())), + "factor" => Some(Box::new(factor::Factor::default())), + "false" => Some(Box::new(r#false::False::default())), + "fold" => Some(Box::new(fold::Fold::default())), + "groups" => Some(Box::new(groups::Groups::default())), + "head" => Some(Box::new(head::Head::default())), + "hostid" => Some(Box::new(hostid::Hostid::default())), + "hostname" => Some(Box::new(hostname::Hostname::default())), + "link" => Some(Box::new(link::Link::default())), + "ln" => Some(Box::new(ln::Ln::default())), + "logname" => Some(Box::new(logname::Logname::default())), + "mkfifo" => Some(Box::new(mkfifo::MkFifo::default())), + "mknod" => Some(Box::new(mknod::MkNod::default())), + "mktemp" => Some(Box::new(mktemp::MkTemp::default())), + "mountpoint" => Some(Box::new(mountpoint::Mountpoint::default())), + "nologin" => Some(Box::new(nologin::Nologin::default())), + "nproc" => Some(Box::new(nproc::Nproc::default())), + "printenv" => Some(Box::new(printenv::Printenv::default())), + "pwd" => Some(Box::new(pwd::Pwd::default())), + "readlink" => Some(Box::new(readlink::Readlink::default())), + "realpath" => Some(Box::new(realpath::Realpath::default())), + "rev" => Some(Box::new(rev::Rev::default())), + "rm" => Some(Box::new(rm::Rm::default())), + "rmdir" => Some(Box::new(rmdir::Rmdir::default())), + "sleep" => Some(Box::new(sleep::Sleep::default())), + "sync" => Some(Box::new(sync::Sync::default())), + "true" => Some(Box::new(r#true::True::default())), + "unlink" => Some(Box::new(unlink::Unlink::default())), + "wc" => Some(Box::new(wc::Wc::default())), + "which" => Some(Box::new(which::Which::default())), + "whoami" => Some(Box::new(whoami::Whoami::default())), + "yes" => Some(Box::new(yes::Yes::default())), + _ => None, + } +} + +pub static COMMANDS: [&str; 44] = [ + "base32", + "base64", + "basename", + "bootstrap", + "chgrp", + "chmod", + "chown", + "chroot", + "clear", + "corebox", + "cut", + "dirname", + "echo", + "false", + "factor", + "fold", + "groups", + "head", + "hostid", + "hostname", + "link", + "ln", + "logname", + "mkfifo", + "mknod", + "mktemp", + "mountpoint", + "nologin", + "nproc", + "printenv", + "pwd", + "readlink", + "realpath", + "rev", + "rm", + "rmdir", + "sleep", + "sync", + "true", + "unlink", + "wc", + "which", + "whoami", + "yes", +]; + +#[derive(Clone, Copy, Debug)] +enum Feedback { + Full, + Changes, +} + +impl Feedback { + fn from_matches(matches: &ArgMatches) -> Option { + if matches.get_flag("verbose") { + Some(Feedback::Full) + } else if matches.get_flag("changes") { + Some(Feedback::Changes) + } else { + None + } + } +} diff --git a/src/cmd/mountpoint/mod.rs b/corebox/commands/mountpoint/mod.rs similarity index 98% rename from src/cmd/mountpoint/mod.rs rename to corebox/commands/mountpoint/mod.rs index c585fb8..1c78b5e 100644 --- a/src/cmd/mountpoint/mod.rs +++ b/corebox/commands/mountpoint/mod.rs @@ -82,8 +82,8 @@ impl Cmd for Mountpoint { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::Bin) + fn path(&self) -> Option { + Some(shitbox::Path::Bin) } } diff --git a/src/cmd/mv/mod.rs b/corebox/commands/mv/mod.rs similarity index 100% rename from src/cmd/mv/mod.rs rename to corebox/commands/mv/mod.rs diff --git a/src/cmd/nologin/mod.rs b/corebox/commands/nologin/mod.rs similarity index 85% rename from src/cmd/nologin/mod.rs rename to corebox/commands/nologin/mod.rs index 41e2e8d..921a2c6 100644 --- a/src/cmd/nologin/mod.rs +++ b/corebox/commands/nologin/mod.rs @@ -17,7 +17,7 @@ impl Cmd for Nologin { process::exit(42); } - fn path(&self) -> Option { - Some(crate::Path::Sbin) + fn path(&self) -> Option { + Some(shitbox::Path::Sbin) } } diff --git a/src/cmd/nproc/mod.rs b/corebox/commands/nproc/mod.rs similarity index 91% rename from src/cmd/nproc/mod.rs rename to corebox/commands/nproc/mod.rs index d9b769c..e91280d 100644 --- a/src/cmd/nproc/mod.rs +++ b/corebox/commands/nproc/mod.rs @@ -27,8 +27,8 @@ impl Cmd for Nproc { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/printenv/mod.rs b/corebox/commands/printenv/mod.rs similarity index 94% rename from src/cmd/printenv/mod.rs rename to corebox/commands/printenv/mod.rs index 8fd657b..5cfe795 100644 --- a/src/cmd/printenv/mod.rs +++ b/corebox/commands/printenv/mod.rs @@ -47,7 +47,7 @@ impl Cmd for Printenv { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/pwd/mod.rs b/corebox/commands/pwd/mod.rs similarity index 94% rename from src/cmd/pwd/mod.rs rename to corebox/commands/pwd/mod.rs index 97fb32b..9b09f9b 100644 --- a/src/cmd/pwd/mod.rs +++ b/corebox/commands/pwd/mod.rs @@ -38,7 +38,7 @@ impl Cmd for Pwd { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/readlink/mod.rs b/corebox/commands/readlink/mod.rs similarity index 95% rename from src/cmd/readlink/mod.rs rename to corebox/commands/readlink/mod.rs index 7ac8765..8b80fd7 100644 --- a/src/cmd/readlink/mod.rs +++ b/corebox/commands/readlink/mod.rs @@ -1,4 +1,4 @@ -use crate::Cmd; +use shitbox::Cmd; use clap::{Arg, ArgAction, ArgMatches, Command, ValueHint}; use std::{fs, path::PathBuf}; @@ -53,7 +53,7 @@ impl Cmd for Readlink { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/realpath/mod.rs b/corebox/commands/realpath/mod.rs similarity index 95% rename from src/cmd/realpath/mod.rs rename to corebox/commands/realpath/mod.rs index da919be..7ac681a 100644 --- a/src/cmd/realpath/mod.rs +++ b/corebox/commands/realpath/mod.rs @@ -57,7 +57,7 @@ impl Cmd for Realpath { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::Bin) + fn path(&self) -> Option { + Some(shitbox::Path::Bin) } } diff --git a/src/cmd/rev/mod.rs b/corebox/commands/rev/mod.rs similarity index 95% rename from src/cmd/rev/mod.rs rename to corebox/commands/rev/mod.rs index c767263..348ae76 100644 --- a/src/cmd/rev/mod.rs +++ b/corebox/commands/rev/mod.rs @@ -1,5 +1,5 @@ use super::Cmd; -use crate::args; +use shitbox::args; use clap::Command; use std::{ fs::File, @@ -48,8 +48,8 @@ impl Cmd for Rev { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/rm/mod.rs b/corebox/commands/rm/mod.rs similarity index 98% rename from src/cmd/rm/mod.rs rename to corebox/commands/rm/mod.rs index 5d7eba8..7aec737 100644 --- a/src/cmd/rm/mod.rs +++ b/corebox/commands/rm/mod.rs @@ -1,5 +1,5 @@ use super::Cmd; -use crate::{args, fs::FileType}; +use shitbox::{args, fs::FileType}; use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command, ValueHint}; use std::{ error::Error, @@ -91,8 +91,8 @@ impl Cmd for Rm { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::Bin) + fn path(&self) -> Option { + Some(shitbox::Path::Bin) } } diff --git a/src/cmd/rmdir/mod.rs b/corebox/commands/rmdir/mod.rs similarity index 95% rename from src/cmd/rmdir/mod.rs rename to corebox/commands/rmdir/mod.rs index 1e8964a..ee342ba 100644 --- a/src/cmd/rmdir/mod.rs +++ b/corebox/commands/rmdir/mod.rs @@ -1,5 +1,5 @@ use super::Cmd; -use crate::args; +use shitbox::args; use clap::{Arg, ArgAction, Command, ValueHint}; use std::{error::Error, fs, path::Path}; @@ -44,8 +44,8 @@ impl Cmd for Rmdir { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::Bin) + fn path(&self) -> Option { + Some(shitbox::Path::Bin) } } diff --git a/src/cmd/sleep/mod.rs b/corebox/commands/sleep/mod.rs similarity index 94% rename from src/cmd/sleep/mod.rs rename to corebox/commands/sleep/mod.rs index 196e938..2312ca5 100644 --- a/src/cmd/sleep/mod.rs +++ b/corebox/commands/sleep/mod.rs @@ -37,7 +37,7 @@ impl Cmd for Sleep { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::Bin) + fn path(&self) -> Option { + Some(shitbox::Path::Bin) } } diff --git a/src/cmd/sync/mod.rs b/corebox/commands/sync/mod.rs similarity index 95% rename from src/cmd/sync/mod.rs rename to corebox/commands/sync/mod.rs index 7e6d8d8..9aa20c4 100644 --- a/src/cmd/sync/mod.rs +++ b/corebox/commands/sync/mod.rs @@ -1,5 +1,4 @@ use super::Cmd; -use crate::unistd; use clap::{Arg, ArgAction, Command}; use std::{error::Error, fs::OpenOptions}; @@ -53,7 +52,7 @@ impl Cmd for Sync { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::Bin) + fn path(&self) -> Option { + Some(shitbox::Path::Bin) } } diff --git a/src/cmd/true/mod.rs b/corebox/commands/true/mod.rs similarity index 85% rename from src/cmd/true/mod.rs rename to corebox/commands/true/mod.rs index d80ce2d..200e4ff 100644 --- a/src/cmd/true/mod.rs +++ b/corebox/commands/true/mod.rs @@ -17,7 +17,7 @@ impl Cmd for True { process::exit(0); } - fn path(&self) -> Option { - Some(crate::Path::Bin) + fn path(&self) -> Option { + Some(shitbox::Path::Bin) } } diff --git a/src/cmd/unlink/mod.rs b/corebox/commands/unlink/mod.rs similarity index 90% rename from src/cmd/unlink/mod.rs rename to corebox/commands/unlink/mod.rs index 3eeea2e..298ff27 100644 --- a/src/cmd/unlink/mod.rs +++ b/corebox/commands/unlink/mod.rs @@ -1,5 +1,5 @@ use super::Cmd; -use crate::{args, unistd}; +use shitbox::args; use clap::{Arg, Command}; #[derive(Debug, Default)] @@ -32,7 +32,7 @@ impl Cmd for Unlink { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/wc/mod.rs b/corebox/commands/wc/mod.rs similarity index 98% rename from src/cmd/wc/mod.rs rename to corebox/commands/wc/mod.rs index 6eadb07..df45fba 100644 --- a/src/cmd/wc/mod.rs +++ b/corebox/commands/wc/mod.rs @@ -1,5 +1,5 @@ use super::Cmd; -use crate::bitflags::BitFlags; +use bitflags::BitFlags; use clap::{Arg, ArgAction, ArgMatches, Command}; use std::{ cmp, @@ -79,8 +79,8 @@ impl Cmd for Wc { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/which/mod.rs b/corebox/commands/which/mod.rs similarity index 93% rename from src/cmd/which/mod.rs rename to corebox/commands/which/mod.rs index ed51c46..cb55201 100644 --- a/src/cmd/which/mod.rs +++ b/corebox/commands/which/mod.rs @@ -1,6 +1,7 @@ use super::Cmd; -use crate::{bitflags::BitFlags, mode::Bit}; +use bitflags::BitFlags; use clap::{Arg, Command}; +use mode::Bit; use std::{env, fs::File, os::unix::prelude::MetadataExt, path::PathBuf, process}; #[derive(Debug, Default)] @@ -43,8 +44,8 @@ impl Cmd for Which { } } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } @@ -56,7 +57,7 @@ fn which(command: &str, path: &[&str]) -> Option { if let Ok(meta) = exe.metadata() { let mode = meta.mode(); let myuid = unsafe { libc::geteuid() }; - let mygroups = crate::pw::get_gids(); + let mygroups = pw::get_gids(); // we own the file and it has u+x if myuid == meta.uid() && mode.contains(Bit::UExec) { return Some(format!("{}", file.display())); diff --git a/src/cmd/whoami/mod.rs b/corebox/commands/whoami/mod.rs similarity index 88% rename from src/cmd/whoami/mod.rs rename to corebox/commands/whoami/mod.rs index ee065a7..fec3169 100644 --- a/src/cmd/whoami/mod.rs +++ b/corebox/commands/whoami/mod.rs @@ -25,7 +25,7 @@ impl Cmd for Whoami { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/yes/mod.rs b/corebox/commands/yes/mod.rs similarity index 87% rename from src/cmd/yes/mod.rs rename to corebox/commands/yes/mod.rs index 9175b05..ec62de3 100644 --- a/src/cmd/yes/mod.rs +++ b/corebox/commands/yes/mod.rs @@ -19,7 +19,7 @@ impl Cmd for Yes { } } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/corebox/main.rs b/corebox/main.rs new file mode 100644 index 0000000..3911358 --- /dev/null +++ b/corebox/main.rs @@ -0,0 +1,18 @@ +use std::process; + +mod commands; + +fn main() { + if let Some(progname) = shitbox::progname() { + if let Some(command) = commands::get(&progname) { + let cli = command.cli(); + if let Err(e) = command.run(&cli.get_matches()) { + eprintln!("{progname}: Error: {e}"); + process::exit(1); + } + } else { + eprintln!("shitbox: Error: unknown command {progname}"); + process::exit(1); + } + } +} diff --git a/src/cmd/b2sum/mod.rs b/hashbox/commands/b2sum/mod.rs similarity index 97% rename from src/cmd/b2sum/mod.rs rename to hashbox/commands/b2sum/mod.rs index 87290f4..4231252 100644 --- a/src/cmd/b2sum/mod.rs +++ b/hashbox/commands/b2sum/mod.rs @@ -1,7 +1,7 @@ use super::Cmd; -use crate::args; use blake2b_simd::Params; use clap::{value_parser, Arg, Command}; +use shitbox::args; use std::{ fs::File, io::{self, BufRead, BufReader, Read}, @@ -98,7 +98,7 @@ impl Cmd for B2sum { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/hashbox/commands/hashbox/mod.rs b/hashbox/commands/hashbox/mod.rs new file mode 100644 index 0000000..7f07682 --- /dev/null +++ b/hashbox/commands/hashbox/mod.rs @@ -0,0 +1,48 @@ +use super::COMMANDS; +use clap::Command; +use shitbox::Cmd; +use std::{error::Error, process}; + +#[derive(Debug, Default)] +pub struct Hashbox; + +impl Cmd for Hashbox { + fn cli(&self) -> clap::Command { + let subcommands: Vec = { + let mut s = vec![]; + for c in COMMANDS { + if c == "hashbox" { + continue; + } + if let Some(cmd) = crate::commands::get(c) { + s.push(cmd.cli()); + } + } + s + }; + Command::new("hashbox") + .about("The box store multitool of embedded Linux") + .version(env!("CARGO_PKG_VERSION")) + .propagate_version(true) + .arg_required_else_help(true) + .subcommand_value_name("APPLET") + .subcommand_help_heading("APPLETS") + .subcommands(&subcommands) + } + + fn run(&self, matches: &clap::ArgMatches) -> Result<(), Box> { + if let Some((name, matches)) = matches.subcommand() { + if let Some(command) = crate::commands::get(name) { + if let Err(e) = command.run(matches) { + eprintln!("Error: {name}: {e}"); + process::exit(1); + } + } + } + Ok(()) + } + + fn path(&self) -> Option { + None + } +} diff --git a/src/cmd/md5sum/mod.rs b/hashbox/commands/md5sum/mod.rs similarity index 89% rename from src/cmd/md5sum/mod.rs rename to hashbox/commands/md5sum/mod.rs index 8226ca8..2953ca3 100644 --- a/src/cmd/md5sum/mod.rs +++ b/hashbox/commands/md5sum/mod.rs @@ -1,10 +1,8 @@ -use super::Cmd; -use crate::{ - args, - hash::{self, HashType}, -}; +use crate::hash::{self, HashType}; use clap::Command; use md5::{Digest, Md5}; +use shitbox::args; +use shitbox::Cmd; use std::{io, process}; #[derive(Debug, Default)] @@ -44,7 +42,7 @@ impl Cmd for Md5sum { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/hashbox/commands/mod.rs b/hashbox/commands/mod.rs new file mode 100644 index 0000000..056e88e --- /dev/null +++ b/hashbox/commands/mod.rs @@ -0,0 +1,37 @@ +use shitbox::Cmd; + +mod b2sum; +mod hashbox; +mod md5sum; +mod sha1sum; +mod sha224sum; +mod sha256sum; +mod sha384sum; +mod sha512sum; + +/// Parses a string into a command to run +#[must_use] +#[allow(clippy::box_default)] +pub fn get(name: &str) -> Option> { + match name { + "b2sum" => Some(Box::new(b2sum::B2sum::default())), + "hashbox" => Some(Box::new(hashbox::Hashbox::default())), + "md5sum" => Some(Box::new(md5sum::Md5sum::default())), + "sha1sum" => Some(Box::new(sha1sum::Sha1sum::default())), + "sha224sum" => Some(Box::new(sha224sum::Sha224sum::default())), + "sha256sum" => Some(Box::new(sha256sum::Sha256sum::default())), + "sha384sum" => Some(Box::new(sha384sum::Sha384sum::default())), + "sha512sum" => Some(Box::new(sha512sum::Sha512sum::default())), + _ => None, + } +} + +pub static COMMANDS: [&str; 7] = [ + "b2sum", + "md5sum", + "sha1sum", + "sha224sum", + "sha256sum", + "sha384sum", + "sha512sum", +]; diff --git a/src/cmd/sha1sum/mod.rs b/hashbox/commands/sha1sum/mod.rs similarity index 90% rename from src/cmd/sha1sum/mod.rs rename to hashbox/commands/sha1sum/mod.rs index ce553bd..35e4450 100644 --- a/src/cmd/sha1sum/mod.rs +++ b/hashbox/commands/sha1sum/mod.rs @@ -1,10 +1,7 @@ -use super::Cmd; -use crate::{ - args, - hash::{self, HashType}, -}; +use crate::hash::{self, HashType}; use clap::Command; use sha1::{Digest, Sha1}; +use shitbox::{args, Cmd}; use std::{io, process}; #[derive(Debug, Default)] @@ -44,7 +41,7 @@ impl Cmd for Sha1sum { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/sha224sum/mod.rs b/hashbox/commands/sha224sum/mod.rs similarity index 91% rename from src/cmd/sha224sum/mod.rs rename to hashbox/commands/sha224sum/mod.rs index 5da6985..626bff5 100644 --- a/src/cmd/sha224sum/mod.rs +++ b/hashbox/commands/sha224sum/mod.rs @@ -1,10 +1,8 @@ use super::Cmd; -use crate::{ - args, - hash::{self, HashType}, -}; +use crate::hash::{self, HashType}; use clap::Command; use sha2::{Digest, Sha224}; +use shitbox::args; use std::{io, process}; #[derive(Debug, Default)] @@ -44,7 +42,7 @@ impl Cmd for Sha224sum { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/sha256sum/mod.rs b/hashbox/commands/sha256sum/mod.rs similarity index 91% rename from src/cmd/sha256sum/mod.rs rename to hashbox/commands/sha256sum/mod.rs index 8233482..3651ca5 100644 --- a/src/cmd/sha256sum/mod.rs +++ b/hashbox/commands/sha256sum/mod.rs @@ -1,10 +1,8 @@ use super::Cmd; -use crate::{ - args, - hash::{self, HashType}, -}; +use crate::hash::{self, HashType}; use clap::Command; use sha2::{Digest, Sha256}; +use shitbox::args; use std::{io, process}; #[derive(Debug, Default)] @@ -44,7 +42,7 @@ impl Cmd for Sha256sum { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/sha384sum/mod.rs b/hashbox/commands/sha384sum/mod.rs similarity index 91% rename from src/cmd/sha384sum/mod.rs rename to hashbox/commands/sha384sum/mod.rs index 94b4b34..7f758e8 100644 --- a/src/cmd/sha384sum/mod.rs +++ b/hashbox/commands/sha384sum/mod.rs @@ -1,10 +1,8 @@ use super::Cmd; -use crate::{ - args, - hash::{self, HashType}, -}; +use crate::hash::{self, HashType}; use clap::Command; use sha2::{Digest, Sha384}; +use shitbox::args; use std::{io, process}; #[derive(Debug, Default)] @@ -44,7 +42,7 @@ impl Cmd for Sha384sum { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/cmd/sha512sum/mod.rs b/hashbox/commands/sha512sum/mod.rs similarity index 91% rename from src/cmd/sha512sum/mod.rs rename to hashbox/commands/sha512sum/mod.rs index a600360..534e76c 100644 --- a/src/cmd/sha512sum/mod.rs +++ b/hashbox/commands/sha512sum/mod.rs @@ -1,10 +1,8 @@ use super::Cmd; -use crate::{ - args, - hash::{self, HashType}, -}; +use crate::hash::{self, HashType}; use clap::Command; use sha2::{Digest, Sha512}; +use shitbox::args; use std::{io, process}; #[derive(Debug, Default)] @@ -44,7 +42,7 @@ impl Cmd for Sha512sum { Ok(()) } - fn path(&self) -> Option { - Some(crate::Path::UsrBin) + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) } } diff --git a/src/hash/mod.rs b/hashbox/hash/mod.rs similarity index 100% rename from src/hash/mod.rs rename to hashbox/hash/mod.rs diff --git a/hashbox/main.rs b/hashbox/main.rs new file mode 100644 index 0000000..17536cc --- /dev/null +++ b/hashbox/main.rs @@ -0,0 +1,19 @@ +use std::process; + +pub mod commands; +pub mod hash; + +fn main() { + if let Some(progname) = shitbox::progname() { + if let Some(command) = commands::get(&progname) { + let cli = command.cli(); + if let Err(e) = command.run(&cli.get_matches()) { + eprintln!("{progname}: Error: {e}"); + process::exit(1); + } + } else { + eprintln!("shitbox: Error: unknown command {progname}"); + process::exit(1); + } + } +} diff --git a/mount/Cargo.toml b/mount/Cargo.toml new file mode 100644 index 0000000..f457bd2 --- /dev/null +++ b/mount/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "mount" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +blkid = "1.0" +sc = { workspace = true } diff --git a/mount/src/lib.rs b/mount/src/lib.rs new file mode 100644 index 0000000..04480f6 --- /dev/null +++ b/mount/src/lib.rs @@ -0,0 +1,56 @@ +use sc::*; +use std::{io, ffi::CString}; + +mod mntent; +mod mntentries; +mod mntflags; + +pub use self::{ + mntent::MntEntry, + mntentries::MntEntries, + mntflags::{Flags as MntFlags, ParseFlagsError}, +}; + +/// Wraps the Linux SYS_mount syscall in a nicer interface +pub fn mount( + dev: &str, + mountpoint: &str, + fstype: &str, + flags: u32, + opts: Option<&str>, +) -> io::Result<()> { + let ret = unsafe { + if let Some(opts) = opts { + syscall!( + MOUNT, + CString::new(dev)?.as_ptr(), + CString::new(mountpoint)?.as_ptr(), + CString::new(fstype)?.as_ptr(), + flags as usize, + CString::new(opts)?.as_ptr() + ) + } else { + syscall!( + MOUNT, + CString::new(dev)?.as_ptr(), + CString::new(mountpoint)?.as_ptr(), + CString::new(fstype)?.as_ptr(), + flags as usize + ) + } + }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } +} + +pub fn swapon(dev: &str, flags: usize) -> io::Result<()> { + let ret = unsafe { syscall!(SWAPON, CString::new(dev)?.as_ptr(), flags) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } +} diff --git a/mount/src/mntent.rs b/mount/src/mntent.rs new file mode 100644 index 0000000..f30e059 --- /dev/null +++ b/mount/src/mntent.rs @@ -0,0 +1,279 @@ +//! A Rust reimplementation of libc's mntent, for parsing /etc/fstab or +//! /proc/mounts +use super::{MntEntries, MntFlags}; +use blkid::{ + cache::Cache, + tag::{PartitionTag, SuperblockTag, TagType}, +}; +use std::{ + fmt::{self, Write}, + fs::File, + io, + os::unix::fs::MetadataExt, + path::PathBuf, + str::FromStr, +}; + +/// The information for a mount broken out into a struct +pub struct MntEntry { + pub fsname: String, + pub dir: String, + pub fstype: String, + pub opts: String, + pub freq: i32, + pub passno: i32, +} + +impl MntEntry { + /// Mount the mount specified by this entry + pub fn mount(&self) -> io::Result<()> { + let dev = self.device()?; + let dev = dev.to_string_lossy(); + let (flags, opts) = self.parse_opts() + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + super::mount(&dev, &self.dir, &self.fstype, flags, Some(&opts))?; + Ok(()) + } + + pub fn is_mounted(&self) -> bool { + if let Ok(entries) = MntEntries::new("/proc/mounts") { + for e in entries { + if self.dir == e.dir && self.fstype == e.fstype { + return true; + } + } + } + false + } + + pub fn parse_opts(&self) -> Result<(u32, String), fmt::Error> { + let mut res = (0, String::new()); + let splitopts = self.opts.split(','); + for opt in splitopts { + match opt.parse::() { + Ok(f) => res.0 |= f, + Err(_) => match opt { + "async" => res.0 &= !MntFlags::Synchronous, + "atime" => res.0 &= !MntFlags::NoAtime, + "dev" => res.0 &= !MntFlags::NoDev, + "diratime" => res.0 &= !MntFlags::NoDirAtime, + "exec" => res.0 &= !MntFlags::NoExec, + "group" => res.0 &= MntFlags::NoSuid | MntFlags::NoDev, + "noiversion" => res.0 &= !MntFlags::IVersion, + "mand" => res.0 &= !MntFlags::NoRemoteLock, + "norelatime" => res.0 &= !MntFlags::RelAtime, + "nostrictatime" => res.0 &= !MntFlags::StrictAtime, + "nolazytime" => res.0 &= !MntFlags::LazyTime, + "loud" => res.0 &= !MntFlags::Silent, + "owner" => res.0 |= MntFlags::NoSuid | MntFlags::NoDev, + "rw" => res.0 &= !MntFlags::ReadOnly, + "user" => { + res.0 &= !MntFlags::NoUser; + res.0 |= MntFlags::NoExec | MntFlags::NoSuid | MntFlags::NoDev; + } + _ => { + if res.1.is_empty() { + write!(res.1, "{opt}")?; + } else { + write!(res.1, ",{opt}")?; + } + } + }, + } + } + Ok(res) + } + + /// Get the canonical path to the device pointed to by this entry + pub fn device(&self) -> io::Result { + match self.fsname.as_str() { + s if s.starts_with("UUID=") => dev_from_uuid(s), + s if s.starts_with("LABEL=") => dev_from_label(s), + s if s.starts_with("PARTUUID=") => dev_from_partuuid(s), + s if s.starts_with("PARTLABEL=") => dev_from_partlabel(s), + s => { + let mut p = PathBuf::from(s); + if p.exists() { + p = p.canonicalize()?; + let fd = File::open(&p)?; + let meta = fd.metadata()?; + if meta.rdev() != 0 { + Ok(p) + } else { + Err(io::Error::new(io::ErrorKind::Other, "not a block device")) + } + } else { + Err(io::Error::new(io::ErrorKind::NotFound, "no such file")) + } + } + } + } +} + +impl FromStr for MntEntry { + type Err = io::Error; + fn from_str(s: &str) -> Result { + let s = match s.split_once('#') { + Some((lhs, _)) => lhs, + None => s, + }; + let mut split = s.split_whitespace(); + let Some(fsname) = split.next() else { + return Err(io::Error::new(io::ErrorKind::Other, "missing field")); + }; + let Some(dir) = split.next() else { + return Err(io::Error::new(io::ErrorKind::Other, "missing field")); + }; + let Some(fstype) = split.next() else { + return Err(io::Error::new(io::ErrorKind::Other, "missing field")); + }; + let Some(opts) = split.next() else { + return Err(io::Error::new(io::ErrorKind::Other, "missing field")); + }; + let Some(freq) = split.next() else { + return Err(io::Error::new(io::ErrorKind::Other, "missing field")); + }; + let Some(passno) = split.next() else { + return Err(io::Error::new(io::ErrorKind::Other, "missing field")); + }; + if split.next().is_some() { + return Err(io::Error::new(io::ErrorKind::Other, "extra field")); + } + Ok(Self { + fsname: fsname.to_string(), + dir: dir.to_string(), + fstype: fstype.to_string(), + opts: opts.to_string(), + freq: freq + .parse() + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?, + passno: passno + .parse() + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?, + }) + } +} + +fn dev_from_uuid(s: &str) -> io::Result { + let uuid = s.strip_prefix("UUID=").ok_or(io::Error::new( + io::ErrorKind::Other, + "this should never happen", + ))?; + let cache = Cache::new().map_err(|x| io::Error::new(io::ErrorKind::Other, x))?; + let devs = cache.devs(); + for dev in devs { + let tags = dev.tags(); + for tag in tags { + match tag.typ() { + TagType::Superblock(SuperblockTag::Uuid) => { + if uuid.to_lowercase().as_str() == tag.value() { + let devname = dev.name(); + let devfile = devname.canonicalize()?; + let fd = File::open(&devfile)?; + let meta = fd.metadata()?; + if meta.rdev() != 0 { + return Ok(devfile); + } else { + return Err(io::Error::new(io::ErrorKind::Other, "not a block device")); + } + } + } + _ => {} + } + } + } + Err(io::Error::new(io::ErrorKind::Other, "device not found")) +} + +fn dev_from_label(s: &str) -> io::Result { + let label = s.strip_prefix("LABEL=").ok_or(io::Error::new( + io::ErrorKind::Other, + "this should never happen", + ))?; + let cache = Cache::new().map_err(|x| io::Error::new(io::ErrorKind::Other, x))?; + let devs = cache.devs(); + for dev in devs { + let tags = dev.tags(); + for tag in tags { + match tag.typ() { + TagType::Superblock(SuperblockTag::Label) => { + if label == tag.value() { + let devname = dev.name(); + let devfile = devname.canonicalize()?; + let fd = File::open(&devfile)?; + let meta = fd.metadata()?; + if meta.rdev() != 0 { + return Ok(devfile); + } else { + return Err(io::Error::new(io::ErrorKind::Other, "not a block device")); + } + } + } + _ => {} + } + } + } + Err(io::Error::new(io::ErrorKind::Other, "device not found")) +} + +fn dev_from_partuuid(s: &str) -> io::Result { + let partlabel = s.strip_prefix("PARTUUID=").ok_or(io::Error::new( + io::ErrorKind::Other, + "this should never happen", + ))?; + let cache = Cache::new().map_err(|x| io::Error::new(io::ErrorKind::Other, x))?; + let devs = cache.devs(); + for dev in devs { + let tags = dev.tags(); + for tag in tags { + match tag.typ() { + TagType::Partition(PartitionTag::PartEntryUuid) => { + if partlabel == tag.value() { + let devname = dev.name(); + let devfile = devname.canonicalize()?; + let fd = File::open(&devfile)?; + let meta = fd.metadata()?; + if meta.rdev() != 0 { + return Ok(devfile); + } else { + return Err(io::Error::new(io::ErrorKind::Other, "not a block device")); + } + } + } + _ => {} + } + } + } + Err(io::Error::new(io::ErrorKind::Other, "device not found")) +} + +fn dev_from_partlabel(s: &str) -> io::Result { + let partlabel = s.strip_prefix("PARTLABEL=").ok_or(io::Error::new( + io::ErrorKind::Other, + "this should never happen", + ))?; + let cache = Cache::new().map_err(|x| io::Error::new(io::ErrorKind::Other, x))?; + let devs = cache.devs(); + for dev in devs { + let tags = dev.tags(); + for tag in tags { + match tag.typ() { + TagType::Partition(PartitionTag::PartEntryName) => { + if partlabel == tag.value() { + let devname = dev.name(); + let devfile = devname.canonicalize()?; + let fd = File::open(&devfile)?; + let meta = fd.metadata()?; + if meta.rdev() != 0 { + return Ok(devfile); + } else { + return Err(io::Error::new(io::ErrorKind::Other, "not a block device")); + } + } + } + _ => {} + } + } + } + Err(io::Error::new(io::ErrorKind::Other, "device not found")) +} diff --git a/mount/src/mntentries.rs b/mount/src/mntentries.rs new file mode 100644 index 0000000..2c9268b --- /dev/null +++ b/mount/src/mntentries.rs @@ -0,0 +1,41 @@ +//! An `Iterator` of the entries in /etc/fstab of /proc/mounts +use super::MntEntry; +use std::{ + fs::File, + io::{self, BufRead, BufReader, Lines}, + path::Path, +}; + +/// An `Iterator` of the entries in /etc/fstab of /proc/mounts +pub struct MntEntries { + lines: Lines>, +} + +impl MntEntries { + /// Create a new `Iterator` over the mounts specified in the file `p` + pub fn new>(p: P) -> io::Result { + let fd = File::open(p)?; + let reader = BufReader::new(fd); + Ok(Self { + lines: reader.lines(), + }) + } +} + +impl Iterator for MntEntries { + type Item = MntEntry; + + fn next(&mut self) -> Option { + loop { + if let Some(line) = self.lines.next() { + if let Ok(line) = line { + if let Ok(entry) = line.parse() { + break Some(entry); + } + } + } else { + break None; + } + } + } +} diff --git a/mount/src/mntflags.rs b/mount/src/mntflags.rs new file mode 100644 index 0000000..23d41a8 --- /dev/null +++ b/mount/src/mntflags.rs @@ -0,0 +1,148 @@ +use std::{ + error::Error, + fmt::Display, + ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not}, + str::FromStr, +}; + +pub enum Flags { + ReadOnly = 1, + NoSuid = 2, + NoDev = 4, + NoExec = 8, + Synchronous = 16, + Remount = 32, + MandLock = 64, + DirSync = 128, + NoSymFollow = 256, + NoAtime = 1024, + NoDirAtime = 2048, + Bind = 4096, + Move = 8192, + Rec = 16384, + Silent = 32768, + PosixAcl = (1 << 16), + UnBindable = (1 << 17), + Private = (1 << 18), + Slave = (1 << 19), + Shared = (1 << 20), + RelAtime = (1 << 21), + KerrnMount = (1 << 22), + IVersion = (1 << 23), + StrictAtime = (1 << 24), + LazyTime = (1 << 25), + NoRemoteLock = (1 << 27), + NoSec = (1 << 28), + Born = (1 << 29), + Active = (1 << 30), + NoUser = (1 << 31), +} + +impl FromStr for Flags { + type Err = ParseFlagsError; + + fn from_str(s: &str) -> Result { + match s { + "ro" => Ok(Self::ReadOnly), + "nosuid" => Ok(Self::NoSuid), + "nodev" => Ok(Self::NoDev), + "noexec" => Ok(Self::NoExec), + "sync" => Ok(Self::Synchronous), + "remount" => Ok(Self::Remount), + "mand" => Ok(Self::MandLock), + "dirsync" => Ok(Self::DirSync), + "nosymfollow" => Ok(Self::NoSymFollow), + "noatime" => Ok(Self::NoAtime), + "nodiratime" => Ok(Self::NoDirAtime), + "bind" => Ok(Self::Bind), + "silent" => Ok(Self::Silent), + "acl" => Ok(Self::PosixAcl), + "relatime" => Ok(Self::RelAtime), + "iversion" => Ok(Self::IVersion), + "strictatime" => Ok(Self::StrictAtime), + "lazytime" => Ok(Self::LazyTime), + "nomand" => Ok(Self::NoRemoteLock), + "nouser" => Ok(Self::NoUser), + _ => Err(ParseFlagsError), + } + } +} + +#[derive(Debug)] +pub struct ParseFlagsError; + +impl Display for ParseFlagsError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:?}") + } +} + +impl Error for ParseFlagsError {} + +impl BitAnd for Flags { + type Output = u32; + + fn bitand(self, rhs: u32) -> Self::Output { + self as u32 & rhs + } +} + +impl BitAnd for u32 { + type Output = u32; + + fn bitand(self, rhs: Flags) -> Self::Output { + self & rhs as u32 + } +} + +impl BitAnd for Flags { + type Output = u32; + + fn bitand(self, rhs: Self) -> Self::Output { + self as u32 & rhs as u32 + } +} + +impl BitAndAssign for u32 { + fn bitand_assign(&mut self, rhs: Flags) { + *self = *self & rhs; + } +} + +impl BitOr for Flags { + type Output = u32; + + fn bitor(self, rhs: u32) -> Self::Output { + self as u32 | rhs + } +} + +impl BitOr for u32 { + type Output = u32; + + fn bitor(self, rhs: Flags) -> Self::Output { + self | rhs as u32 + } +} + +impl BitOr for Flags { + type Output = u32; + + fn bitor(self, rhs: Self) -> Self::Output { + self as u32 | rhs as u32 + } +} + +impl BitOrAssign for u32 { + fn bitor_assign(&mut self, rhs: Flags) { + *self = *self | rhs; + } +} + +impl Not for Flags { + type Output = u32; + + fn not(self) -> Self::Output { + !(self as u32) + } +} diff --git a/pw/Cargo.toml b/pw/Cargo.toml new file mode 100644 index 0000000..a0d3fca --- /dev/null +++ b/pw/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "pw" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = { workspace = true } diff --git a/src/pw/mod.rs b/pw/src/lib.rs similarity index 100% rename from src/pw/mod.rs rename to pw/src/lib.rs diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index e239b6a..fabc97b 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -1,64 +1,6 @@ use clap::ArgMatches; use std::{error::Error, fmt}; -mod b2sum; -pub mod base32; -mod base64; -mod basename; -mod bootstrap; -mod cat; -mod chmod; -mod chown; -mod chroot; -mod clear; -mod cp; -mod cut; -mod date; -mod dd; -mod dirname; -mod echo; -mod expand; -mod factor; -mod r#false; -mod fold; -mod getty; -mod groups; -mod head; -mod hostid; -mod hostname; -mod link; -mod ln; -mod logname; -mod ls; -mod md5sum; -mod mkfifo; -mod mknod; -mod mktemp; -mod mountpoint; -mod mv; -mod nologin; -mod nproc; -mod printenv; -mod pwd; -mod readlink; -mod realpath; -mod rev; -mod rm; -mod rmdir; -mod sha1sum; -mod sha224sum; -mod sha256sum; -mod sha384sum; -mod sha512sum; -mod shitbox; -mod sleep; -mod sync; -mod r#true; -mod unlink; -mod wc; -mod which; -mod whoami; -mod yes; /// Defines a command or applet, it's cli interface, and it's installation directory /// relative to the binary @@ -73,135 +15,3 @@ pub trait Cmd: fmt::Debug + Sync { /// will be installed fn path(&self) -> Option; } - -/// Parses a string into a command to run -#[must_use] -#[allow(clippy::box_default)] -pub fn get(name: &str) -> Option> { - match name { - "b2sum" => Some(Box::new(b2sum::B2sum::default())), - "base64" => Some(Box::new(base64::Base64::default())), - "base32" => Some(Box::new(base32::Base32::default())), - "basename" => Some(Box::new(basename::Basename::default())), - "bootstrap" => Some(Box::new(bootstrap::Bootstrap::default())), - "chmod" => Some(Box::new(chmod::Chmod::default())), - "chgrp" => Some(Box::new(chown::Chgrp::default())), - "chown" => Some(Box::new(chown::Chown::default())), - "chroot" => Some(Box::new(chroot::Chroot::default())), - "clear" => Some(Box::new(clear::Clear::default())), - "cut" => Some(Box::new(cut::Cut::default())), - "dirname" => Some(Box::new(dirname::Dirname::default())), - "echo" => Some(Box::new(echo::Echo::default())), - "factor" => Some(Box::new(factor::Factor::default())), - "false" => Some(Box::new(r#false::False::default())), - "fold" => Some(Box::new(fold::Fold::default())), - "groups" => Some(Box::new(groups::Groups::default())), - "head" => Some(Box::new(head::Head::default())), - "hostid" => Some(Box::new(hostid::Hostid::default())), - "hostname" => Some(Box::new(hostname::Hostname::default())), - "link" => Some(Box::new(link::Link::default())), - "ln" => Some(Box::new(ln::Ln::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())), - "mountpoint" => Some(Box::new(mountpoint::Mountpoint::default())), - "nologin" => Some(Box::new(nologin::Nologin::default())), - "nproc" => Some(Box::new(nproc::Nproc::default())), - "printenv" => Some(Box::new(printenv::Printenv::default())), - "pwd" => Some(Box::new(pwd::Pwd::default())), - "readlink" => Some(Box::new(readlink::Readlink::default())), - "realpath" => Some(Box::new(realpath::Realpath::default())), - "rev" => Some(Box::new(rev::Rev::default())), - "rm" => Some(Box::new(rm::Rm::default())), - "rmdir" => Some(Box::new(rmdir::Rmdir::default())), - "sha1sum" => Some(Box::new(sha1sum::Sha1sum::default())), - "sha224sum" => Some(Box::new(sha224sum::Sha224sum::default())), - "sha256sum" => Some(Box::new(sha256sum::Sha256sum::default())), - "sha384sum" => Some(Box::new(sha384sum::Sha384sum::default())), - "sha512sum" => Some(Box::new(sha512sum::Sha512sum::default())), - "shitbox" => Some(Box::new(shitbox::Shitbox::default())), - "sleep" => Some(Box::new(sleep::Sleep::default())), - "sync" => Some(Box::new(sync::Sync::default())), - "true" => Some(Box::new(r#true::True::default())), - "unlink" => Some(Box::new(unlink::Unlink::default())), - "wc" => Some(Box::new(wc::Wc::default())), - "which" => Some(Box::new(which::Which::default())), - "whoami" => Some(Box::new(whoami::Whoami::default())), - "yes" => Some(Box::new(yes::Yes::default())), - _ => None, - } -} - -pub static COMMANDS: [&str; 51] = [ - "b2sum", - "base32", - "base64", - "basename", - "bootstrap", - "chgrp", - "chmod", - "chown", - "chroot", - "clear", - "cut", - "dirname", - "echo", - "false", - "factor", - "fold", - "groups", - "head", - "hostid", - "hostname", - "link", - "ln", - "logname", - "md5sum", - "mkfifo", - "mknod", - "mktemp", - "mountpoint", - "nologin", - "nproc", - "printenv", - "pwd", - "readlink", - "realpath", - "rev", - "rm", - "rmdir", - "sha1sum", - "sha224sum", - "sha256sum", - "sha384sum", - "sha512sum", - "shitbox", - "sleep", - "sync", - "true", - "unlink", - "wc", - "which", - "whoami", - "yes", -]; - -#[derive(Clone, Copy, Debug)] -enum Feedback { - Full, - Changes, -} - -impl Feedback { - fn from_matches(matches: &ArgMatches) -> Option { - if matches.get_flag("verbose") { - Some(Feedback::Full) - } else if matches.get_flag("changes") { - Some(Feedback::Changes) - } else { - None - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 926bf0e..01b6360 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,11 @@ #![warn(clippy::all, clippy::pedantic)] -use std::{env, path::PathBuf, process, string::ToString}; +use std::{env, path::PathBuf, string::ToString}; pub mod args; -pub mod bitflags; mod cmd; pub mod fs; -pub mod hash; pub mod math; -pub mod mode; -pub mod pw; pub mod stat; -pub mod unistd; pub use cmd::Cmd; @@ -62,19 +57,3 @@ pub fn progpath() -> Option { _ => None, } } - -/// The entry point of the program -pub fn run() { - if let Some(progname) = progname() { - if let Some(command) = cmd::get(&progname) { - let cli = command.cli(); - if let Err(e) = command.run(&cli.get_matches()) { - eprintln!("{progname}: Error: {e}"); - process::exit(1); - } - } else { - eprintln!("shitbox: Error: unknown command {progname}"); - process::exit(1); - } - } -} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 8b68cd6..0000000 --- a/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - shitbox::run(); -} diff --git a/src/mode/bit.rs b/src/mode/bit.rs deleted file mode 100644 index 62399f3..0000000 --- a/src/mode/bit.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::bitflags::BitFlags; -use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign}; - -/// Unix permission bit flags -#[derive(Clone, Copy, PartialEq)] -pub enum Bit { - Suid = 0o4000, - Sgid = 0o2000, - Sticky = 0o1000, - URead = 0o400, - UWrite = 0o200, - UExec = 0o100, - GRead = 0o40, - GWrite = 0o20, - GExec = 0o10, - ORead = 0o4, - OWrite = 0o2, - OExec = 0o1, -} - -impl BitAnd for Bit { - type Output = u32; - - fn bitand(self, rhs: u32) -> Self::Output { - self as u32 & rhs - } -} - -impl BitAnd for u32 { - type Output = u32; - - fn bitand(self, rhs: Bit) -> Self::Output { - self & rhs as u32 - } -} - -impl BitAnd for Bit { - type Output = u32; - - fn bitand(self, rhs: Self) -> Self::Output { - self as u32 & rhs as u32 - } -} - -impl BitAndAssign for u32 { - fn bitand_assign(&mut self, rhs: Bit) { - *self = *self & rhs; - } -} - -impl BitOr for Bit { - type Output = u32; - - fn bitor(self, rhs: u32) -> Self::Output { - self as u32 | rhs - } -} - -impl BitOr for u32 { - type Output = u32; - - fn bitor(self, rhs: Bit) -> Self::Output { - self | rhs as u32 - } -} - -impl BitOr for Bit { - type Output = u32; - - fn bitor(self, rhs: Self) -> Self::Output { - self as u32 | rhs as u32 - } -} - -impl BitOrAssign for u32 { - fn bitor_assign(&mut self, rhs: Bit) { - *self = *self | rhs; - } -} - -impl Bit { - pub fn as_char(&self, mode: u32) -> char { - if mode & *self != 0 { - match self { - Self::Suid | Self::Sgid => 's', - Self::Sticky => 't', - Self::URead | Self::GRead | Self::ORead => 'r', - Self::UWrite | Self::GWrite | Self::OWrite => 'w', - Self::UExec if mode.contains(Self::Suid) => 's', - Self::GExec if mode.contains(Self::Sgid) => 's', - Self::OExec if mode.contains(Self::Sticky) => 't', - Self::UExec | Self::GExec | Self::OExec => 'x', - } - } else { - '-' - } - } -} diff --git a/src/mode/mod.rs b/src/mode/mod.rs deleted file mode 100644 index c5b6697..0000000 --- a/src/mode/mod.rs +++ /dev/null @@ -1,141 +0,0 @@ -//! Functions for parsing and managing permissions -mod bit; -mod parser; -mod who; -use std::fmt::{self, Write}; - -pub use { - bit::Bit, - parser::{ParseError, Parser}, - who::Who, -}; - -/// Gets the umask for the current user -#[must_use] -pub fn get_umask() -> u32 { - let mask = unsafe { libc::umask(0) }; - let _mask = unsafe { libc::umask(mask) }; - mask -} - -/// Functions for extracting information about Unix modes -pub trait Mode { - /// Returns a string representing permissions in symbolic format - /// including file type - fn mode_string_full(&self) -> Result; - - /// Returns a string representing permissions in symbolic format - /// minus file type - fn mode_string(&self) -> Result; -} - -impl Mode for u32 { - fn mode_string_full(&self) -> Result { - let b = if self & 0o40000 != 0 && self & 0o20000 != 0 { - 'b' - } else if self & 0o40000 != 0 { - 'd' - } else if self & 0o20000 != 0 { - 'c' - } else { - '-' - }; - let mut s = String::new(); - write!(s, "{b}")?; - [ - Bit::URead, - Bit::UWrite, - Bit::UExec, - Bit::GRead, - Bit::GWrite, - Bit::GExec, - Bit::ORead, - Bit::OWrite, - Bit::OExec, - ] - .iter() - .try_for_each(|b| write!(s, "{}", b.as_char(*self)))?; - Ok(s) - } - - fn mode_string(&self) -> Result { - let mut s = String::new(); - [ - Bit::URead, - Bit::UWrite, - Bit::UExec, - Bit::GRead, - Bit::GWrite, - Bit::GExec, - Bit::ORead, - Bit::OWrite, - Bit::OExec, - ] - .iter() - .try_for_each(|b| write!(s, "{}", b.as_char(*self)))?; - Ok(s) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn getumask() { - let mask = unsafe { libc::umask(0o22) }; - assert_eq!(get_umask(), 0o022); - unsafe { - libc::umask(mask); - } - } - - #[test] - fn display_bits_dir() { - let m: u32 = 0o40755; - let s = m.mode_string_full().unwrap(); - assert_eq!(s.as_str(), "drwxr-xr-x") - } - - #[test] - fn display_bits_char() { - let m: u32 = 0o20666; - let s = m.mode_string_full().unwrap(); - assert_eq!(s.as_str(), "crw-rw-rw-") - } - - #[test] - fn display_bits_block() { - let m: u32 = 0o60660; - let s = m.mode_string_full().unwrap(); - assert_eq!(s.as_str(), "brw-rw----") - } - - #[test] - fn display_bits_file() { - let m: u32 = 0o100644; - let s = m.mode_string_full().unwrap(); - assert_eq!(s.as_str(), "-rw-r--r--") - } - - #[test] - fn display_bits_suid() { - let m: u32 = 0o104755; - let s = m.mode_string_full().unwrap(); - assert_eq!(s.as_str(), "-rwsr-xr-x") - } - - #[test] - fn display_bits_sgid() { - let m: u32 = 0o102755; - let s = m.mode_string_full().unwrap(); - assert_eq!(s.as_str(), "-rwxr-sr-x") - } - - #[test] - fn display_bits_sticky() { - let m: u32 = 0o41777; - let s = m.mode_string_full().unwrap(); - assert_eq!(s.as_str(), "drwxrwxrwt") - } -} diff --git a/src/mode/parser.rs b/src/mode/parser.rs deleted file mode 100644 index bb5e034..0000000 --- a/src/mode/parser.rs +++ /dev/null @@ -1,386 +0,0 @@ -use super::{get_umask, Bit, Who}; -use crate::bitflags::BitFlags; -use std::{error, fmt::Display, num::ParseIntError}; - -/// Errors which might occur when parsing Unix permissions from a string -#[derive(Debug, PartialEq)] -pub enum ParseError { - /// the given `Bit` cannot be set for the given `Who` - InvalidBit, - /// the character is not recognized - InvalidChar, - /// the specified octal mode is invalid - OutsideRange, - ParseIntError(ParseIntError), - /// no `Op` is set when parsing a `Bit` - NoOpSet, -} - -impl Display for ParseError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:?}") - } -} - -impl error::Error for ParseError {} - -impl From for ParseError { - fn from(value: ParseIntError) -> Self { - Self::ParseIntError(value) - } -} - -#[derive(PartialEq)] -/// Operations which can be performed to add, remove, or set explicitly the given -/// `Bit` for the given `Who` -enum Op { - /// `Bit`s will be added for `Who` - Add, - /// `Bit`s will be remoed for `Who` - Remove, - /// `Bit`s will be set to exactly the specified arrangement - Equals, -} - -/// A parser for octal and symbolic permissions. `Parser::default` creates an -/// instance which applies the given operations to the default setting for the -/// current user's umask. `Parser::new` creates a parser which applies the given -/// operations to the specified beginning set of permissions. Therefore, when -/// creating new files or directories use `Parser::default`, while `Parser::new` -/// should be used for setting permissions of already existing files or directories. -pub struct Parser { - mode: u32, - op: Option, - who: u32, - bits: u32, -} - -impl Default for Parser { - fn default() -> Self { - let umask = get_umask(); - let mut mode = 0o0666; - mode &= umask; - Self { - mode, - op: None, - who: 0, - bits: 0, - } - } -} - -impl Parser { - #[must_use] - pub fn new(mode: u32) -> Self { - Self { - mode, - op: None, - who: 0, - bits: 0, - } - } - - fn parse_octal(value: &str) -> Result { - let m = u32::from_str_radix(value, 8)?; - if m <= 0o7777 { - Ok(m) - } else { - Err(ParseError::OutsideRange) - } - } - - fn add_who(&mut self, who: Who) -> Result<(), ParseError> { - if self.op.is_some() || !self.bits == 0 { - Err(ParseError::InvalidChar) - } else { - self.who |= who; - Ok(()) - } - } - - fn set_op(&mut self, op: Op) -> Result<(), ParseError> { - if self.op.is_some() || !self.bits == 0 { - Err(ParseError::InvalidChar) - } else { - self.op = Some(op); - if self.who == 0 { - self.who |= 0o111; - } - Ok(()) - } - } - - fn push_read_bits(&mut self) -> Result<(), ParseError> { - if self.op.is_none() { - Err(ParseError::NoOpSet) - } else { - if self.who.contains(Who::User) { - self.bits |= Bit::URead; - } - if self.who.contains(Who::Group) { - self.bits |= Bit::GRead; - } - if self.who.contains(Who::Other) { - self.bits |= Bit::ORead; - } - Ok(()) - } - } - - fn push_write_bits(&mut self) -> Result<(), ParseError> { - if self.op.is_none() { - Err(ParseError::NoOpSet) - } else { - if self.who.contains(Who::User) { - self.bits |= Bit::UWrite; - } - if self.who.contains(Who::Group) { - self.bits |= Bit::GWrite; - } - if self.who.contains(Who::Other) { - self.bits |= Bit::OWrite; - } - Ok(()) - } - } - - fn push_exec_bits(&mut self) -> Result<(), ParseError> { - if self.op.is_none() { - Err(ParseError::NoOpSet) - } else { - if self.who.contains(Who::User) { - self.bits |= Bit::UExec; - } - if self.who.contains(Who::Group) { - self.bits |= Bit::GExec; - } - if self.who.contains(Who::Other) { - self.bits |= Bit::OExec; - } - Ok(()) - } - } - - fn push_suid_sgid(&mut self) -> Result<(), ParseError> { - if self.who == 0 || self.who.contains(Who::Other) { - return Err(ParseError::InvalidBit); - } else if self.op.is_none() { - return Err(ParseError::NoOpSet); - } - if self.who.contains(Who::User) { - self.bits |= Bit::Suid; - } - if self.who.contains(Who::Group) { - self.bits |= Bit::Sgid; - } - Ok(()) - } - - fn push_sticky(&mut self) -> Result<(), ParseError> { - if self.who == 0 || self.who.contains(Who::User) || self.who.contains(Who::Group) { - return Err(ParseError::InvalidBit); - } else if self.op.is_none() { - return Err(ParseError::NoOpSet); - } - if self.who.contains(Who::Other) { - self.bits |= Bit::Sticky; - } - Ok(()) - } - - fn add_bits(&mut self) { - self.mode |= self.bits; - } - - fn remove_bits(&mut self) { - self.mode &= !self.bits; - } - - fn set_bits(&mut self) -> Result<(), ParseError> { - match self.op { - Some(Op::Add) => self.add_bits(), - Some(Op::Remove) => self.remove_bits(), - Some(Op::Equals) => { - if self.who.contains(Who::User) { - self.mode &= !(0o4700); - } - if self.who.contains(Who::Group) { - self.mode &= !(0o2070); - } - if self.who.contains(Who::Other) { - self.mode &= !(0o1007); - } - self.add_bits(); - } - None => return Err(ParseError::NoOpSet), - } - Ok(()) - } - - fn reset(&mut self) { - self.who = 0; - self.op = None; - self.bits = 0; - } - - #[must_use] - pub fn mode(&self) -> u32 { - self.mode - } - - /// Parses a numerical mode from either an octal string or symbolic representation - /// and applies those permissions to the starting set of permissions. - /// # Errors - /// Returns `ParseError` if: - /// - invalid digit - /// - no operation specified - /// - more than one operation specified at a time (multiple operations can - /// be specified separated by comma) - /// - the specified bit cannot be applied to the specified `Who` - /// - bits are specified before operations - /// - the specified octal mode is greater than 0o7777 - pub fn parse(&mut self, value: &str) -> Result { - match Self::parse_octal(value) { - Ok(mode) => { - self.mode = mode; - return Ok(mode); - } - Err(e) => { - if e == ParseError::OutsideRange { - return Err(e); - } - } - } - for c in value.chars() { - match c { - 'u' => self.add_who(Who::User)?, - 'g' => self.add_who(Who::Group)?, - 'o' => self.add_who(Who::Other)?, - 'a' => { - self.add_who(Who::User)?; - self.add_who(Who::Group)?; - self.add_who(Who::Other)?; - } - '-' => self.set_op(Op::Remove)?, - '+' => self.set_op(Op::Add)?, - '=' => self.set_op(Op::Equals)?, - 'r' => self.push_read_bits()?, - 'w' => self.push_write_bits()?, - 'x' => self.push_exec_bits()?, - 's' => self.push_suid_sgid()?, - 't' => self.push_sticky()?, - ',' => { - self.set_bits()?; - self.reset(); - } - _ => return Err(ParseError::InvalidChar), - } - } - self.set_bits()?; - self.reset(); - Ok(self.mode) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn add() { - let mut parser = Parser::new(0o644); - let mode = parser.parse("ug+x"); - assert_eq!(mode, Ok(0o754)); - } - - #[test] - fn remove() { - let mut parser = Parser::new(0o777); - let mode = parser.parse("go-wx"); - assert_eq!(mode, Ok(0o744)); - } - - #[test] - fn octal() { - let mut parser = Parser::default(); - let mode = parser.parse("4755"); - assert_eq!(mode, Ok(0o4755)); - } - - #[test] - fn equals() { - let mut parser = Parser::new(0o701); - let mode = parser.parse("ugo=rw"); - assert_eq!(mode, Ok(0o666)); - } - - #[test] - fn o_equals() { - let mut parser = Parser::new(0o752); - let mode = parser.parse("o=rx"); - assert_eq!(mode, Ok(0o755)); - } - - #[test] - fn empty_who() { - let mut parser = Parser::new(0o644); - let mode = parser.parse("+x"); - assert_eq!(mode, Ok(0o755)); - } - - #[test] - fn compound_ops() { - let mut parser = Parser::new(0o666); - let mode = parser.parse("u+x,g-w,o-r"); - assert_eq!(mode, Ok(0o742)); - } - - #[test] - fn compount_ops2() { - let mut parser = Parser::new(0o4444); - let mode = parser.parse("u+w,ug+x,o=rx,u-s"); - assert_eq!(mode, Ok(0o755)); - } - - #[test] - fn invalid_sticky_bit() { - let mut parser = Parser::default(); - let mode = parser.parse("u+t"); - assert_eq!(mode, Err(ParseError::InvalidBit)); - } - - #[test] - fn invalid_s() { - let mut parser = Parser::default(); - let mode = parser.parse("+s"); - assert_eq!(mode, Err(ParseError::InvalidBit)); - } - - #[test] - fn outside_range() { - let mut parser = Parser::default(); - let mode = parser.parse("10000"); - assert_eq!(mode, Err(ParseError::OutsideRange)) - } - - #[test] - fn no_op() { - let mut parser = Parser::default(); - let mode = parser.parse("rws"); - assert_eq!(mode, Err(ParseError::NoOpSet)); - } - - #[test] - fn ordering_error() { - let mut parser = Parser::default(); - let mode = parser.parse("ux+s"); - assert_eq!(mode, Err(ParseError::NoOpSet)); - } - - #[test] - fn ordering_error1() { - let mut parser = Parser::default(); - let mode = parser.parse("x+s"); - assert_eq!(mode, Err(ParseError::NoOpSet)); - } -} diff --git a/src/mode/who.rs b/src/mode/who.rs deleted file mode 100644 index f1f21c8..0000000 --- a/src/mode/who.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign}; - -#[derive(PartialEq)] -/// The granularity of the given permissions -pub enum Who { - /// applies for the current user - User = 0o100, - /// applies for the current group - Group = 0o10, - /// applies for everyone else - Other = 0o1, -} - -impl BitAnd for u32 { - type Output = u32; - - fn bitand(self, rhs: Who) -> Self::Output { - self & rhs as u32 - } -} - -impl BitAnd for Who { - type Output = u32; - - fn bitand(self, rhs: u32) -> Self::Output { - self as u32 & rhs - } -} - -impl BitAndAssign for u32 { - fn bitand_assign(&mut self, rhs: Who) { - *self = *self & rhs; - } -} - -impl BitOr for u32 { - type Output = u32; - - fn bitor(self, rhs: Who) -> Self::Output { - self | rhs as u32 - } -} - -impl BitOrAssign for u32 { - fn bitor_assign(&mut self, rhs: Who) { - *self = *self | rhs; - } -} diff --git a/unistd/Cargo.toml b/unistd/Cargo.toml new file mode 100644 index 0000000..5d5da8d --- /dev/null +++ b/unistd/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "unistd" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = { workspace = true } +sc = { workspace = true } diff --git a/src/unistd/mod.rs b/unistd/src/lib.rs similarity index 100% rename from src/unistd/mod.rs rename to unistd/src/lib.rs diff --git a/unix-mode b/unix-mode new file mode 160000 index 0000000..0bbf1ae --- /dev/null +++ b/unix-mode @@ -0,0 +1 @@ +Subproject commit 0bbf1aed9ad583017b90aa2c03d3f7870538a914