Closed insinfo closed 2 years ago
Yep, you're right, thanks for reporting!
There is a wrong clause in the current regex which checks the ls
output, which doesn't allow any character which is not alphanumeric.
Anyway, termscp shouldn't be used as a library (it was possible until the latest version) and it it won't be possible anymore in the near future. Feel free to copy the code of the scp.rs file into your project, as long as it is compliant with the MIT license. I'm not going to patch this really soon, but in two months, since a release of the binary requires too much effort and I don't have the time right now.
In the next months I'm going to release this library https://github.com/veeso/remotefs-rs which will expose the entire filetransfer module of termscp, so you'll be able to re-import the original code.
For the moment, if you want to fix it by yourself, the correct regex should be:
^([\-ld])([\-rwxs]{9})\s+(\d+)\s+(.+)\s+(.+)\s+(\d+)\s+(\w{3}\s+\d{1,2}\s+(?:\d{1,2}:\d{1,2}|\d{4}))\s+(.+)$
Thank you so much for your quick feedback,
"Feel free to copy the code of the scp.rs file into your project, as long as it is compliant with the MIT license."
I followed your suggestion and copied the relevant code and made the Regex change and it worked.
I'm developing an open source linux server backup utility application in flutter for desktop, and currently the entire implementation is written in dart with libssh(not libssh2) links to handle SCP/SFTP, but the file transfer is very slow, so I'm trying to re-implement in rust the part that handles file transfer.
"In the next months I'm going to release this library https://github.com/veeso/remotefs-rs which will expose the entire file transfer module of termscp, so you'll be able to re-import the original code. "
Great news to know that you will be releasing a specific library for file transfer, there are not many open source file transfer library options that implement various protocols like SFTP/SCP/FTP etc, it would be nice if this library you are developing already implement directory synchronization similar to rsync over SSH, maybe using ideas and parts of these projects: https://github.com/WanzenBug/rustsync https://github.com/wchang22/LuminS https://github.com/dropbox/fast_rsync
https://github.com/insinfo/fsbackup https://github.com/insinfo/fsbackup_engine
@veeso i found another problem, it seems that the "list_dir" function is falling into an infinite loop, i don't know exactly what is causing it but maybe a better option would be to use the "find" command instead of the "ls -la" to have more control , accuracy of file modification time and better optimization for when you want to do a "list_dir_recursively" to get the complete list of files before starting the copy.
find "$(cd '/var/www/'; pwd)" -maxdepth 1 -printf '%M|%u|%g|%s|%Ts|%p|%f|%l\n'
16:35:21 [INFO] Getting file entries in /var/www/.pub-cache/hosted/pub.dartlang.org/devtools-0.1.1/build/pack/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk
16:35:21 [INFO] Getting file entries in /var/www/.pub-cache/hosted/pub.dartlang.org/devtools-0.1.1/build/pack/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk
16:35:21 [INFO] Getting file entries in /var/www/.pub-cache/hosted/pub.dartlang.org/devtools-0.1.1/build/pack/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk/$sdk
fn main() {
//configure log
CombinedLogger::init(
vec![
TermLogger::new(LevelFilter::Debug, Config::default(), TerminalMode::Mixed, ColorChoice::Auto),
// WriteLogger::new(LevelFilter::Info, Config::default(), File::create("my_rust_binary.log").unwrap()),
]
).unwrap();
//configure host
let localhost = Localhost::new(PathBuf::from(r"C:\MyRustProjects\fsbackup_engine")).unwrap();
//connection setup
let config = ProtocolParams::Generic(GenericProtocolParams {
address: "192.168.133.13".to_string(),
port: 22,
username: Some(String::from("isaque.neves")),
password: Some(String::from("Ins257257")),
});
let mut activity = FileTransferActivity::new(localhost, FileTransferProtocol::Scp, config);
activity.connect();
//let file_to_download = FsEntry::File(FsFile::from_str("/var/www/.profile"));
// /var/www/html/Alex
let dir_to_download =FsDirectory::from_str("/var/www");
let entry_to_download = FsEntry::Directory(dir_to_download.clone());
let dest_dir_path = r"C:\MyRustProjects\fsbackup_engine\download";
let destiny = PathBuf::from(dest_dir_path);
//create directory if it doesn't exist
std::fs::create_dir_all(dest_dir_path).expect(format!("Failed to create directory: {}", dest_dir_path).as_str());
let start = std::time::Instant::now();
match activity.filetransfer_recv(TransferPayload::Any(entry_to_download), &destiny, None) {
//match activity.filetransfer_recv_dir_as_zip(&dir_to_download, &destiny, "result.zip") {
Ok(result) => {
let duration = start.elapsed();
info!("Time elapsed in filetransfer_recv: {:?}", duration);
println!("result: {:?}", result);
}
Err(e) => {
println!("error: {:?}", e);//eprintln!()
process::exit(0);//1
}
}
}
/// ### list_dir_recursively
///
/// List directory entries recursively
fn list_dir_recursively(&mut self, base_path: &Path) -> FileTransferResult<Vec<FsEntry>> {
match self.is_connected() {
true => {
info!("list dir recursively of: {}", base_path.display());
let base_path: PathBuf = Self::resolve(base_path);
let wrkdir: PathBuf = self.wrkdir.clone();
match self.is_valid_file_name(&base_path.to_str().unwrap()) {
Ok(r) => {}
Err(err) => {
error!("{} : {}",err, base_path.display());
return Err(FileTransferError::new(
FileTransferErrorType::InvalidFilename,
));
}
}
// \0 ASCII NUL.
// \\ A literal backslash (`\')
// -mindepth levels | -maxdepth 2
//-xdev Don't descend directories on other filesystems.
//%a Hora do último acesso ao arquivo no formato retornado pela função C ctime (3).
//%Ak Hora do último acesso ao arquivo no formato especificado por k , que é `@ 'ou uma diretiva para a função C example: find . -printf '%A@ | %p\n'
//%f Print the basename; the file's name with any leading directories removed (only the last element).
// For /, the result is `/'.
//%P File's name with the name of the starting-point under which it was found removed.
//%t File's last modification time in the format returned by the C ctime(3) function.
//%T@ seconds since Jan. 1, 1970, 00:00 GMT, with fractional part.
//%Ts The number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC). (TZ) (Calculated from mktime(tm).)
//%c File's last status change
//%d File's depth in the directory tree
//%h Dirname; the Leading directories of the file's name (all but the last element). If the file name contains no slashes (since it is in the current directory) the %h specifier expands to `.'. For files which are themselves directories and contain a slash (including /), %h expands to the empty string.
//%H Starting-point under which file was found.
//%Ck File's last status change time in the format specified by k, which is the same as for %A.
//%G File's numeric group ID.
//%g File's group name,
//%m File's permission bits (in octal).
//%U File's numeric user ID.
//%u File's user name
//%y File's type (like in ls -l), U=unknown type (shouldn't happen)
//%l Object of symbolic link (empty string if file is not a symbolic link).
//A opção -depth, por exemplo, faz com que o find percorra o sistema de arquivos em uma ordem de profundidade primeiro.
// find "$(cd /home/sali; pwd)" -printf '%M|%u|%g|%s|%Ts|%p|%f\n'
// com o caractere nulo \0 Isso permite que os nomes de arquivo que contêm novas linhas
// ou outros tipos de espaço em branco sejam interpretados corretamente por programas que processam a saída
// A configuração da variável de ambiente LC_CTYPE é usada para determinar quais caracteres precisam ser citados.
match self.perform_shell_cmd_with_path(
wrkdir.as_path(),
format!("unset LANG; find \"$(cd '{}'; pwd)\" -printf '%M|%U|%G|%s|%Ts|%p|%f|%l\n'", base_path.display()).as_str(),
) {
Ok(output) => {
// Split output by (\r)\n
let lines: Vec<&str> = output.as_str().lines().collect();
let mut entries: Vec<FsEntry> = Vec::with_capacity(lines.len());
let mut index = 0;
for line in lines.iter() {
// First line must always be ignored
// Parse row, if ok push to entries
if index > 0 {
let parsed = self.parse_find_output(&base_path, line);
//if let Ok(mut entry) = parse
if let Ok( entry) = parsed {
entries.push(entry)
}
}
index += 1;
}
// info!( "Found {} out of {} valid file entries", entries.len(), lines.len() );
Ok(entries)
}
Err(err) => Err(FileTransferError::new_ex(
FileTransferErrorType::ProtocolError,
err.to_string(),
)),
}
}
false => Err(FileTransferError::new(
FileTransferErrorType::UninitializedSession,
)),
}
}
fn parse_find_output(&mut self, path: &Path, line: &str) -> Result<FsEntry, ()> {
//debug!("parse_find_outputline: '{}'", line);
let columns: Vec<&str> = line.split('|').collect();
//0 1 2 3 4 5 6 7
//permission | user | group | size | last modification time | path | name | Object of symbolic link
//-rwxrwxrwx|www-data|www-data|890524|1521033382|/var/www/html/Alex/CIENTE.mdj|CIENTE.mdj
/*if columns.len() < 7 {
return Err(());
}*/
if columns.len() != 8 {
error!("Parse Error, possibly there is a file with an invalid name line: {:?}",line);
return Err(());
}
//debug!("Parsing find line metadata: {:?}", columns);
// Get if is directory and if is symlink
let type_info = columns.get(0).unwrap().get(0..1).unwrap();
//debug!("Parsing type_info: {:?}", type_info);
let (mut is_dir, is_symlink): (bool, bool) = match type_info
{
"-" => (false, false),
"l" => (false, true),
"d" => (true, false),
_ => return Err(()), // Ignore special files
};
let permition_info = &columns.get(0).unwrap()[1..];
// Check string length (unix pex)
if permition_info.len() < 9 {
return Err(());
}
// this: rw-r--r--" to: (UnixPex { read: true, write: true, execute: false }, UnixPex { read: true, write: false, execute: false }, UnixPex { read: true, write: false, execute: false })
// Get unix pex
let unix_pex = parse_str_linux_permission(&permition_info);
//debug!("Parsing permition_info: {:?} parsed: {:?}", permition_info, unix_pex);
// Parse mtime and convert to SystemTime
let mtime_info = columns.get(4).unwrap();
//debug!("Parsing mtime_info: {:?}", mtime_info);
let mtime: SystemTime = match str_unix_timestamp_to_system_time(mtime_info) {
Ok(t) => t,
Err(_) => { return Err(()); }
};
// Get uid
let uid_info = columns.get(1).unwrap();
let uid: Option<u32> = match uid_info.parse::<u32>() {
Ok(uid) => Some(uid),
Err(_) => None,
};
// Get gid
let gid_info = columns.get(2).unwrap();
let gid: Option<u32> = match gid_info.parse::<u32>() {
Ok(gid) => Some(gid),
Err(_) => None,
};
// Get filesize
let filesize_info = columns.get(3).unwrap();
let filesize: usize = filesize_info
.parse::<usize>()
.unwrap_or(0);
// Get link and name
let file_name_info = columns.get(6).unwrap();
let symbolic_link_info = columns.get(7).unwrap();
let (file_name, symlink_path): (String, Option<PathBuf>) = match is_symlink {
true => (file_name_info.to_string(), Some(PathBuf::from(symbolic_link_info))),
false => (file_name_info.to_string(), None),
};
if file_name.as_str() == "." || file_name.as_str() == ".." {
//debug!("File name is {}; ignoring entry", file_name);
return Err(());
}
// get absolute path
let abs_path_info = columns.get(5).unwrap();
let abs_path: PathBuf = PathBuf::from(abs_path_info);
//debug!("Parsing abs_path: {:?}", abs_path);
//Obtenha o link simbólico; PATH não deve ser igual a nome do arquivo
// Get symlink; PATH mustn't be equal to filename
let symlink: Option<Box<FsEntry>> = match symlink_path {
None => None,
Some(p) => match p.file_name().unwrap_or_else(|| std::ffi::OsStr::new(""))
== file_name.as_str()
{
// If name is equal, don't stat path; otherwise it would get stuck
// Se o nome for igual, não stat path; caso contrário, ficaria preso
true => None,
false => match self.stat2(p.as_path()) {
// If path match filename
Ok(e) => {
// If e is a directory, set is_dir to true
if e.is_dir() {
is_dir = true;
}
Some(Box::new(e))
}
Err(_) => None, // Ignore errors
}
},
};
//debug!("Parsing symlink: {:?}", symlink);
// Get extension
let extension: Option<String> = abs_path
.as_path()
.extension()
.map(|s| String::from(s.to_string_lossy()));
// debug!("Parsing extension: {:?}", extension);
// Push to entries
Ok(match is_dir {
true => FsEntry::Directory(FsDirectory {
name: file_name,
abs_path,
last_change_time: mtime,
last_access_time: mtime,
creation_time: mtime,
symlink,
user: uid,
group: gid,
unix_pex: Some(unix_pex),
}),
false => FsEntry::File(FsFile {
name: file_name,
abs_path,
last_change_time: mtime,
last_access_time: mtime,
creation_time: mtime,
size: filesize,
ftype: extension,
symlink,
user: uid,
group: gid,
unix_pex: Some(unix_pex),
}),
})
}
I'm also noticing very long copy time (6 hours to copy 10GB, 300000 files), I don't know if this is normal as I'm still doing tests to compare with my implementation done in dart, but I believe there should be some way to improve one bit of that, one thing I did in dart was calculate the total size very quickly and simply as I show below and also increase the copy buffer size but I'm not sure how to do this in rust with libssh2 because in dart I used libssh which is quite different from the ssh2 lib.
///return total size in bytes of each file inside folder ignoring linux directory metadata size
int getSizeOfDirectory(Pointer<ssh_session_struct> session, String remoteDirectoryPath,
{bool isThrowException = true}) {
//windows = 1041090242
//debian < 6 : find /var/www -type f -print0 | xargs -0 stat --format=%s | paste -sd+ - | bc -l
//ls -lAR | grep -v '^d' | awk '{total += $5} END {print "Total:", total}'
//linux = 1041090469 -> find ./dart/ -type f -print | xargs -d '\n' du -bs | awk '{ sum+=$1} END {print sum}'
var cmdToGetTotaSize =
"find $remoteDirectoryPath -type f -print0 | xargs -0 stat --format=%s | awk '{s+=\$1} END {print s}'";
String? cmdRes;
try {
cmdRes = execCommandSync(session, cmdToGetTotaSize);
if (cmdRes.trim().isEmpty) {
//verifica se é um link sinbolico file /var/run
//var/run: symbolic link to /run
cmdRes = execCommandSync(session, 'file $remoteDirectoryPath');
//é um link sinbolico ?
if (cmdRes.contains('symbolic')) {
//obtem o caminho real do link sinbolico
var cmdRe = execCommandSync(session, 'readlink -f $remoteDirectoryPath');
if (cmdRe.trim().isNotEmpty) {
cmdRes = execCommandSync(session,
"find ${cmdRe.replaceAll(RegExp(r'\n'), '')} -type f -print0 | xargs -0 stat --format=%s | awk '{s+=\$1} END {print s}'");
}
}
}
//para debian antigos como debian 6
//exeplo 1.57903e+10
if (cmdRes.contains('e+')) {
cmdRes = execCommandSync(
session, 'find $remoteDirectoryPath -type f -print0 | xargs -0 stat --format=%s | paste -sd+ - | bc -l');
} else if (int.tryParse(cmdRes) == null) {
//du -s -B1 /var/www
//4096 * 23775 = 97382400
//obter total de diretorios => find /var/www -type d -print| wc -l
// find /var/www -type f -print0 | du -scb -L --apparent-size --files0-from=- | tail -n 1
//cmdRes = execCommandSync(session, 'du -sb --apparent-size --block-size=1 $remoteDirectoryPath');
cmdRes = execCommandSync(session,
'find $remoteDirectoryPath -type f -print0 | du -scb -L --apparent-size --files0-from=- | tail -n 1');
return int.parse(cmdRes.split(' ').first.trim());
}
return int.parse(cmdRes);
} catch (e) {
print('getSizeOfDirectory: $e \r\n cmdToGetTotaSize: $cmdToGetTotaSize \r\n cmdRes: $cmdRes');
if (isThrowException) {
throw LibsshGetFileSizeException(
'Unable to get the size of a directory in bytes, \r\n cmd: $cmdToGetTotaSize cmdResult: $cmdRes');
}
}
return 0;
}
The recursive problem is caused by the fact that you probably have something like this:
❯ /bin/ls -l
total 0
lrwxr-xr-x 1 christianvisintin wheel 3 Dec 3 16:56 $sdk -> ../
which is causing the symlink resolver to recurse.
This is going to be solved with the deprecation of the current file transfer module in favour of the newest remotefs-rs library, which won't stat symlink but will just return the path pointed by them.
So not an issue for me.
Regarding this:
I'm also noticing very long copy time (6 hours to copy 10GB, 300000 files), I don't know if this is normal as I'm still doing tests to compare with my implementation done in dart, but I believe there should be some way to improve one bit of that, one thing I did in dart was calculate the total size very quickly and simply as I show below and also increase the copy buffer size but I'm not sure how to do this in rust with libssh2 because in dart I used libssh which is quite different from the ssh2 lib.
Don't know, but as far as I said before, termscp is not a library and the methods perform many operations other than writing data to the ssh session, so it's pretty much obvious. I can't even use your algo to calculate directory size since termscp must handle Aws s3 and FTP. Also, the buffer size in libssh2 is static and is set to 65536, so a change would not improve performance.
Regex fix merged into 0.8.0; fixed in remotefs.
Description
I'm trying to use your lib/code to transfer a folder from the remote server to my local machine, and it's failing because there's a bug with the regular expression you use to parse the output of the "ls -la" command. the regex cannot handle this output "-rwxrwxrwx 1 www-data www-data 890524 Mar 14 2018 CIENTE.mdj"
Steps to reproduce
Expected behaviour
the expected behavior should be copying the directory without any errors
Environment
Log
Additional information