From c80d9330d8085290db1e380be2f4d3c393b728d2 Mon Sep 17 00:00:00 2001 From: Benjamin Nguyen Date: Tue, 23 May 2023 11:24:23 -0700 Subject: [PATCH 1/8] support for user and group --- Cargo.lock | 18 +++++-- Cargo.toml | 1 + src/disk_usage/file_size/byte.rs | 10 ++-- src/fs/mod.rs | 5 +- src/fs/ug.rs | 89 ++++++++++++++++++++++++++++++++ src/main.rs | 1 + src/render/grid/cell.rs | 6 +-- src/render/grid/mod.rs | 18 ++++--- src/tree/node/mod.rs | 27 ++++++++-- tests/flat.rs | 2 +- 10 files changed, 152 insertions(+), 25 deletions(-) create mode 100644 src/fs/ug.rs diff --git a/Cargo.lock b/Cargo.lock index d3009ac5..bc25b3c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -225,6 +225,7 @@ dependencies = [ "clap", "clap_complete", "dirs", + "errno 0.3.1", "filesize", "ignore", "indextree", @@ -253,13 +254,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -631,7 +632,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d" dependencies = [ "bitflags", - "errno 0.3.0", + "errno 0.3.1", "io-lifetimes", "libc", "linux-raw-sys 0.3.1", @@ -944,6 +945,15 @@ dependencies = [ "windows-targets 0.42.2", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + [[package]] name = "windows-targets" version = "0.42.2" diff --git a/Cargo.toml b/Cargo.toml index 68e4f6a1..e179b3fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ chrono = "0.4.24" clap = { version = "4.1.1", features = ["derive"] } clap_complete = "4.1.1" dirs = "5.0" +errno = "0.3.1" filesize = "0.2.0" ignore = "0.4.2" indextree = "4.6.0" diff --git a/src/disk_usage/file_size/byte.rs b/src/disk_usage/file_size/byte.rs index 4ecfef78..b694cfc4 100644 --- a/src/disk_usage/file_size/byte.rs +++ b/src/disk_usage/file_size/byte.rs @@ -98,7 +98,7 @@ impl Display for Metric { } else { let base_value = unit.base_value(); let size = value / (base_value as f64); - write!(f, "{size:.2} {unit}") + write!(f, "{size:.1} {unit}") } } PrefixKind::Bin => { @@ -113,7 +113,7 @@ impl Display for Metric { } else { let base_value = unit.base_value(); let size = value / (base_value as f64); - write!(f, "{size:.2} {unit}") + write!(f, "{size:.1} {unit}") } } } @@ -136,7 +136,7 @@ fn test_metric() { human_readable: true, prefix_kind: PrefixKind::Si, }; - assert_eq!(format!("{}", metric), "1.00 KB"); + assert_eq!(format!("{}", metric), "1.0 KB"); let metric = Metric { value: 1000, @@ -152,7 +152,7 @@ fn test_metric() { human_readable: true, prefix_kind: PrefixKind::Bin, }; - assert_eq!(format!("{}", metric), "1.00 KiB"); + assert_eq!(format!("{}", metric), "1.0 KiB"); let metric = Metric { value: 2_u64.pow(20), @@ -160,7 +160,7 @@ fn test_metric() { human_readable: true, prefix_kind: PrefixKind::Bin, }; - assert_eq!(format!("{}", metric), "1.00 MiB"); + assert_eq!(format!("{}", metric), "1.0 MiB"); let metric = Metric { value: 123454, diff --git a/src/fs/mod.rs b/src/fs/mod.rs index a266260f..9419eab8 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -8,10 +8,13 @@ pub mod inode; #[cfg(unix)] pub mod permissions; -#[cfg(unix)] /// Determining whether or not a file has extended attributes. +#[cfg(unix)] pub mod xattr; +/// Concerned with determining group and owner of file. +pub mod ug; + /// Returns the path to the target of the soft link. Returns `None` if provided `dir_entry` isn't a /// symlink. pub fn symlink_target(dir_entry: &DirEntry) -> Option { diff --git a/src/fs/ug.rs b/src/fs/ug.rs new file mode 100644 index 00000000..b067aacc --- /dev/null +++ b/src/fs/ug.rs @@ -0,0 +1,89 @@ +use errno::{errno, set_errno, Errno}; +use ignore::DirEntry; +use std::{convert::AsRef, ffi::CStr, mem, os::unix::ffi::OsStrExt, path::Path, ptr}; + +impl UserGroupInfo for DirEntry { + fn path(&self) -> &Path { + self.path() + } +} + +type Owner = String; +type Group = String; + +/// Trait that allows for files to query their owner and group. +pub trait UserGroupInfo { + fn path(&self) -> &Path; + + /// Attemps to query the owner of the implementor. + fn try_get_owner(&self) -> Result { + unsafe { + let libc::stat { st_uid, .. } = try_init_stat(self.path())?; + try_get_user(st_uid) + } + } + + /// Attempts to query both the owner and group of the implementor. + fn try_get_owner_and_group(&self) -> Result<(Owner, Group), Errno> { + unsafe { + let libc::stat { st_uid, st_gid, .. } = try_init_stat(self.path())?; + let user = try_get_user(st_uid)?; + let group = try_get_group(st_gid)?; + + Ok((user, group)) + } + } +} + +/// A wrapper around [`libc::stat`]. +unsafe fn try_init_stat>(path: P) -> Result { + let mut stat = mem::zeroed::(); + + let stat_ptr = ptr::addr_of_mut!(stat); + let path_ptr = path + .as_ref() + .as_os_str() + .as_bytes() + .as_ptr() + .cast::(); + + if libc::stat(path_ptr, stat_ptr) == -1 { + return Err(errno()); + } + + Ok(stat) +} + +/// Attempts to return the name of the group associated with `gid`. +unsafe fn try_get_group(gid: libc::gid_t) -> Result { + set_errno(Errno(0)); + + let group = libc::getgrgid(gid); + + let errno = errno(); + + if errno.0 != 0 { + return Err(errno); + } + + let libc::group { gr_name, .. } = *group; + + Ok(CStr::from_ptr(gr_name).to_string_lossy().to_string()) +} + +/// Attempts to return the name of the user associated with `uid`. +unsafe fn try_get_user(uid: libc::uid_t) -> Result { + set_errno(Errno(0)); + + let pwd = libc::getpwuid(uid); + + let errno = errno(); + + if errno.0 != 0 { + return Err(errno); + } + + let libc::passwd { pw_name, .. } = *pwd; + + Ok(CStr::from_ptr(pw_name).to_string_lossy().to_string()) +} diff --git a/src/main.rs b/src/main.rs index ea69ca84..e33ae735 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ )] #![allow( clippy::struct_excessive_bools, + clippy::too_many_arguments, clippy::cast_precision_loss, clippy::cast_sign_loss, clippy::cast_possible_truncation diff --git a/src/render/grid/cell.rs b/src/render/grid/cell.rs index 028479fa..9fd44e97 100644 --- a/src/render/grid/cell.rs +++ b/src/render/grid/cell.rs @@ -1,5 +1,5 @@ use crate::{ - context::{Context, time}, + context::{time, Context}, disk_usage::{ file_size::{byte, DiskUsage, FileSize}, units::PrefixKind, @@ -216,7 +216,7 @@ impl<'a> Cell<'a> { write!(f, "{formatted_datetime}") } - /// Rules on how to format timestamp + /// Rules on how to format timestamp #[cfg(unix)] #[inline] fn fmt_timestamp(&self, dt: DateTime) -> String { @@ -228,7 +228,7 @@ impl<'a> Cell<'a> { time::Format::Short => dt.format("%Y-%m-%d"), }; - format!("{:>12}", delayed_format) + format!("{delayed_format:>12}") } /// Rules on how to format permissions for rendering diff --git a/src/render/grid/mod.rs b/src/render/grid/mod.rs index 2f46a883..34b3d738 100644 --- a/src/render/grid/mod.rs +++ b/src/render/grid/mod.rs @@ -48,13 +48,14 @@ impl Display for Row<'_, Tree> { ); let row = if ctx.long { - let ino = Cell::new(node, ctx, cell::Kind::Ino); + //let ino = Cell::new(node, ctx, cell::Kind::Ino); let perms = Cell::new(node, ctx, cell::Kind::Permissions); - let nlink = Cell::new(node, ctx, cell::Kind::Nlink); - let blocks = Cell::new(node, ctx, cell::Kind::Blocks); + //let nlink = Cell::new(node, ctx, cell::Kind::Nlink); + //let blocks = Cell::new(node, ctx, cell::Kind::Blocks); let time = Cell::new(node, ctx, cell::Kind::Datetime); - format!("{ino} {perms} {nlink} {blocks} {time} {size} {name}") + //format!("{ino} {perms} {nlink} {blocks} {time} {size} {name}") + format!("{perms} {time} {size} {name}") } else { format!("{size} {name}") }; @@ -106,13 +107,14 @@ impl Display for Row<'_, Flat> { let path = Cell::new(node, ctx, cell::Kind::FilePath); let row = if ctx.long { - let ino = Cell::new(node, ctx, cell::Kind::Ino); + //let ino = Cell::new(node, ctx, cell::Kind::Ino); let perms = Cell::new(node, ctx, cell::Kind::Permissions); - let nlink = Cell::new(node, ctx, cell::Kind::Nlink); - let blocks = Cell::new(node, ctx, cell::Kind::Blocks); + //let nlink = Cell::new(node, ctx, cell::Kind::Nlink); + //let blocks = Cell::new(node, ctx, cell::Kind::Blocks); let time = Cell::new(node, ctx, cell::Kind::Datetime); - format!("{ino} {perms} {nlink} {blocks} {time} {size} {path}") + //format!("{ino} {perms} {nlink} {blocks} {time} {size} {path}") + format!("{perms} {time} {size} {path}") } else { format!("{size} {path}") }; diff --git a/src/tree/node/mod.rs b/src/tree/node/mod.rs index 4281821d..9466083a 100644 --- a/src/tree/node/mod.rs +++ b/src/tree/node/mod.rs @@ -23,6 +23,7 @@ use crate::{ disk_usage::file_size::block, fs::{ permissions::{FileMode, SymbolicNotation}, + ug::UserGroupInfo, xattr::ExtendedAttr, }, }; @@ -45,6 +46,10 @@ pub struct Node { #[cfg(unix)] has_xattrs: bool, + #[cfg(unix)] + owner: Option, + #[cfg(unix)] + group: Option, } impl Node { @@ -58,6 +63,8 @@ impl Node { inode: Option, #[cfg(unix)] has_xattrs: bool, + #[cfg(unix)] owner: Option, + #[cfg(unix)] group: Option, ) -> Self { Self { dir_entry, @@ -68,6 +75,10 @@ impl Node { inode, #[cfg(unix)] has_xattrs, + #[cfg(unix)] + owner, + #[cfg(unix)] + group, } } @@ -273,10 +284,16 @@ impl TryFrom<(DirEntry, &Context)> for Node { let inode = Inode::try_from(&metadata).ok(); #[cfg(unix)] - let has_xattrs = if ctx.long { - dir_entry.has_xattrs() + let (has_xattrs, owner, group) = if ctx.long { + let has_xattrs = dir_entry.has_xattrs(); + + if let Ok((o, g)) = dir_entry.try_get_owner_and_group() { + (has_xattrs, Some(o), Some(g)) + } else { + (has_xattrs, None, None) + } } else { - false + (false, None, None) }; Ok(Self::new( @@ -288,6 +305,10 @@ impl TryFrom<(DirEntry, &Context)> for Node { inode, #[cfg(unix)] has_xattrs, + #[cfg(unix)] + owner, + #[cfg(unix)] + group, )) } } diff --git a/tests/flat.rs b/tests/flat.rs index 23dcd485..eede31a7 100644 --- a/tests/flat.rs +++ b/tests/flat.rs @@ -37,7 +37,7 @@ fn flat_human() { 446 B lipsum 308 B dream_cycle/polaris.txt 308 B dream_cycle -1.21 KiB data + 1.2 KiB data 3 directories, 6 files" ) From 407b612b47ad3bc9449ea431c247d171e92beda1 Mon Sep 17 00:00:00 2001 From: Benjamin Nguyen Date: Tue, 23 May 2023 11:37:49 -0700 Subject: [PATCH 2/8] pull unix attrs into sep struct from node --- src/tree/node/mod.rs | 40 +++++++++++----------------------------- src/tree/node/unix.rs | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 29 deletions(-) create mode 100644 src/tree/node/unix.rs diff --git a/src/tree/node/mod.rs b/src/tree/node/mod.rs index 9466083a..f1c7fe5c 100644 --- a/src/tree/node/mod.rs +++ b/src/tree/node/mod.rs @@ -31,6 +31,9 @@ use crate::{ /// Ordering and sorting rules for [Node]. pub mod cmp; +/// File attributes specific to Unix systems. +pub mod unix; + /// A node of [`Tree`] that can be created from a [`DirEntry`]. Any filesystem I/O and /// relevant system calls are expected to complete after initialization. A `Node` when `Display`ed /// uses ANSI colors determined by the file-type and `LS_COLORS`. @@ -45,11 +48,7 @@ pub struct Node { inode: Option, #[cfg(unix)] - has_xattrs: bool, - #[cfg(unix)] - owner: Option, - #[cfg(unix)] - group: Option, + unix_attrs: unix::Attrs, } impl Node { @@ -61,10 +60,7 @@ impl Node { style: Option