mirror of
https://github.com/wez/wezterm.git
synced 2024-11-27 02:25:28 +03:00
ssh: introduce SftpWrap
This commit is contained in:
parent
a6022f5c65
commit
9d44cc1720
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -2413,7 +2413,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libssh-rs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/wez/libssh-rs.git?rev=6894d2f3871344b08652f7fc09761a65d6c992d6#6894d2f3871344b08652f7fc09761a65d6c992d6"
|
||||
source = "git+https://github.com/wez/libssh-rs.git?rev=02256a841e3859e8fc4c3c92e849245ac31e17f0#02256a841e3859e8fc4c3c92e849245ac31e17f0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libssh-rs-sys",
|
||||
@ -2423,7 +2423,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libssh-rs-sys"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/wez/libssh-rs.git?rev=6894d2f3871344b08652f7fc09761a65d6c992d6#6894d2f3871344b08652f7fc09761a65d6c992d6"
|
||||
source = "git+https://github.com/wez/libssh-rs.git?rev=02256a841e3859e8fc4c3c92e849245ac31e17f0#02256a841e3859e8fc4c3c92e849245ac31e17f0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libz-sys",
|
||||
|
@ -26,7 +26,7 @@ portable-pty = { version="0.5", path = "../pty" }
|
||||
regex = "1"
|
||||
smol = "1.2"
|
||||
ssh2 = {version="0.9.3", features=["openssl-on-win32"]}
|
||||
libssh-rs = {git="https://github.com/wez/libssh-rs.git", rev="6894d2f3871344b08652f7fc09761a65d6c992d6", features=["vendored", "vendored-openssl"]}
|
||||
libssh-rs = {git="https://github.com/wez/libssh-rs.git", rev="02256a841e3859e8fc4c3c92e849245ac31e17f0", features=["vendored", "vendored-openssl"]}
|
||||
#libssh-rs = {path="../../libssh-rs/libssh-rs", features=["vendored", "vendored-openssl"]}
|
||||
thiserror = "1.0"
|
||||
|
||||
|
@ -8,6 +8,7 @@ mod session;
|
||||
mod sessioninner;
|
||||
mod sessionwrap;
|
||||
mod sftp;
|
||||
mod sftpwrap;
|
||||
|
||||
pub use auth::*;
|
||||
pub use config::*;
|
||||
|
@ -5,10 +5,10 @@ use crate::pty::*;
|
||||
use crate::session::{Exec, ExecResult, SessionEvent, SessionRequest, SignalChannel};
|
||||
use crate::sessionwrap::SessionWrap;
|
||||
use crate::sftp::{
|
||||
self, File, FileId, FileRequest, Metadata, SftpChannelError, SftpChannelResult, SftpRequest,
|
||||
self, File, FileId, FileRequest, SftpChannelError, SftpChannelResult, SftpRequest,
|
||||
};
|
||||
use crate::sftpwrap::SftpWrap;
|
||||
use anyhow::{anyhow, Context};
|
||||
use camino::Utf8PathBuf;
|
||||
use filedescriptor::{
|
||||
poll, pollfd, socketpair, AsRawSocketDescriptor, FileDescriptor, POLLIN, POLLOUT,
|
||||
};
|
||||
@ -16,7 +16,6 @@ use libssh_rs as libssh;
|
||||
use portable_pty::ExitStatus;
|
||||
use smol::channel::{bounded, Receiver, Sender, TryRecvError};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::convert::TryFrom;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::TcpStream;
|
||||
use std::time::Duration;
|
||||
@ -420,18 +419,6 @@ impl SessionInner {
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
SessionRequest::Sftp(SftpRequest::Open(msg)) => {
|
||||
if let Err(err) = self.open(sess, &msg) {
|
||||
log::error!("{:?} -> error: {:#}", msg, err);
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
SessionRequest::Sftp(SftpRequest::Create(msg)) => {
|
||||
if let Err(err) = self.create(sess, &msg) {
|
||||
log::error!("{:?} -> error: {:#}", msg, err);
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
SessionRequest::Sftp(SftpRequest::OpenDir(msg)) => {
|
||||
if let Err(err) = self.open_dir(sess, &msg) {
|
||||
log::error!("{:?} -> error: {:#}", msg, err);
|
||||
@ -649,58 +636,15 @@ impl SessionInner {
|
||||
sess: &mut SessionWrap,
|
||||
msg: &sftp::OpenWithMode,
|
||||
) -> anyhow::Result<()> {
|
||||
let flags: ssh2::OpenFlags = msg.opts.into();
|
||||
let mode = msg.opts.mode;
|
||||
let open_type: ssh2::OpenType = msg.opts.ty.into();
|
||||
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.open_mode(msg.filename.as_std_path(), flags, mode, open_type)
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
let result = self
|
||||
.init_sftp(sess)
|
||||
.and_then(|sftp| sftp.open(&msg.filename, msg.opts));
|
||||
|
||||
match result {
|
||||
Ok(ssh_file) => {
|
||||
let (file_id, file) = self.make_file();
|
||||
msg.reply.try_send(Ok(file))?;
|
||||
self.files.insert(file_id, FileWrap::Ssh2(ssh_file));
|
||||
}
|
||||
Err(x) => msg.reply.try_send(Err(x))?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper to open a file in the `Read` mode.
|
||||
pub fn open(&mut self, sess: &mut SessionWrap, msg: &sftp::Open) -> anyhow::Result<()> {
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.open(msg.filename.as_std_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(ssh_file) => {
|
||||
let (file_id, file) = self.make_file();
|
||||
msg.reply.try_send(Ok(file))?;
|
||||
self.files.insert(file_id, FileWrap::Ssh2(ssh_file));
|
||||
}
|
||||
Err(x) => msg.reply.try_send(Err(x))?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper to create a file in write-only mode with truncation.
|
||||
pub fn create(&mut self, sess: &mut SessionWrap, msg: &sftp::Create) -> anyhow::Result<()> {
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.create(msg.filename.as_std_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(ssh_file) => {
|
||||
let (file_id, file) = self.make_file();
|
||||
msg.reply.try_send(Ok(file))?;
|
||||
self.files.insert(file_id, FileWrap::Ssh2(ssh_file));
|
||||
self.files.insert(file_id, ssh_file);
|
||||
}
|
||||
Err(x) => msg.reply.try_send(Err(x))?,
|
||||
}
|
||||
@ -710,16 +654,15 @@ impl SessionInner {
|
||||
|
||||
/// Helper to open a directory for reading its contents.
|
||||
pub fn open_dir(&mut self, sess: &mut SessionWrap, msg: &sftp::OpenDir) -> anyhow::Result<()> {
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.opendir(msg.filename.as_std_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
let result = self
|
||||
.init_sftp(sess)
|
||||
.and_then(|sftp| sftp.open_dir(&msg.filename));
|
||||
|
||||
match result {
|
||||
Ok(ssh_file) => {
|
||||
let (file_id, file) = self.make_file();
|
||||
msg.reply.try_send(Ok(file))?;
|
||||
self.files.insert(file_id, FileWrap::Ssh2(ssh_file));
|
||||
self.files.insert(file_id, ssh_file);
|
||||
}
|
||||
Err(x) => msg.reply.try_send(Err(x))?,
|
||||
}
|
||||
@ -859,26 +802,9 @@ impl SessionInner {
|
||||
|
||||
/// Convenience function to read the files in a directory.
|
||||
pub fn read_dir(&mut self, sess: &mut SessionWrap, msg: &sftp::ReadDir) -> anyhow::Result<()> {
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.readdir(msg.filename.as_std_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
.and_then(|entries| {
|
||||
let mut mapped_entries = Vec::new();
|
||||
for (path, stat) in entries {
|
||||
match Utf8PathBuf::try_from(path) {
|
||||
Ok(path) => mapped_entries.push((path, Metadata::from(stat))),
|
||||
Err(x) => {
|
||||
return Err(SftpChannelError::from(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
x,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(mapped_entries)
|
||||
})
|
||||
});
|
||||
let result = self
|
||||
.init_sftp(sess)
|
||||
.and_then(|sftp| sftp.read_dir(&msg.filename));
|
||||
msg.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
@ -890,10 +816,9 @@ impl SessionInner {
|
||||
sess: &mut SessionWrap,
|
||||
msg: &sftp::CreateDir,
|
||||
) -> anyhow::Result<()> {
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.mkdir(msg.filename.as_std_path(), msg.mode)
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
let result = self
|
||||
.init_sftp(sess)
|
||||
.and_then(|sftp| sftp.create_dir(&msg.filename, msg.mode));
|
||||
msg.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
@ -905,10 +830,9 @@ impl SessionInner {
|
||||
sess: &mut SessionWrap,
|
||||
msg: &sftp::RemoveDir,
|
||||
) -> anyhow::Result<()> {
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.rmdir(msg.filename.as_std_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
let result = self
|
||||
.init_sftp(sess)
|
||||
.and_then(|sftp| sftp.remove_dir(&msg.filename));
|
||||
msg.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
@ -920,11 +844,9 @@ impl SessionInner {
|
||||
sess: &mut SessionWrap,
|
||||
msg: &sftp::GetMetadata,
|
||||
) -> anyhow::Result<()> {
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.stat(msg.filename.as_std_path())
|
||||
.map(Metadata::from)
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
let result = self
|
||||
.init_sftp(sess)
|
||||
.and_then(|sftp| sftp.metadata(&msg.filename));
|
||||
msg.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
@ -936,11 +858,9 @@ impl SessionInner {
|
||||
sess: &mut SessionWrap,
|
||||
msg: &sftp::SymlinkMetadata,
|
||||
) -> anyhow::Result<()> {
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.lstat(msg.filename.as_std_path())
|
||||
.map(Metadata::from)
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
let result = self
|
||||
.init_sftp(sess)
|
||||
.and_then(|sftp| sftp.symlink_metadata(&msg.filename));
|
||||
msg.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
@ -952,10 +872,9 @@ impl SessionInner {
|
||||
sess: &mut SessionWrap,
|
||||
msg: &sftp::SetMetadata,
|
||||
) -> anyhow::Result<()> {
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.setstat(msg.filename.as_std_path(), msg.metadata.into())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
let result = self
|
||||
.init_sftp(sess)
|
||||
.and_then(|sftp| sftp.set_metadata(&msg.filename, msg.metadata));
|
||||
msg.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
@ -963,10 +882,9 @@ impl SessionInner {
|
||||
|
||||
/// Create symlink at `target` pointing at `path`.
|
||||
pub fn symlink(&mut self, sess: &mut SessionWrap, msg: &sftp::Symlink) -> anyhow::Result<()> {
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.symlink(msg.path.as_std_path(), msg.target.as_std_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
let result = self
|
||||
.init_sftp(sess)
|
||||
.and_then(|sftp| sftp.symlink(&msg.path, &msg.target));
|
||||
msg.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
@ -978,18 +896,9 @@ impl SessionInner {
|
||||
sess: &mut SessionWrap,
|
||||
msg: &sftp::ReadLink,
|
||||
) -> anyhow::Result<()> {
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.readlink(msg.path.as_std_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
.and_then(|path| {
|
||||
Utf8PathBuf::try_from(path).map_err(|x| {
|
||||
SftpChannelError::from(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
x,
|
||||
))
|
||||
})
|
||||
})
|
||||
});
|
||||
let result = self
|
||||
.init_sftp(sess)
|
||||
.and_then(|sftp| sftp.read_link(&msg.path));
|
||||
msg.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
@ -1001,18 +910,9 @@ impl SessionInner {
|
||||
sess: &mut SessionWrap,
|
||||
msg: &sftp::Canonicalize,
|
||||
) -> anyhow::Result<()> {
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.realpath(msg.path.as_std_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
.and_then(|path| {
|
||||
Utf8PathBuf::try_from(path).map_err(|x| {
|
||||
SftpChannelError::from(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
x,
|
||||
))
|
||||
})
|
||||
})
|
||||
});
|
||||
let result = self
|
||||
.init_sftp(sess)
|
||||
.and_then(|sftp| sftp.canonicalize(&msg.path));
|
||||
msg.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
@ -1021,12 +921,8 @@ impl SessionInner {
|
||||
/// Rename the filesystem object on the remote filesystem.
|
||||
pub fn rename(&mut self, sess: &mut SessionWrap, msg: &sftp::Rename) -> anyhow::Result<()> {
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.rename(
|
||||
msg.src.as_std_path(),
|
||||
msg.dst.as_std_path(),
|
||||
Some(msg.opts.into()),
|
||||
)
|
||||
.map_err(SftpChannelError::from)
|
||||
sftp.rename(&msg.src, &msg.dst, msg.opts)
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
msg.reply.try_send(result)?;
|
||||
|
||||
@ -1039,24 +935,18 @@ impl SessionInner {
|
||||
sess: &mut SessionWrap,
|
||||
msg: &sftp::RemoveFile,
|
||||
) -> anyhow::Result<()> {
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.unlink(msg.file.as_std_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
let result = self.init_sftp(sess).and_then(|sftp| sftp.unlink(&msg.file));
|
||||
msg.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialize the sftp channel if not already created, returning a mutable reference to it
|
||||
fn init_sftp<'a>(
|
||||
&mut self,
|
||||
sess: &'a mut SessionWrap,
|
||||
) -> SftpChannelResult<&'a mut ssh2::Sftp> {
|
||||
fn init_sftp<'a>(&mut self, sess: &'a mut SessionWrap) -> SftpChannelResult<&'a mut SftpWrap> {
|
||||
match sess {
|
||||
SessionWrap::Ssh2(sess) => {
|
||||
if sess.sftp.is_none() {
|
||||
sess.sftp = Some(sess.sess.sftp()?);
|
||||
sess.sftp = Some(SftpWrap::Ssh2(sess.sess.sftp()?));
|
||||
}
|
||||
Ok(sess.sftp.as_mut().expect("sftp should have been set above"))
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
use crate::channelwrap::ChannelWrap;
|
||||
use crate::sftpwrap::SftpWrap;
|
||||
use filedescriptor::{AsRawSocketDescriptor, SocketDescriptor, POLLIN, POLLOUT};
|
||||
use libssh_rs as libssh;
|
||||
use ssh2::BlockDirections;
|
||||
|
||||
pub(crate) struct Ssh2Session {
|
||||
pub sess: ssh2::Session,
|
||||
pub sftp: Option<ssh2::Sftp>,
|
||||
pub sftp: Option<SftpWrap>,
|
||||
}
|
||||
|
||||
pub(crate) enum SessionWrap {
|
||||
|
@ -91,16 +91,16 @@ impl Sftp {
|
||||
T: TryInto<Utf8PathBuf, Error = E>,
|
||||
E: Into<Box<dyn std::error::Error + Send + Sync>>,
|
||||
{
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Open(Open {
|
||||
filename: filename.try_into().map_err(into_invalid_data)?,
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let mut result = rx.recv().await??;
|
||||
result.initialize_sender(self.tx.clone());
|
||||
Ok(result)
|
||||
self.open_with_mode(
|
||||
filename,
|
||||
OpenOptions {
|
||||
read: true,
|
||||
write: None,
|
||||
mode: 0,
|
||||
ty: OpenFileType::File,
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Helper to create a file in write-only mode with truncation.
|
||||
@ -109,16 +109,16 @@ impl Sftp {
|
||||
T: TryInto<Utf8PathBuf, Error = E>,
|
||||
E: Into<Box<dyn std::error::Error + Send + Sync>>,
|
||||
{
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Create(Create {
|
||||
filename: filename.try_into().map_err(into_invalid_data)?,
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let mut result = rx.recv().await??;
|
||||
result.initialize_sender(self.tx.clone());
|
||||
Ok(result)
|
||||
self.open_with_mode(
|
||||
filename,
|
||||
OpenOptions {
|
||||
read: false,
|
||||
write: Some(WriteMode::Write),
|
||||
mode: 0o666,
|
||||
ty: OpenFileType::File,
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Helper to open a directory for reading its contents.
|
||||
@ -356,8 +356,6 @@ impl Sftp {
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum SftpRequest {
|
||||
OpenWithMode(OpenWithMode),
|
||||
Open(Open),
|
||||
Create(Create),
|
||||
OpenDir(OpenDir),
|
||||
ReadDir(ReadDir),
|
||||
CreateDir(CreateDir),
|
||||
|
162
wezterm-ssh/src/sftpwrap.rs
Normal file
162
wezterm-ssh/src/sftpwrap.rs
Normal file
@ -0,0 +1,162 @@
|
||||
use crate::filewrap::FileWrap;
|
||||
use crate::sftp::{Metadata, OpenOptions, RenameOptions, SftpChannelError, SftpChannelResult};
|
||||
use camino::{Utf8Path, Utf8PathBuf};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub(crate) enum SftpWrap {
|
||||
Ssh2(ssh2::Sftp),
|
||||
}
|
||||
|
||||
impl SftpWrap {
|
||||
pub fn open(&self, filename: &Utf8Path, opts: OpenOptions) -> SftpChannelResult<FileWrap> {
|
||||
match self {
|
||||
Self::Ssh2(sftp) => {
|
||||
let flags: ssh2::OpenFlags = opts.into();
|
||||
let mode = opts.mode;
|
||||
let open_type: ssh2::OpenType = opts.ty.into();
|
||||
|
||||
let file = sftp
|
||||
.open_mode(filename.as_std_path(), flags, mode, open_type)
|
||||
.map_err(SftpChannelError::from)?;
|
||||
Ok(FileWrap::Ssh2(file))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symlink(&self, path: &Utf8Path, target: &Utf8Path) -> SftpChannelResult<()> {
|
||||
match self {
|
||||
Self::Ssh2(sftp) => sftp
|
||||
.symlink(path.as_std_path(), target.as_std_path())
|
||||
.map_err(SftpChannelError::from),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_link(&self, filename: &Utf8Path) -> SftpChannelResult<Utf8PathBuf> {
|
||||
match self {
|
||||
Self::Ssh2(sftp) => sftp
|
||||
.readlink(filename.as_std_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
.and_then(|path| {
|
||||
Utf8PathBuf::try_from(path).map_err(|x| {
|
||||
SftpChannelError::from(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
x,
|
||||
))
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn canonicalize(&self, filename: &Utf8Path) -> SftpChannelResult<Utf8PathBuf> {
|
||||
match self {
|
||||
Self::Ssh2(sftp) => sftp
|
||||
.realpath(filename.as_std_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
.and_then(|path| {
|
||||
Utf8PathBuf::try_from(path).map_err(|x| {
|
||||
SftpChannelError::from(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
x,
|
||||
))
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unlink(&self, filename: &Utf8Path) -> SftpChannelResult<()> {
|
||||
match self {
|
||||
Self::Ssh2(sftp) => sftp
|
||||
.unlink(filename.as_std_path())
|
||||
.map_err(SftpChannelError::from),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_dir(&self, filename: &Utf8Path) -> SftpChannelResult<()> {
|
||||
match self {
|
||||
Self::Ssh2(sftp) => sftp
|
||||
.rmdir(filename.as_std_path())
|
||||
.map_err(SftpChannelError::from),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_dir(&self, filename: &Utf8Path, mode: i32) -> SftpChannelResult<()> {
|
||||
match self {
|
||||
Self::Ssh2(sftp) => sftp
|
||||
.mkdir(filename.as_std_path(), mode)
|
||||
.map_err(SftpChannelError::from),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rename(
|
||||
&self,
|
||||
src: &Utf8Path,
|
||||
dest: &Utf8Path,
|
||||
opts: RenameOptions,
|
||||
) -> SftpChannelResult<()> {
|
||||
match self {
|
||||
Self::Ssh2(sftp) => sftp
|
||||
.rename(src.as_std_path(), dest.as_std_path(), Some(opts.into()))
|
||||
.map_err(SftpChannelError::from),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symlink_metadata(&self, filename: &Utf8Path) -> SftpChannelResult<Metadata> {
|
||||
match self {
|
||||
Self::Ssh2(sftp) => sftp
|
||||
.lstat(filename.as_std_path())
|
||||
.map(Metadata::from)
|
||||
.map_err(SftpChannelError::from),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn metadata(&self, filename: &Utf8Path) -> SftpChannelResult<Metadata> {
|
||||
match self {
|
||||
Self::Ssh2(sftp) => sftp
|
||||
.stat(filename.as_std_path())
|
||||
.map(Metadata::from)
|
||||
.map_err(SftpChannelError::from),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_metadata(&self, filename: &Utf8Path, metadata: Metadata) -> SftpChannelResult<()> {
|
||||
match self {
|
||||
Self::Ssh2(sftp) => sftp
|
||||
.setstat(filename.as_std_path(), metadata.into())
|
||||
.map_err(SftpChannelError::from),
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: need a DirWrap
|
||||
pub fn open_dir(&self, filename: &Utf8Path) -> SftpChannelResult<FileWrap> {
|
||||
match self {
|
||||
Self::Ssh2(sftp) => sftp
|
||||
.opendir(filename.as_std_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
.map(FileWrap::Ssh2),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_dir(&self, filename: &Utf8Path) -> SftpChannelResult<Vec<(Utf8PathBuf, Metadata)>> {
|
||||
match self {
|
||||
Self::Ssh2(sftp) => sftp
|
||||
.readdir(filename.as_std_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
.and_then(|entries| {
|
||||
let mut mapped_entries = Vec::new();
|
||||
for (path, stat) in entries {
|
||||
match Utf8PathBuf::try_from(path) {
|
||||
Ok(path) => mapped_entries.push((path, Metadata::from(stat))),
|
||||
Err(x) => {
|
||||
return Err(SftpChannelError::from(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
x,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(mapped_entries)
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user