diff --git a/src/filetransfer/ftp_transfer.rs b/src/filetransfer/ftp_transfer.rs index dc9b108..2530abd 100644 --- a/src/filetransfer/ftp_transfer.rs +++ b/src/filetransfer/ftp_transfer.rs @@ -26,7 +26,7 @@ * SOFTWARE. */ use super::{FileTransfer, FileTransferError, FileTransferErrorType}; -use crate::fs::{FsDirectory, FsEntry, FsFile}; +use crate::fs::{FsDirectory, FsEntry, FsFile, UnixPex}; use crate::utils::fmt::shadow_password; // Includes @@ -158,32 +158,25 @@ impl FtpFileTransfer { /// ### query_unix_pex /// /// Returns unix pex in tuple of values - fn query_unix_pex(f: &File) -> (u8, u8, u8) { + fn query_unix_pex(f: &File) -> (UnixPex, UnixPex, UnixPex) { ( - Self::pex_to_byte( + UnixPex::new( f.can_read(PosixPexQuery::Owner), f.can_write(PosixPexQuery::Owner), f.can_execute(PosixPexQuery::Owner), ), - Self::pex_to_byte( + UnixPex::new( f.can_read(PosixPexQuery::Group), f.can_write(PosixPexQuery::Group), f.can_execute(PosixPexQuery::Group), ), - Self::pex_to_byte( + UnixPex::new( f.can_read(PosixPexQuery::Others), f.can_write(PosixPexQuery::Others), f.can_execute(PosixPexQuery::Others), ), ) } - - /// ### pex_to_byte - /// - /// Convert unix permissions to byte value - fn pex_to_byte(read: bool, write: bool, exec: bool) -> u8 { - ((read as u8) << 2) + ((write as u8) << 1) + (exec as u8) - } } impl FileTransfer for FtpFileTransfer { @@ -775,7 +768,7 @@ mod tests { symlink: None, // UNIX only user: Some(0), // UNIX only group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }); assert!(ftp .rename(&dummy, PathBuf::from("/a/b/c").as_path()) @@ -874,7 +867,10 @@ mod tests { assert!(file.symlink.is_none()); assert_eq!(file.user, None); assert_eq!(file.group, None); - assert_eq!(file.unix_pex.unwrap(), (6, 6, 4)); + assert_eq!( + file.unix_pex.unwrap(), + (UnixPex::from(6), UnixPex::from(6), UnixPex::from(4)) + ); assert_eq!( file.last_access_time .duration_since(UNIX_EPOCH) @@ -930,7 +926,7 @@ mod tests { symlink: None, // UNIX only user: Some(0), // UNIX only group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }; let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false); assert!(ftp.change_dir(Path::new("/tmp")).is_err()); diff --git a/src/filetransfer/scp_transfer.rs b/src/filetransfer/scp_transfer.rs index 478038a..86df810 100644 --- a/src/filetransfer/scp_transfer.rs +++ b/src/filetransfer/scp_transfer.rs @@ -27,7 +27,7 @@ */ // Locals use super::{FileTransfer, FileTransferError, FileTransferErrorType}; -use crate::fs::{FsDirectory, FsEntry, FsFile}; +use crate::fs::{FsDirectory, FsEntry, FsFile, UnixPex}; use crate::system::sshkey_storage::SshKeyStorage; use crate::utils::fmt::{fmt_time, shadow_password}; use crate::utils::parser::parse_lstime; @@ -128,7 +128,11 @@ impl ScpFileTransfer { }; // Get unix pex - let unix_pex = (pex(0..3), pex(3..6), pex(6..9)); + let unix_pex = ( + UnixPex::from(pex(0..3)), + UnixPex::from(pex(3..6)), + UnixPex::from(pex(6..9)), + ); // Parse mtime and convert to SystemTime let mtime: SystemTime = match parse_lstime( @@ -873,7 +877,11 @@ impl FileTransfer for ScpFileTransfer { // Calculate file mode let mode: i32 = match local.unix_pex { None => 0o644, - Some((u, g, o)) => ((u as i32) << 6) + ((g as i32) << 3) + (o as i32), + Some((u, g, o)) => { + ((u.as_byte() as i32) << 6) + + ((g.as_byte() as i32) << 3) + + (o.as_byte() as i32) + } }; // Calculate mtime, atime let times: (u64, u64) = { @@ -1126,7 +1134,7 @@ mod tests { symlink: None, // UNIX only user: Some(0), // UNIX only group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }); assert!(client .rename(&dummy, PathBuf::from("/a/b/c").as_path()) @@ -1239,7 +1247,10 @@ mod tests { .unwrap_file(); assert_eq!(entry.name.as_str(), "Cargo.toml"); assert_eq!(entry.abs_path, PathBuf::from("/tmp/Cargo.toml")); - assert_eq!(entry.unix_pex.unwrap(), (6, 4, 4)); + assert_eq!( + entry.unix_pex.unwrap(), + (UnixPex::from(6), UnixPex::from(4), UnixPex::from(4)) + ); assert_eq!(entry.size, 2056); assert_eq!(entry.ftype.unwrap().as_str(), "toml"); assert!(entry.symlink.is_none()); @@ -1254,7 +1265,10 @@ mod tests { .unwrap_file(); assert_eq!(entry.name.as_str(), "CODE_OF_CONDUCT.md"); assert_eq!(entry.abs_path, PathBuf::from("/tmp/CODE_OF_CONDUCT.md")); - assert_eq!(entry.unix_pex.unwrap(), (6, 6, 6)); + assert_eq!( + entry.unix_pex.unwrap(), + (UnixPex::from(6), UnixPex::from(6), UnixPex::from(6)) + ); assert_eq!(entry.size, 3368); assert_eq!(entry.ftype.unwrap().as_str(), "md"); assert!(entry.symlink.is_none()); @@ -1269,7 +1283,10 @@ mod tests { .unwrap_dir(); assert_eq!(entry.name.as_str(), "docs"); assert_eq!(entry.abs_path, PathBuf::from("/tmp/docs")); - assert_eq!(entry.unix_pex.unwrap(), (7, 5, 5)); + assert_eq!( + entry.unix_pex.unwrap(), + (UnixPex::from(7), UnixPex::from(5), UnixPex::from(5)) + ); assert!(entry.symlink.is_none()); // Short metadata assert!(client @@ -1320,7 +1337,7 @@ mod tests { symlink: None, // UNIX only user: Some(0), // UNIX only group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }; let mut scp: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty()); assert!(scp.change_dir(Path::new("/tmp")).is_err()); diff --git a/src/filetransfer/sftp_transfer.rs b/src/filetransfer/sftp_transfer.rs index 398cfc6..00c50e0 100644 --- a/src/filetransfer/sftp_transfer.rs +++ b/src/filetransfer/sftp_transfer.rs @@ -27,7 +27,7 @@ */ // Locals use super::{FileTransfer, FileTransferError, FileTransferErrorType}; -use crate::fs::{FsDirectory, FsEntry, FsFile}; +use crate::fs::{FsDirectory, FsEntry, FsFile, UnixPex}; use crate::system::sshkey_storage::SshKeyStorage; use crate::utils::fmt::{fmt_time, shadow_password}; @@ -126,11 +126,11 @@ impl SftpFileTransfer { .map(|ext| String::from(ext.to_str().unwrap_or(""))); let uid: Option = metadata.uid; let gid: Option = metadata.gid; - let pex: Option<(u8, u8, u8)> = metadata.perm.map(|x| { + let pex: Option<(UnixPex, UnixPex, UnixPex)> = metadata.perm.map(|x| { ( - ((x >> 6) & 0x7) as u8, - ((x >> 3) & 0x7) as u8, - (x & 0x7) as u8, + UnixPex::from(((x >> 6) & 0x7) as u8), + UnixPex::from(((x >> 3) & 0x7) as u8), + UnixPex::from((x & 0x7) as u8), ) }); let size: u64 = metadata.size.unwrap_or(0); @@ -720,7 +720,7 @@ impl FileTransfer for SftpFileTransfer { // Calculate file mode let mode: i32 = match local.unix_pex { None => 0o644, - Some((u, g, o)) => ((u as i32) << 6) + ((g as i32) << 3) + (o as i32), + Some((u, g, o)) => ((u.as_byte() as i32) << 6) + ((g.as_byte() as i32) << 3) + (o.as_byte() as i32), }; debug!("File mode {:?}", mode); match sftp.open_mode( @@ -924,7 +924,7 @@ mod tests { symlink: None, // UNIX only user: Some(0), // UNIX only group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }); assert!(client .rename(&dummy, PathBuf::from("/a/b/c").as_path()) @@ -1072,7 +1072,7 @@ mod tests { symlink: None, // UNIX only user: Some(0), // UNIX only group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }; let mut sftp: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty()); assert!(sftp.change_dir(Path::new("/tmp")).is_err()); diff --git a/src/fs/explorer/formatter.rs b/src/fs/explorer/formatter.rs index f630b21..006da84 100644 --- a/src/fs/explorer/formatter.rs +++ b/src/fs/explorer/formatter.rs @@ -354,7 +354,9 @@ impl Formatter { pex.push(file_type); match fsentry.get_unix_pex() { None => pex.push_str("?????????"), - Some((owner, group, others)) => pex.push_str(fmt_pex(owner, group, others).as_str()), + Some((owner, group, others)) => pex.push_str( + format!("{}{}{}", fmt_pex(owner), fmt_pex(group), fmt_pex(others)).as_str(), + ), } // Add to cur str, prefix and the key value format!("{}{}{:10}", cur_str, prefix, pex) @@ -533,7 +535,7 @@ impl Formatter { mod tests { use super::*; - use crate::fs::{FsDirectory, FsFile}; + use crate::fs::{FsDirectory, FsFile, UnixPex}; use pretty_assertions::assert_eq; use std::path::PathBuf; @@ -553,10 +555,10 @@ mod tests { creation_time: t_now, size: 8192, ftype: Some(String::from("txt")), - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }); let prefix: String = String::from("h"); let mut callchain: CallChainBlock = CallChainBlock::new(dummy_fmt, prefix, None, None); @@ -593,10 +595,10 @@ mod tests { creation_time: t, size: 8192, ftype: Some(String::from("txt")), - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }); #[cfg(target_family = "unix")] assert_eq!( @@ -623,10 +625,10 @@ mod tests { creation_time: t, size: 8192, ftype: Some(String::from("txt")), - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }); #[cfg(target_family = "unix")] assert_eq!( @@ -718,10 +720,10 @@ mod tests { last_change_time: t_now, last_access_time: t_now, creation_time: t_now, - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((7, 5, 5)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(7), UnixPex::from(5), UnixPex::from(5))), // UNIX only }); #[cfg(target_family = "unix")] assert_eq!( @@ -797,7 +799,7 @@ mod tests { symlink: Some(Box::new(pointer)), // UNIX only user: None, // UNIX only group: None, // UNIX only - unix_pex: Some((7, 5, 5)), // UNIX only + unix_pex: Some((UnixPex::from(7), UnixPex::from(5), UnixPex::from(5))), // UNIX only }); assert_eq!(formatter.fmt(&entry), format!( "projects/ -> project.info 0 0 lrwxr-xr-x {} {} {}", @@ -812,10 +814,10 @@ mod tests { last_change_time: t, last_access_time: t, creation_time: t, - symlink: None, // UNIX only - user: None, // UNIX only - group: None, // UNIX only - unix_pex: Some((7, 5, 5)), // UNIX only + symlink: None, // UNIX only + user: None, // UNIX only + group: None, // UNIX only + unix_pex: Some((UnixPex::from(7), UnixPex::from(5), UnixPex::from(5))), // UNIX only }); assert_eq!(formatter.fmt(&entry), format!( "projects/ 0 0 drwxr-xr-x {} {} {}", @@ -848,7 +850,7 @@ mod tests { symlink: Some(Box::new(pointer)), // UNIX only user: None, // UNIX only group: None, // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }); assert_eq!(formatter.fmt(&entry), format!( "bar.txt -> project.info 0 0 lrw-r--r-- 8.2 KB {} {} {}", @@ -865,10 +867,10 @@ mod tests { creation_time: t, size: 8192, ftype: Some(String::from("txt")), - symlink: None, // UNIX only - user: None, // UNIX only - group: None, // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + symlink: None, // UNIX only + user: None, // UNIX only + group: None, // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }); assert_eq!(formatter.fmt(&entry), format!( "bar.txt 0 0 -rw-r--r-- 8.2 KB {} {} {}", diff --git a/src/fs/explorer/mod.rs b/src/fs/explorer/mod.rs index 5d98603..cb5436f 100644 --- a/src/fs/explorer/mod.rs +++ b/src/fs/explorer/mod.rs @@ -363,7 +363,7 @@ impl FromStr for GroupDirs { mod tests { use super::*; - use crate::fs::{FsDirectory, FsFile}; + use crate::fs::{FsDirectory, FsFile, UnixPex}; use crate::utils::fmt::fmt_time; use pretty_assertions::assert_eq; @@ -587,10 +587,10 @@ mod tests { creation_time: t, size: 8192, ftype: Some(String::from("txt")), - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }); #[cfg(target_family = "unix")] assert_eq!( @@ -669,11 +669,11 @@ mod tests { last_access_time: t_now, creation_time: t_now, size: 64, - ftype: None, // File type - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + ftype: None, // File type + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }), true => FsEntry::Directory(FsDirectory { name: name.to_string(), @@ -681,10 +681,10 @@ mod tests { last_change_time: t_now, last_access_time: t_now, creation_time: t_now, - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((7, 5, 5)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(7), UnixPex::from(5), UnixPex::from(5))), // UNIX only }), } } @@ -699,11 +699,11 @@ mod tests { last_access_time: t_now, creation_time: t_now, size: size, - ftype: None, // File type - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + ftype: None, // File type + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }), true => FsEntry::Directory(FsDirectory { name: name.to_string(), @@ -711,10 +711,10 @@ mod tests { last_change_time: t_now, last_access_time: t_now, creation_time: t_now, - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((7, 5, 5)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(7), UnixPex::from(5), UnixPex::from(5))), // UNIX only }), } } diff --git a/src/fs/mod.rs b/src/fs/mod.rs index cb21fdd..d04d3f5 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -52,10 +52,10 @@ pub struct FsDirectory { pub last_change_time: SystemTime, pub last_access_time: SystemTime, pub creation_time: SystemTime, - pub symlink: Option>, // UNIX only - pub user: Option, // UNIX only - pub group: Option, // UNIX only - pub unix_pex: Option<(u8, u8, u8)>, // UNIX only + pub symlink: Option>, // UNIX only + pub user: Option, // UNIX only + pub group: Option, // UNIX only + pub unix_pex: Option<(UnixPex, UnixPex, UnixPex)>, // UNIX only } /// ### FsFile @@ -70,11 +70,72 @@ pub struct FsFile { pub last_access_time: SystemTime, pub creation_time: SystemTime, pub size: usize, - pub ftype: Option, // File type - pub symlink: Option>, // UNIX only - pub user: Option, // UNIX only - pub group: Option, // UNIX only - pub unix_pex: Option<(u8, u8, u8)>, // UNIX only + pub ftype: Option, // File type + pub symlink: Option>, // UNIX only + pub user: Option, // UNIX only + pub group: Option, // UNIX only + pub unix_pex: Option<(UnixPex, UnixPex, UnixPex)>, // UNIX only +} + +/// ## UnixPex +/// +/// Describes the permissions on POSIX system. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct UnixPex { + read: bool, + write: bool, + execute: bool, +} + +impl UnixPex { + /// ### new + /// + /// Instantiates a new `UnixPex` + pub fn new(read: bool, write: bool, execute: bool) -> Self { + Self { + read, + write, + execute, + } + } + + /// ### can_read + /// + /// Returns whether user can read + pub fn can_read(&self) -> bool { + self.read + } + + /// ### can_write + /// + /// Returns whether user can write + pub fn can_write(&self) -> bool { + self.write + } + + /// ### can_execute + /// + /// Returns whether user can execute + pub fn can_execute(&self) -> bool { + self.execute + } + + /// ### as_byte + /// + /// Convert permission to byte as on POSIX systems + pub fn as_byte(&self) -> u8 { + ((self.read as u8) << 2) + ((self.write as u8) << 1) + (self.execute as u8) + } +} + +impl From for UnixPex { + fn from(bits: u8) -> Self { + Self { + read: ((bits >> 2) & 0x01) != 0, + write: ((bits >> 1) & 0x01) != 0, + execute: (bits & 0x01) != 0, + } + } } impl FsEntry { @@ -171,7 +232,7 @@ impl FsEntry { /// ### get_unix_pex /// /// Get unix pex from `FsEntry` - pub fn get_unix_pex(&self) -> Option<(u8, u8, u8)> { + pub fn get_unix_pex(&self) -> Option<(UnixPex, UnixPex, UnixPex)> { match self { FsEntry::Directory(dir) => dir.unix_pex, FsEntry::File(file) => file.unix_pex, @@ -262,10 +323,10 @@ mod tests { last_change_time: t_now, last_access_time: t_now, creation_time: t_now, - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((7, 5, 5)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(7), UnixPex::from(5), UnixPex::from(5))), // UNIX only }); assert_eq!(entry.get_abs_path(), PathBuf::from("/foo")); assert_eq!(entry.get_name(), String::from("foo")); @@ -279,7 +340,10 @@ mod tests { assert_eq!(entry.is_symlink(), false); assert_eq!(entry.is_dir(), true); assert_eq!(entry.is_file(), false); - assert_eq!(entry.get_unix_pex(), Some((7, 5, 5))); + assert_eq!( + entry.get_unix_pex(), + Some((UnixPex::from(7), UnixPex::from(5), UnixPex::from(5))) + ); assert_eq!(entry.unwrap_dir().abs_path, PathBuf::from("/foo")); } @@ -294,10 +358,10 @@ mod tests { creation_time: t_now, size: 8192, ftype: Some(String::from("txt")), - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }); assert_eq!(entry.get_abs_path(), PathBuf::from("/bar.txt")); assert_eq!(entry.get_name(), String::from("bar.txt")); @@ -308,7 +372,10 @@ mod tests { assert_eq!(entry.get_ftype(), Some(String::from("txt"))); assert_eq!(entry.get_user(), Some(0)); assert_eq!(entry.get_group(), Some(0)); - assert_eq!(entry.get_unix_pex(), Some((6, 4, 4))); + assert_eq!( + entry.get_unix_pex(), + Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))) + ); assert_eq!(entry.is_symlink(), false); assert_eq!(entry.is_dir(), false); assert_eq!(entry.is_file(), true); @@ -327,10 +394,10 @@ mod tests { creation_time: t_now, size: 8192, ftype: Some(String::from("txt")), - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }); entry.unwrap_dir(); } @@ -345,10 +412,10 @@ mod tests { last_change_time: t_now, last_access_time: t_now, creation_time: t_now, - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((7, 5, 5)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(7), UnixPex::from(5), UnixPex::from(5))), // UNIX only }); entry.unwrap_file(); } @@ -364,10 +431,10 @@ mod tests { creation_time: t_now, size: 8192, ftype: Some(String::from("txt")), - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }); assert_eq!(entry.is_hidden(), false); let entry: FsEntry = FsEntry::File(FsFile { @@ -378,10 +445,10 @@ mod tests { creation_time: t_now, size: 8192, ftype: Some(String::from("txt")), - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }); assert_eq!(entry.is_hidden(), true); let entry: FsEntry = FsEntry::Directory(FsDirectory { @@ -390,10 +457,10 @@ mod tests { last_change_time: t_now, last_access_time: t_now, creation_time: t_now, - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((7, 5, 5)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(7), UnixPex::from(5), UnixPex::from(5))), // UNIX only }); assert_eq!(entry.is_hidden(), true); } @@ -410,10 +477,10 @@ mod tests { creation_time: t_now, size: 8192, ftype: Some(String::from("txt")), - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }); // Symlink is None... assert_eq!( @@ -427,10 +494,10 @@ mod tests { last_change_time: t_now, last_access_time: t_now, creation_time: t_now, - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((7, 5, 5)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(7), UnixPex::from(5), UnixPex::from(5))), // UNIX only }); assert_eq!(entry.get_realfile().get_abs_path(), PathBuf::from("/foo")); } @@ -446,10 +513,10 @@ mod tests { last_change_time: t_now, last_access_time: t_now, creation_time: t_now, - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((7, 7, 7)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(7), UnixPex::from(7), UnixPex::from(7))), // UNIX only }); let entry_child: FsEntry = FsEntry::Directory(FsDirectory { name: String::from("projects"), @@ -460,7 +527,7 @@ mod tests { symlink: Some(Box::new(entry_target)), user: Some(0), group: Some(0), - unix_pex: Some((7, 7, 7)), + unix_pex: Some((UnixPex::from(7), UnixPex::from(7), UnixPex::from(7))), }); let entry_root: FsEntry = FsEntry::File(FsFile { name: String::from("projects"), @@ -473,7 +540,7 @@ mod tests { symlink: Some(Box::new(entry_child)), user: Some(0), group: Some(0), - unix_pex: Some((7, 7, 7)), + unix_pex: Some((UnixPex::from(7), UnixPex::from(7), UnixPex::from(7))), }); assert_eq!(entry_root.is_symlink(), true); // get real file @@ -484,4 +551,28 @@ mod tests { PathBuf::from("/home/cvisintin/projects") ); } + + #[test] + fn unix_pex() { + let pex: UnixPex = UnixPex::from(4); + assert_eq!(pex.can_read(), true); + assert_eq!(pex.can_write(), false); + assert_eq!(pex.can_execute(), false); + let pex: UnixPex = UnixPex::from(0); + assert_eq!(pex.can_read(), false); + assert_eq!(pex.can_write(), false); + assert_eq!(pex.can_execute(), false); + let pex: UnixPex = UnixPex::from(3); + assert_eq!(pex.can_read(), false); + assert_eq!(pex.can_write(), true); + assert_eq!(pex.can_execute(), true); + let pex: UnixPex = UnixPex::from(7); + assert_eq!(pex.can_read(), true); + assert_eq!(pex.can_write(), true); + assert_eq!(pex.can_execute(), true); + let pex: UnixPex = UnixPex::from(3); + assert_eq!(pex.as_byte(), 3); + let pex: UnixPex = UnixPex::from(7); + assert_eq!(pex.as_byte(), 7); + } } diff --git a/src/host/mod.rs b/src/host/mod.rs index 9eb434a..593ab74 100644 --- a/src/host/mod.rs +++ b/src/host/mod.rs @@ -38,7 +38,7 @@ use std::fs::set_permissions; use std::os::unix::fs::{MetadataExt, PermissionsExt}; // Locals -use crate::fs::{FsDirectory, FsEntry, FsFile}; +use crate::fs::{FsDirectory, FsEntry, FsFile, UnixPex}; /// ## HostErrorType /// @@ -784,10 +784,10 @@ impl Localhost { /// /// Return string with format xxxxxx to tuple of permissions (user, group, others) #[cfg(target_family = "unix")] - fn u32_to_mode(&self, mode: u32) -> (u8, u8, u8) { - let user: u8 = ((mode >> 6) & 0x7) as u8; - let group: u8 = ((mode >> 3) & 0x7) as u8; - let others: u8 = (mode & 0x7) as u8; + fn u32_to_mode(&self, mode: u32) -> (UnixPex, UnixPex, UnixPex) { + let user: UnixPex = UnixPex::from(((mode >> 6) & 0x7) as u8); + let group: UnixPex = UnixPex::from(((mode >> 3) & 0x7) as u8); + let others: UnixPex = UnixPex::from((mode & 0x7) as u8); (user, group, others) } diff --git a/src/ui/activities/filetransfer/session.rs b/src/ui/activities/filetransfer/session.rs index 77eee6b..a9a4eb7 100644 --- a/src/ui/activities/filetransfer/session.rs +++ b/src/ui/activities/filetransfer/session.rs @@ -707,13 +707,16 @@ impl FileTransferActivity { target_os = "macos", target_os = "linux" ))] - if let Some(pex) = dir.unix_pex { - if let Err(err) = self.host.chmod(local_dir_path.as_path(), pex) { + if let Some((owner, group, others)) = dir.unix_pex { + if let Err(err) = self.host.chmod( + local_dir_path.as_path(), + (owner.as_byte(), group.as_byte(), others.as_byte()), + ) { self.log( LogLevel::Error, format!( "Could not apply file mode {:?} to \"{}\": {}", - pex, + (owner.as_byte(), group.as_byte(), others.as_byte()), local_dir_path.display(), err ), @@ -874,13 +877,16 @@ impl FileTransferActivity { target_os = "macos", target_os = "linux" ))] - if let Some(pex) = remote.unix_pex { - if let Err(err) = self.host.chmod(local, pex) { + if let Some((owner, group, others)) = remote.unix_pex { + if let Err(err) = self + .host + .chmod(local, (owner.as_byte(), group.as_byte(), others.as_byte())) + { self.log( LogLevel::Error, format!( "Could not apply file mode {:?} to \"{}\": {}", - pex, + (owner.as_byte(), group.as_byte(), others.as_byte()), local.display(), err ), diff --git a/src/utils/fmt.rs b/src/utils/fmt.rs index 44f8f6a..60f426b 100644 --- a/src/utils/fmt.rs +++ b/src/utils/fmt.rs @@ -25,6 +25,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +use crate::fs::UnixPex; + use chrono::prelude::*; use std::path::{Path, PathBuf}; use std::time::{Duration, SystemTime}; @@ -32,55 +34,23 @@ use tuirealm::tui::style::Color; /// ### fmt_pex /// -/// Convert 3 bytes of permissions value into ls notation (e.g. rwx-wx--x) -pub fn fmt_pex(owner: u8, group: u8, others: u8) -> String { - let mut mode: String = String::with_capacity(9); - let read: u8 = (owner >> 2) & 0x1; - let write: u8 = (owner >> 1) & 0x1; - let exec: u8 = owner & 0x1; - mode.push_str(match read { - 1 => "r", - _ => "-", - }); - mode.push_str(match write { - 1 => "w", - _ => "-", - }); - mode.push_str(match exec { - 1 => "x", - _ => "-", - }); - let read: u8 = (group >> 2) & 0x1; - let write: u8 = (group >> 1) & 0x1; - let exec: u8 = group & 0x1; - mode.push_str(match read { - 1 => "r", - _ => "-", - }); - mode.push_str(match write { - 1 => "w", - _ => "-", - }); - mode.push_str(match exec { - 1 => "x", - _ => "-", - }); - let read: u8 = (others >> 2) & 0x1; - let write: u8 = (others >> 1) & 0x1; - let exec: u8 = others & 0x1; - mode.push_str(match read { - 1 => "r", - _ => "-", - }); - mode.push_str(match write { - 1 => "w", - _ => "-", - }); - mode.push_str(match exec { - 1 => "x", - _ => "-", - }); - mode +/// Convert permissions bytes of permissions value into ls notation (e.g. rwx,-wx,--x) +pub fn fmt_pex(pex: UnixPex) -> String { + format!( + "{}{}{}", + match pex.can_read() { + true => 'r', + false => '-', + }, + match pex.can_write() { + true => 'w', + false => '-', + }, + match pex.can_execute() { + true => 'x', + false => '-', + } + ) } /// ### instant_to_str @@ -326,14 +296,9 @@ mod tests { #[test] fn test_utils_fmt_pex() { - assert_eq!(fmt_pex(7, 7, 7), String::from("rwxrwxrwx")); - assert_eq!(fmt_pex(7, 5, 5), String::from("rwxr-xr-x")); - assert_eq!(fmt_pex(6, 6, 6), String::from("rw-rw-rw-")); - assert_eq!(fmt_pex(6, 4, 4), String::from("rw-r--r--")); - assert_eq!(fmt_pex(6, 0, 0), String::from("rw-------")); - assert_eq!(fmt_pex(0, 0, 0), String::from("---------")); - assert_eq!(fmt_pex(4, 4, 4), String::from("r--r--r--")); - assert_eq!(fmt_pex(1, 2, 1), String::from("--x-w---x")); + assert_eq!(fmt_pex(UnixPex::from(7)), String::from("rwx")); + assert_eq!(fmt_pex(UnixPex::from(5)), String::from("r-xr-x")); + assert_eq!(fmt_pex(UnixPex::from(6)), String::from("rw-")); } #[test] diff --git a/src/utils/test_helpers.rs b/src/utils/test_helpers.rs index 8706900..8ec02f0 100644 --- a/src/utils/test_helpers.rs +++ b/src/utils/test_helpers.rs @@ -25,7 +25,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -use crate::fs::{FsDirectory, FsEntry, FsFile}; +use crate::fs::{FsDirectory, FsEntry, FsFile, UnixPex}; // ext use std::fs::File; #[cfg(feature = "with-containers")] @@ -53,11 +53,11 @@ pub fn create_sample_file_entry() -> (FsFile, NamedTempFile) { last_access_time: SystemTime::UNIX_EPOCH, creation_time: SystemTime::UNIX_EPOCH, size: 127, - ftype: None, // File type - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + ftype: None, // File type + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }, tmpfile, ) @@ -161,10 +161,10 @@ pub fn make_fsentry(path: PathBuf, is_dir: bool) -> FsEntry { last_change_time: SystemTime::UNIX_EPOCH, last_access_time: SystemTime::UNIX_EPOCH, creation_time: SystemTime::UNIX_EPOCH, - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }), false => FsEntry::File(FsFile { name: path.file_name().unwrap().to_string_lossy().to_string(), @@ -173,11 +173,11 @@ pub fn make_fsentry(path: PathBuf, is_dir: bool) -> FsEntry { last_access_time: SystemTime::UNIX_EPOCH, creation_time: SystemTime::UNIX_EPOCH, size: 127, - ftype: None, // File type - symlink: None, // UNIX only - user: Some(0), // UNIX only - group: Some(0), // UNIX only - unix_pex: Some((6, 4, 4)), // UNIX only + ftype: None, // File type + symlink: None, // UNIX only + user: Some(0), // UNIX only + group: Some(0), // UNIX only + unix_pex: Some((UnixPex::from(6), UnixPex::from(4), UnixPex::from(4))), // UNIX only }), } }