stat method for file transfers

This commit is contained in:
ChristianVisintin
2020-12-01 10:57:13 +01:00
parent 698db4219c
commit 9b6e1864d8
2 changed files with 123 additions and 72 deletions

View File

@@ -141,6 +141,11 @@ pub trait FileTransfer {
/// Rename file or a directory /// Rename file or a directory
fn rename(&self, file: &FsEntry, dst: &Path) -> Result<(), FileTransferError>; fn rename(&self, file: &FsEntry, dst: &Path) -> Result<(), FileTransferError>;
/// ### stat
///
/// Stat file and return FsEntry
fn stat(&self, path: &Path) -> Result<FsEntry, FileTransferError>;
/// ### send_file /// ### send_file
/// ///
/// Send file to remote /// Send file to remote

View File

@@ -31,7 +31,7 @@ use super::{FileTransfer, FileTransferError};
use crate::fs::{FsDirectory, FsEntry, FsFile}; use crate::fs::{FsDirectory, FsEntry, FsFile};
// Includes // Includes
use ssh2::{Session, Sftp}; use ssh2::{FileStat, Session, Sftp};
use std::io::{Read, Seek, Write}; use std::io::{Read, Seek, Write};
use std::net::TcpStream; use std::net::TcpStream;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@@ -100,6 +100,78 @@ impl SftpFileTransfer {
false => PathBuf::from(p), false => PathBuf::from(p),
} }
} }
/// ### make_fsentry
///
/// Make fsentry from path and metadata
fn make_fsentry(&self, path: &Path, metadata: &FileStat) -> FsEntry {
// Get common parameters
let file_name: String = String::from(path.file_name().unwrap().to_str().unwrap_or(""));
let file_type: Option<String> = match path.extension() {
Some(ext) => Some(String::from(ext.to_str().unwrap_or(""))),
None => None,
};
let uid: Option<u32> = metadata.uid;
let gid: Option<u32> = metadata.gid;
let pex: Option<(u8, u8, u8)> = match metadata.perm {
Some(perms) => Some((
((perms >> 6) & 0x7) as u8,
((perms >> 3) & 0x7) as u8,
(perms & 0x7) as u8,
)),
None => None,
};
let size: u64 = metadata.size.unwrap_or(0);
let mut atime: SystemTime = SystemTime::UNIX_EPOCH;
atime = atime
.checked_add(Duration::from_secs(metadata.atime.unwrap_or(0)))
.unwrap_or(SystemTime::UNIX_EPOCH);
let mut mtime: SystemTime = SystemTime::UNIX_EPOCH;
mtime = mtime
.checked_add(Duration::from_secs(metadata.mtime.unwrap_or(0)))
.unwrap_or(SystemTime::UNIX_EPOCH);
// Check if symlink
let is_symlink: bool = metadata.file_type().is_symlink();
let symlink: Option<PathBuf> = match is_symlink {
true => {
// Read symlink
match self.sftp.as_ref().unwrap().readlink(path) {
Ok(p) => Some(p),
Err(_) => None,
}
}
false => None,
};
// Is a directory?
match metadata.is_dir() {
true => FsEntry::Directory(FsDirectory {
name: file_name,
abs_path: PathBuf::from(path),
last_change_time: mtime,
last_access_time: atime,
creation_time: SystemTime::UNIX_EPOCH,
readonly: false,
symlink: symlink,
user: uid,
group: gid,
unix_pex: pex,
}),
false => FsEntry::File(FsFile {
name: file_name,
abs_path: PathBuf::from(path),
size: size as usize,
ftype: file_type,
last_change_time: mtime,
last_access_time: atime,
creation_time: SystemTime::UNIX_EPOCH,
readonly: false,
symlink: symlink,
user: uid,
group: gid,
unix_pex: pex,
}),
}
}
} }
impl FileTransfer for SftpFileTransfer { impl FileTransfer for SftpFileTransfer {
@@ -229,77 +301,7 @@ impl FileTransfer for SftpFileTransfer {
let mut entries: Vec<FsEntry> = Vec::with_capacity(files.len()); let mut entries: Vec<FsEntry> = Vec::with_capacity(files.len());
// Iterate over files // Iterate over files
for (path, metadata) in files { for (path, metadata) in files {
// Get common parameters entries.push(self.make_fsentry(path.as_path(), &metadata));
let file_name: String =
String::from(path.file_name().unwrap().to_str().unwrap_or(""));
let file_type: Option<String> = match path.extension() {
Some(ext) => Some(String::from(ext.to_str().unwrap_or(""))),
None => None,
};
let uid: Option<u32> = metadata.uid;
let gid: Option<u32> = metadata.gid;
let pex: Option<(u8, u8, u8)> = match metadata.perm {
Some(perms) => Some((
((perms >> 6) & 0x7) as u8,
((perms >> 3) & 0x7) as u8,
(perms & 0x7) as u8,
)),
None => None,
};
let size: u64 = metadata.size.unwrap_or(0);
let mut atime: SystemTime = SystemTime::UNIX_EPOCH;
atime = atime
.checked_add(Duration::from_secs(metadata.atime.unwrap_or(0)))
.unwrap_or(SystemTime::UNIX_EPOCH);
let mut mtime: SystemTime = SystemTime::UNIX_EPOCH;
mtime = mtime
.checked_add(Duration::from_secs(metadata.mtime.unwrap_or(0)))
.unwrap_or(SystemTime::UNIX_EPOCH);
// Check if symlink
let is_symlink: bool = metadata.file_type().is_symlink();
let symlink: Option<PathBuf> = match is_symlink {
true => {
// Read symlink
match sftp.readlink(path.as_path()) {
Ok(p) => Some(p),
Err(_) => None,
}
}
false => None,
};
// Is a directory?
match metadata.is_dir() {
true => {
entries.push(FsEntry::Directory(FsDirectory {
name: file_name,
abs_path: path.clone(),
last_change_time: mtime,
last_access_time: atime,
creation_time: SystemTime::UNIX_EPOCH,
readonly: false,
symlink: symlink,
user: uid,
group: gid,
unix_pex: pex,
}));
}
false => {
entries.push(FsEntry::File(FsFile {
name: file_name,
abs_path: path.clone(),
size: size as usize,
ftype: file_type,
last_change_time: mtime,
last_access_time: atime,
creation_time: SystemTime::UNIX_EPOCH,
readonly: false,
symlink: symlink,
user: uid,
group: gid,
unix_pex: pex,
}));
}
}
} }
Ok(entries) Ok(entries)
} }
@@ -389,6 +391,27 @@ impl FileTransfer for SftpFileTransfer {
} }
} }
/// ### stat
///
/// Stat file and return FsEntry
fn stat(&self, path: &Path) -> Result<FsEntry, FileTransferError> {
match self.sftp.as_ref() {
Some(sftp) => {
// Get path
let dir: PathBuf = match self.get_remote_path(path) {
Ok(p) => p,
Err(err) => return Err(err),
};
// Get file
match sftp.stat(dir.as_path()) {
Ok(metadata) => Ok(self.make_fsentry(dir.as_path(), &metadata)),
Err(_) => Err(FileTransferError::NoSuchFileOrDirectory),
}
}
None => Err(FileTransferError::UninitializedSession),
}
}
/// ### send_file /// ### send_file
/// ///
/// Send file to remote /// Send file to remote
@@ -603,6 +626,29 @@ mod tests {
assert!(client.disconnect().is_ok()); assert!(client.disconnect().is_ok());
} }
#[test]
fn test_filetransfer_sftp_stat() {
let mut client: SftpFileTransfer = SftpFileTransfer::new();
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and sftp
assert!(client.session.is_some());
assert!(client.sftp.is_some());
assert_eq!(client.wrkdir, PathBuf::from("/"));
let file: FsEntry = client.stat(PathBuf::from("readme.txt").as_path()).ok().unwrap();
if let FsEntry::File(file) = file {
assert_eq!(file.abs_path, PathBuf::from("/readme.txt"));
} else {
panic!("Expected readme.txt to be a file");
}
}
#[test] #[test]
fn test_filetransfer_sftp_recv() { fn test_filetransfer_sftp_recv() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(); let mut client: SftpFileTransfer = SftpFileTransfer::new();