Localhost implementation

This commit is contained in:
ChristianVisintin
2020-11-08 21:39:01 +01:00
parent aa963b5000
commit 2d9ddd3c10
4 changed files with 298 additions and 10 deletions

88
Cargo.lock generated
View File

@@ -92,6 +92,17 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "getrandom"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [
"cfg-if 0.1.10",
"libc",
"wasi",
]
[[package]]
name = "instant"
version = "0.1.8"
@@ -222,12 +233,68 @@ dependencies = [
"winapi",
]
[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom",
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
@@ -272,12 +339,27 @@ dependencies = [
"winapi",
]
[[package]]
name = "tempfile"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
dependencies = [
"cfg-if 0.1.10",
"libc",
"rand",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "termscp"
version = "0.1.0"
dependencies = [
"crossterm 0.18.2",
"getopts",
"tempfile",
"tui",
"users",
]
@@ -317,6 +399,12 @@ dependencies = [
"log",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "winapi"
version = "0.3.9"

View File

@@ -20,6 +20,9 @@ getopts = "0.2.21"
tui = { version = "0.12.0", features = ["crossterm"], default-features = false }
users = "0.11.0"
[dev-dependencies]
tempfile = "3"
[[bin]]
name = "termscp"
path = "src/main.rs"

View File

@@ -42,7 +42,7 @@ pub enum FsEntry {
#[derive(Clone)]
pub struct FsDirectory {
pub name: PathBuf,
pub name: String,
pub last_change_time: SystemTime,
pub last_access_time: SystemTime,
pub creation_time: SystemTime,
@@ -59,7 +59,7 @@ pub struct FsDirectory {
#[derive(Clone)]
pub struct FsFile {
pub name: PathBuf,
pub name: String,
pub last_change_time: SystemTime,
pub last_access_time: SystemTime,
pub creation_time: SystemTime,

View File

@@ -23,14 +23,14 @@
*
*/
use std::fs::{self, Metadata};
use std::fs::{self, File, Metadata, OpenOptions};
use std::path::{Path, PathBuf};
use std::time::SystemTime;
// Metadata ext
#[cfg(any(unix, macos, linux))]
extern crate users;
#[cfg(any(unix, macos, linux))]
use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::os::unix::fs::MetadataExt;
#[cfg(any(unix, macos, linux))]
use users::{get_group_by_gid, get_user_by_uid};
@@ -44,6 +44,7 @@ pub enum HostError {
NoSuchFileOrDirectory,
ReadonlyFile,
DirNotAccessible,
FileNotAccessible,
}
/// ## Localhost
@@ -113,6 +114,42 @@ impl Localhost {
Ok(self.wrkdir.clone())
}
/// ### open_file_read
///
/// Open file for read
pub fn open_file_read(&self, file: &Path) -> Result<File, HostError> {
if !self.file_exists(file) {
return Err(HostError::NoSuchFileOrDirectory);
}
match OpenOptions::new()
.create(false)
.read(true)
.write(false)
.open(file)
{
Ok(f) => Ok(f),
Err(_) => Err(HostError::FileNotAccessible),
}
}
/// ### open_file_write
///
/// Open file for write
pub fn open_file_write(&self, file: &Path) -> Result<File, HostError> {
match OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(file)
{
Ok(f) => Ok(f),
Err(_) => match self.file_exists(file) {
true => Err(HostError::ReadonlyFile),
false => Err(HostError::FileNotAccessible),
},
}
}
/// ### file_exists
///
/// Returns whether provided file path exists
@@ -137,19 +174,21 @@ impl Localhost {
let is_symlink: bool = attr.file_type().is_symlink();
// Get user stuff
let user: Option<String> = match get_user_by_uid(attr.uid()) {
Some(user) => String::from(user.name().to_str().unwrap_or("")),
Some(user) => Some(String::from(user.name().to_str().unwrap_or(""))),
None => None,
};
let group: Option<String> = match get_group_by_gid(attr.gid()) {
Some(gruop) => String::from(group.name().to_str().unwrap_or("")),
Some(group) => Some(String::from(group.name().to_str().unwrap_or(""))),
None => None,
};
let file_name: String =
String::from(path.file_name().unwrap().to_str().unwrap_or(""));
// Match dir / file
fs_entries.push(match path.is_dir() {
true => {
// Is dir
FsEntry::Directory(FsDirectory {
name: path.file_name(),
name: file_name,
last_change_time: attr.modified().unwrap_or(SystemTime::UNIX_EPOCH),
last_access_time: attr.accessed().unwrap_or(SystemTime::UNIX_EPOCH),
creation_time: attr.created().unwrap_or(SystemTime::UNIX_EPOCH),
@@ -176,7 +215,7 @@ impl Localhost {
None => None,
};
FsEntry::File(FsFile {
name: path.file_name(),
name: file_name,
last_change_time: attr.modified().unwrap_or(SystemTime::UNIX_EPOCH),
last_access_time: attr.accessed().unwrap_or(SystemTime::UNIX_EPOCH),
creation_time: attr.created().unwrap_or(SystemTime::UNIX_EPOCH),
@@ -219,11 +258,13 @@ impl Localhost {
if let Ok(entry) = entry {
let path: PathBuf = entry.path();
let attr: Metadata = fs::metadata(path.clone()).unwrap();
let file_name: String =
String::from(path.file_name().unwrap().to_str().unwrap_or(""));
fs_entries.push(match path.is_dir() {
true => {
// Is dir
FsEntry::Directory(FsDirectory {
name: path,
name: file_name,
last_change_time: attr.modified().unwrap_or(SystemTime::UNIX_EPOCH),
last_access_time: attr.accessed().unwrap_or(SystemTime::UNIX_EPOCH),
creation_time: attr.created().unwrap_or(SystemTime::UNIX_EPOCH),
@@ -241,7 +282,7 @@ impl Localhost {
None => None,
};
FsEntry::File(FsFile {
name: path,
name: file_name,
last_change_time: attr.modified().unwrap_or(SystemTime::UNIX_EPOCH),
last_access_time: attr.accessed().unwrap_or(SystemTime::UNIX_EPOCH),
creation_time: attr.created().unwrap_or(SystemTime::UNIX_EPOCH),
@@ -271,3 +312,159 @@ impl Localhost {
(user, group, others)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
#[cfg(any(unix, macos, linux))]
use std::os::unix::fs::PermissionsExt;
#[test]
#[cfg(any(unix, macos, linux))]
fn test_host_localhost_new() {
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
assert_eq!(host.wrkdir, PathBuf::from("/bin"));
// Scan dir
let entries = std::fs::read_dir(PathBuf::from("/bin").as_path()).unwrap();
let mut counter: usize = 0;
for _ in entries {
counter = counter + 1;
}
assert_eq!(host.files.len(), counter);
}
#[test]
#[cfg(target_os = "windows")]
fn test_host_localhost_new() {
let host: Localhost = Localhost::new(PathBuf::from("C:\\")).ok().unwrap();
assert_eq!(host.wrkdir, PathBuf::from("C:\\"));
// Scan dir
let entries = std::fs::read_dir(PathBuf::from("C:\\").as_path()).unwrap();
let mut counter: usize = 0;
for _ in entries {
counter = counter + 1;
}
assert_eq!(host.files.len(), counter);
}
#[test]
#[should_panic]
fn test_host_localhost_new_bad() {
Localhost::new(PathBuf::from("/omargabber/123/345"))
.ok()
.unwrap();
}
#[test]
#[cfg(any(unix, macos, linux))]
fn test_host_localhost_pwd() {
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
assert_eq!(host.pwd(), PathBuf::from("/bin"));
}
#[test]
#[cfg(any(unix, macos, linux))]
fn test_host_localhost_list_files() {
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
// Scan dir
let entries = std::fs::read_dir(PathBuf::from("/bin").as_path()).unwrap();
let mut counter: usize = 0;
for _ in entries {
counter = counter + 1;
}
assert_eq!(host.list_dir().len(), counter);
}
#[test]
#[cfg(any(unix, macos, linux))]
fn test_host_localhost_change_dir() {
let mut host: Localhost = Localhost::new(PathBuf::from("/usr")).ok().unwrap();
let new_dir: PathBuf = PathBuf::from("/usr");
assert!(host.change_wrkdir(new_dir.clone()).is_ok());
// Verify new files
// Scan dir
let entries = std::fs::read_dir(PathBuf::from(new_dir).as_path()).unwrap();
let mut counter: usize = 0;
for _ in entries {
counter = counter + 1;
}
assert_eq!(host.files.len(), counter);
}
#[test]
#[cfg(any(unix, macos, linux))]
#[should_panic]
fn test_host_localhost_change_dir_failed() {
let mut host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
let new_dir: PathBuf = PathBuf::from("/omar/gabber/123/456");
assert!(host.change_wrkdir(new_dir.clone()).is_ok());
}
#[test]
#[cfg(any(unix, macos, linux))]
fn test_host_localhost_open_read() {
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
// Create temp file
let file: tempfile::NamedTempFile = create_sample_file();
assert!(host.open_file_read(file.path()).is_ok());
}
#[test]
#[cfg(any(unix, macos, linux))]
#[should_panic]
fn test_host_localhost_open_read_err_no_such_file() {
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
assert!(host
.open_file_read(PathBuf::from("/bin/foo-bar-test-omar-123-456-789.txt").as_path())
.is_ok());
}
#[test]
#[cfg(any(unix, macos, linux))]
fn test_host_localhost_open_read_err_not_accessible() {
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
let file: tempfile::NamedTempFile = create_sample_file();
//let mut perms = fs::metadata(file.path())?.permissions();
fs::set_permissions(file.path(), PermissionsExt::from_mode(0o222)).unwrap();
//fs::set_permissions(file.path(), perms)?;
assert!(host.open_file_read(file.path()).is_err());
}
#[test]
#[cfg(any(unix, macos, linux))]
fn test_host_localhost_open_write() {
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
// Create temp file
let file: tempfile::NamedTempFile = create_sample_file();
assert!(host.open_file_write(file.path()).is_ok());
}
#[test]
#[cfg(any(unix, macos, linux))]
fn test_host_localhost_open_write_err() {
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
let file: tempfile::NamedTempFile = create_sample_file();
//let mut perms = fs::metadata(file.path())?.permissions();
fs::set_permissions(file.path(), PermissionsExt::from_mode(0o444)).unwrap();
//fs::set_permissions(file.path(), perms)?;
assert!(host.open_file_write(file.path()).is_err());
}
/// ### create_sample_file
///
/// Create a sample file
#[cfg(any(unix, macos, linux))]
fn create_sample_file() -> tempfile::NamedTempFile {
// Write
let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();
write!(
tmpfile,
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.\nMauris ultricies consequat eros,\nnec scelerisque magna imperdiet metus.\n"
)
.unwrap();
tmpfile
}
}