diff --git a/Cargo.lock b/Cargo.lock index cb92e58..9a4460d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,9 +78,9 @@ version = "0.1.0" [[package]] name = "blake2b_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", "arrayvec", @@ -169,9 +169,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.4" +version = "4.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" +checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3" dependencies = [ "bitflags", "clap_lex", @@ -182,11 +182,11 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.1.1" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6540eedc41f8a5a76cf3d8d458057dcdf817be4158a55b5f861f7a5483de75" +checksum = "bd125be87bf4c255ebc50de0b7f4d2a6201e8ac3dc86e39c0ad081dc5e7236fe" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", ] [[package]] @@ -195,7 +195,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7fa41f5e6aa83bd151b70fd0ceaee703d68cd669522795dc812df9edad1252c" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "clap_complete", ] @@ -210,19 +210,19 @@ dependencies = [ [[package]] name = "clap_mangen" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb258c6232b4d728d13d6072656627924c16707aae6267cd5a1ea05abff9a25c" +checksum = "48283ce8d5cd9513633949a674a0442bcb507ab61ed6533863437052ddbb494b" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "roff", ] [[package]] name = "constant_time_eq" -version = "0.1.5" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" [[package]] name = "cpufeatures" @@ -333,9 +333,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "humantime" @@ -359,7 +359,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" dependencies = [ - "hermit-abi 0.3.0", + "hermit-abi 0.3.1", "io-lifetimes", "rustix", "windows-sys", @@ -578,7 +578,7 @@ dependencies = [ "atty", "bitflags-mini", "blake2b_simd", - "clap 4.1.4", + "clap 4.1.6", "clap_complete", "clap_complete_nushell", "clap_mangen", @@ -593,6 +593,7 @@ dependencies = [ "sc", "sha1", "sha2", + "size-display", "termcolor", "textwrap 0.16.0", "unistd", @@ -606,6 +607,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +[[package]] +name = "size-display" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df415f09e1c4d4f58cd0cd08b2f5385bc07cf1878e7a7846abc2225fa00e1e1" + [[package]] name = "smawk" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index e2864f4..ec88332 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ md5 = { version = "0.10", package = "md-5" } sc = { workspace = true } sha1 = "0.10" sha2 = "0.10" +size-display = "0.1" termcolor = "1.1" textwrap = { version = "0.16", default-features = false, features = ["smawk"] } walkdir = "2.3" diff --git a/README.md b/README.md index df1496f..84f2add 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ code between applets, making for an overall smaller binary. - chroot - clear - cut +- df - dirname - echo - false diff --git a/corebox/commands/bootstrap/mod.rs b/corebox/commands/bootstrap/mod.rs index d4751c8..2901191 100644 --- a/corebox/commands/bootstrap/mod.rs +++ b/corebox/commands/bootstrap/mod.rs @@ -395,4 +395,3 @@ fn links(prefix: &str, usr: bool, cmd: &ArgMatches) -> Result<(), Box })?; Ok(()) } - diff --git a/corebox/commands/df/mod.rs b/corebox/commands/df/mod.rs new file mode 100644 index 0000000..2875e0e --- /dev/null +++ b/corebox/commands/df/mod.rs @@ -0,0 +1,150 @@ +use clap::{Arg, ArgAction, ArgMatches, Command}; +use mount::MntEntries; +use shitbox::Cmd; +use size_display::Size; +use std::{error::Error, fmt, io, path::PathBuf}; + +#[derive(Debug, Default)] +pub struct Df; + +impl Cmd for Df { + fn cli(&self) -> Command { + Command::new("df") + .about("report free disk space") + .author("Nathan Fisher") + .version(env!("CARGO_PKG_VERSION")) + .disable_help_flag(true) + .args([ + Arg::new("kilobytes") + .help("Use 1024-byte units, instead of the default 512-byte units, when writing space figures.") + .short('k') + .long("kilobytes") + .action(ArgAction::SetTrue), + Arg::new("human") + .help("print sizes in powers of 1024 (e.g., 1023M)") + .short('h') + .long("human-readable") + .conflicts_with("kilobytes") + .action(ArgAction::SetTrue), + Arg::new("file") + .value_name("FILE") + .num_args(1..) + ]) + } + + fn run(&self, matches: &ArgMatches) -> Result<(), Box> { + let units = if matches.get_flag("kilobytes") { + Units::Kilo + } else if matches.get_flag("human") { + Units::Natural + } else { + Units::Posix + }; + println!("Filesystem {units} Used Avail Capacity Mounted on"); + if let Some(files) = matches.get_many::("file") { + for f in files { + let entry = MntEntries::new("/proc/mounts")? + .filter(|e| e.dir.as_str() == f) + .next() + .ok_or(io::Error::new(io::ErrorKind::Other, "no such filesystem"))?; + print_stats(&entry.fsname, &entry.dir, units)?; + } + } else { + let entries = MntEntries::new("/proc/mounts")?.filter(|x| { + x.fstype != "proc" + && x.fstype != "sysfs" + && x.fstype != "securityfs" + && x.fstype != "cgroup2" + && x.fstype != "pstore" + && x.fstype != "bpf" + && x.fstype != "autofs" + && x.fstype != "hugetlbfs" + && x.fstype != "tracefs" + && x.fstype != "debugfs" + && x.fstype != "configfs" + && x.fstype != "devpts" + && x.fstype != "efivarfs" + && x.fstype != "ramfs" + && x.fstype != "binfmt_misc" + && x.fstype != "fuse.gvfsd-fuse" + && x.fstype != "fuse.portal" + && x.fstype != "mqueue" + && x.fstype != "fusectl" + }); + for e in entries { + print_stats(&e.fsname, &e.dir, units)?; + } + } + Ok(()) + } + + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) + } +} + +#[derive(Clone, Copy, PartialEq)] +enum Units { + Posix, + Kilo, + Natural, +} + +impl fmt::Display for Units { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Posix => write!(f, "512-blocks"), + Self::Kilo => write!(f, " 1K-blocks"), + Self::Natural => write!(f, " Size"), + } + } +} + +fn print_stats(fs: &str, mntpt: &str, units: Units) -> Result<(), Box> { + let p = PathBuf::from(mntpt); + if p.exists() { + let st = shitbox::stat::statfs(mntpt)?; + let bs = match units { + Units::Posix => st.f_frsize / 512, + Units::Kilo => st.f_frsize / 1024, + Units::Natural => st.f_frsize, + }; + let total = st.f_blocks * bs as u64; + let avail = st.f_bfree * bs as u64; + let used = total - avail; + let mut capacity = if total > 0 { (used * 100) / total } else { 0 }; + if used * 100 != capacity * (used + avail) { + capacity += 1; + } + if units == Units::Natural { + print_human(fs, mntpt, total, avail, used, capacity); + return Ok(()); + } + println!("{fs:<12} {total:>9} {used:>9} {avail:>9} {capacity:>7}% {mntpt}"); + Ok(()) + } else { + Err(io::Error::new(io::ErrorKind::Other, "fs does not exist").into()) + } +} + +fn print_human(fs: &str, mntpt: &str, total: u64, avail: u64, used: u64, capacity: u64) { + // Unfortunately these strings must be printed separately due to the way the + // library formats size display, as 0 will be a shorter string due to having + // no suffix, throwing off column alignment + print!("{fs:<12}"); + if total > 0 { + print!("{:>9.1}", Size(total)); + } else { + print!("{total:>10}"); + } + if used > 0 { + print!("{:>9.1}", Size(used)); + } else { + print!("{used:>10}"); + } + if avail > 0 { + println!("{:9.1} {capacity:>7}% {mntpt}", Size(avail)); + } else { + println!("{avail:>10} {capacity:>7}% {mntpt}"); + } +} diff --git a/corebox/commands/mod.rs b/corebox/commands/mod.rs index 64ccd54..d0a3b71 100644 --- a/corebox/commands/mod.rs +++ b/corebox/commands/mod.rs @@ -14,6 +14,7 @@ mod cp; mod cut; mod date; mod dd; +mod df; mod dirname; mod echo; mod expand; @@ -66,6 +67,7 @@ pub fn get(name: &str) -> Option> { "chroot" => Some(Box::new(chroot::Chroot::default())), "corebox" => Some(Box::new(corebox::Corebox::default())), "cut" => Some(Box::new(cut::Cut::default())), + "df" => Some(Box::new(df::Df::default())), "dirname" => Some(Box::new(dirname::Dirname::default())), "echo" => Some(Box::new(echo::Echo::default())), "factor" => Some(Box::new(factor::Factor::default())), @@ -102,7 +104,7 @@ pub fn get(name: &str) -> Option> { } } -pub static COMMANDS: [&str; 42] = [ +pub static COMMANDS: [&str; 43] = [ "base32", "base64", "basename", @@ -113,6 +115,7 @@ pub static COMMANDS: [&str; 42] = [ "chroot", "corebox", "cut", + "df", "dirname", "echo", "false", diff --git a/hashbox/commands/bootstrap/mod.rs b/hashbox/commands/bootstrap/mod.rs index d4751c8..2901191 100644 --- a/hashbox/commands/bootstrap/mod.rs +++ b/hashbox/commands/bootstrap/mod.rs @@ -395,4 +395,3 @@ fn links(prefix: &str, usr: bool, cmd: &ArgMatches) -> Result<(), Box })?; Ok(()) } - diff --git a/mount/src/lib.rs b/mount/src/lib.rs index 74dc247..812d7c8 100644 --- a/mount/src/lib.rs +++ b/mount/src/lib.rs @@ -1,6 +1,6 @@ use errno::Errno; use sc::*; -use std::{ffi::CString, error::Error}; +use std::{error::Error, ffi::CString}; mod mntent; mod mntentries; diff --git a/mount/src/mntent.rs b/mount/src/mntent.rs index 16eee83..6191581 100644 --- a/mount/src/mntent.rs +++ b/mount/src/mntent.rs @@ -6,11 +6,12 @@ use blkid::{ tag::{PartitionTag, SuperblockTag, TagType}, }; use std::{ + error::Error, fmt::{self, Write}, fs::File, io::{self, BufRead, BufReader}, path::PathBuf, - str::FromStr, error::Error, + str::FromStr, }; /// The information for a mount broken out into a struct diff --git a/src/stat/mod.rs b/src/stat/mod.rs index 3addc57..946bf54 100644 --- a/src/stat/mod.rs +++ b/src/stat/mod.rs @@ -1,6 +1,12 @@ use errno::Errno; use sc::*; -use std::{ffi::CString, error::Error}; +use std::{ + error::Error, + ffi::{c_long, CString}, + fs::File, + mem, + os::fd::AsRawFd, +}; #[inline(always)] pub fn mknod(path: &str, mode: u32, dev: u64) -> Result<(), Box> { @@ -37,3 +43,17 @@ pub fn mkfifo(path: &str, mode: u32) -> Result<(), Box> { Err(Errno::from(ret).into()) } } + +#[inline(always)] +pub fn statfs(dir: &str) -> Result> { + let mut buf = mem::MaybeUninit::::uninit(); + let fd = File::open(dir)?; + let ret = unsafe { libc::fstatvfs(fd.as_raw_fd(), buf.as_mut_ptr()) }; + if ret == 0 { + let buf = unsafe { buf.assume_init() }; + Ok(buf) + } else { + let e = unsafe { *libc::__errno_location() }; + Err(Errno::from(e as c_long).into()) + } +} diff --git a/utilbox/commands/bootstrap/mod.rs b/utilbox/commands/bootstrap/mod.rs index d4751c8..2901191 100644 --- a/utilbox/commands/bootstrap/mod.rs +++ b/utilbox/commands/bootstrap/mod.rs @@ -395,4 +395,3 @@ fn links(prefix: &str, usr: bool, cmd: &ArgMatches) -> Result<(), Box })?; Ok(()) } -