mirror of
https://github.com/wez/wezterm.git
synced 2024-11-27 02:25:28 +03:00
Create SftpError type and associated structs to provide more verbose errors for sftp
This commit is contained in:
parent
4a0376e6de
commit
072bb1c470
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -5381,6 +5381,7 @@ dependencies = [
|
||||
"ssh2",
|
||||
"structopt",
|
||||
"termwiz",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -24,18 +24,19 @@ portable-pty = { version="0.5", path = "../pty" }
|
||||
regex = "1"
|
||||
smol = "1.2"
|
||||
ssh2 = {version="0.9.3", features=["openssl-on-win32"]}
|
||||
thiserror = "1.0"
|
||||
|
||||
# Not used directly, but is used to centralize the openssl vendor feature selection
|
||||
async_ossl = { path = "../async_ossl" }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_fs = "1.0.4"
|
||||
indoc = "1.0.3"
|
||||
indoc = "1.0"
|
||||
k9 = "0.11.0"
|
||||
once_cell = "1.8.0"
|
||||
predicates = "2.0.2"
|
||||
once_cell = "1.8"
|
||||
predicates = "2.0"
|
||||
pretty_env_logger = "0.4"
|
||||
rstest = "0.11.0"
|
||||
rstest = "0.11"
|
||||
shell-words = "1.0"
|
||||
smol-potat = "1.1.2"
|
||||
structopt = "0.3"
|
||||
|
@ -18,7 +18,7 @@ use std::time::Duration;
|
||||
mod sftp;
|
||||
pub use sftp::{
|
||||
File, FilePermissions, FileType, Metadata, OpenFileType, OpenOptions, RenameOptions, Sftp,
|
||||
WriteMode,
|
||||
SftpChannelError, SftpChannelResult, SftpError, SftpResult, WriteMode,
|
||||
};
|
||||
use sftp::{FileId, FileRequest, SftpRequest};
|
||||
|
||||
@ -628,16 +628,19 @@ impl SessionInner {
|
||||
let mode = open_mode.opts.mode;
|
||||
let open_type: ssh2::OpenType = open_mode.opts.ty.into();
|
||||
|
||||
let ssh_file = self.init_sftp(sess)?.open_mode(
|
||||
open_mode.filename.as_path(),
|
||||
flags,
|
||||
mode,
|
||||
open_type,
|
||||
)?;
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.open_mode(open_mode.filename.as_path(), flags, mode, open_type)
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
|
||||
let (file_id, file) = self.make_file();
|
||||
open_mode.reply.try_send(file)?;
|
||||
self.files.insert(file_id, ssh_file);
|
||||
match result {
|
||||
Ok(ssh_file) => {
|
||||
let (file_id, file) = self.make_file();
|
||||
open_mode.reply.try_send(Ok(file))?;
|
||||
self.files.insert(file_id, ssh_file);
|
||||
}
|
||||
Err(x) => open_mode.reply.try_send(Err(x))?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -646,11 +649,19 @@ impl SessionInner {
|
||||
///
|
||||
/// See [`Sftp::open`] for more information.
|
||||
pub fn open(&mut self, sess: &ssh2::Session, open: &sftp::Open) -> anyhow::Result<()> {
|
||||
let ssh_file = self.init_sftp(sess)?.open(open.filename.as_path())?;
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.open(open.filename.as_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
|
||||
let (file_id, file) = self.make_file();
|
||||
open.reply.try_send(file)?;
|
||||
self.files.insert(file_id, ssh_file);
|
||||
match result {
|
||||
Ok(ssh_file) => {
|
||||
let (file_id, file) = self.make_file();
|
||||
open.reply.try_send(Ok(file))?;
|
||||
self.files.insert(file_id, ssh_file);
|
||||
}
|
||||
Err(x) => open.reply.try_send(Err(x))?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -659,11 +670,19 @@ impl SessionInner {
|
||||
///
|
||||
/// See [`Sftp::create`] for more information.
|
||||
pub fn create(&mut self, sess: &ssh2::Session, create: &sftp::Create) -> anyhow::Result<()> {
|
||||
let ssh_file = self.init_sftp(sess)?.create(create.filename.as_path())?;
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.create(create.filename.as_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
|
||||
let (file_id, file) = self.make_file();
|
||||
create.reply.try_send(file)?;
|
||||
self.files.insert(file_id, ssh_file);
|
||||
match result {
|
||||
Ok(ssh_file) => {
|
||||
let (file_id, file) = self.make_file();
|
||||
create.reply.try_send(Ok(file))?;
|
||||
self.files.insert(file_id, ssh_file);
|
||||
}
|
||||
Err(x) => create.reply.try_send(Err(x))?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -672,11 +691,19 @@ impl SessionInner {
|
||||
///
|
||||
/// See [`Sftp::opendir`] for more information.
|
||||
pub fn opendir(&mut self, sess: &ssh2::Session, opendir: &sftp::Opendir) -> anyhow::Result<()> {
|
||||
let ssh_file = self.init_sftp(sess)?.opendir(opendir.filename.as_path())?;
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.opendir(opendir.filename.as_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
|
||||
let (file_id, file) = self.make_file();
|
||||
opendir.reply.try_send(file)?;
|
||||
self.files.insert(file_id, ssh_file);
|
||||
match result {
|
||||
Ok(ssh_file) => {
|
||||
let (file_id, file) = self.make_file();
|
||||
opendir.reply.try_send(Ok(file))?;
|
||||
self.files.insert(file_id, ssh_file);
|
||||
}
|
||||
Err(x) => opendir.reply.try_send(Err(x))?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -702,9 +729,9 @@ impl SessionInner {
|
||||
} = write_file;
|
||||
|
||||
if let Some(file) = self.files.get_mut(file_id) {
|
||||
file.write_all(data)?;
|
||||
let result = file.write_all(data).map_err(SftpChannelError::from);
|
||||
reply.try_send(result)?;
|
||||
}
|
||||
reply.try_send(())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -724,11 +751,13 @@ impl SessionInner {
|
||||
if let Some(file) = self.files.get_mut(file_id) {
|
||||
// TODO: Move this somewhere to avoid re-allocating buffer
|
||||
let mut buf = vec![0u8; *max_bytes];
|
||||
let n = file.read(&mut buf)?;
|
||||
buf.truncate(n);
|
||||
reply.try_send(buf)?;
|
||||
} else {
|
||||
reply.try_send(Vec::new())?;
|
||||
match file.read(&mut buf).map_err(SftpChannelError::from) {
|
||||
Ok(n) => {
|
||||
buf.truncate(n);
|
||||
reply.try_send(Ok(buf))?;
|
||||
}
|
||||
Err(x) => reply.try_send(Err(x))?,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -741,7 +770,7 @@ impl SessionInner {
|
||||
close_file: &sftp::CloseFile,
|
||||
) -> anyhow::Result<()> {
|
||||
self.files.remove(&close_file.file_id);
|
||||
close_file.reply.try_send(())?;
|
||||
close_file.reply.try_send(Ok(()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -753,9 +782,9 @@ impl SessionInner {
|
||||
flush_file: &sftp::FlushFile,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(file) = self.files.get_mut(&flush_file.file_id) {
|
||||
file.flush()?;
|
||||
let result = file.flush().map_err(SftpChannelError::from);
|
||||
flush_file.reply.try_send(result)?;
|
||||
}
|
||||
flush_file.reply.try_send(())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -773,9 +802,11 @@ impl SessionInner {
|
||||
} = setstat_file;
|
||||
|
||||
if let Some(file) = self.files.get_mut(file_id) {
|
||||
file.setstat((*metadata).into())?;
|
||||
let result = file
|
||||
.setstat((*metadata).into())
|
||||
.map_err(SftpChannelError::from);
|
||||
reply.try_send(result)?;
|
||||
}
|
||||
reply.try_send(())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -787,8 +818,11 @@ impl SessionInner {
|
||||
stat_file: &sftp::StatFile,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(file) = self.files.get_mut(&stat_file.file_id) {
|
||||
let stat = file.stat()?;
|
||||
stat_file.reply.try_send(Metadata::from(stat))?;
|
||||
let result = file
|
||||
.stat()
|
||||
.map(Metadata::from)
|
||||
.map_err(SftpChannelError::from);
|
||||
stat_file.reply.try_send(result)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -801,8 +835,11 @@ impl SessionInner {
|
||||
readdir_file: &sftp::ReaddirFile,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(file) = self.files.get_mut(&readdir_file.file_id) {
|
||||
let (path, stat) = file.readdir()?;
|
||||
readdir_file.reply.try_send((path, Metadata::from(stat)))?;
|
||||
let result = file
|
||||
.readdir()
|
||||
.map(|(path, stat)| (path, Metadata::from(stat)))
|
||||
.map_err(SftpChannelError::from);
|
||||
readdir_file.reply.try_send(result)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -815,9 +852,9 @@ impl SessionInner {
|
||||
fsync_file: &sftp::FsyncFile,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(file) = self.files.get_mut(&fsync_file.file_id) {
|
||||
file.fsync()?;
|
||||
let result = file.fsync().map_err(SftpChannelError::from);
|
||||
fsync_file.reply.try_send(result)?;
|
||||
}
|
||||
fsync_file.reply.try_send(())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -826,12 +863,16 @@ impl SessionInner {
|
||||
///
|
||||
/// See [`Sftp::readdir`] for more information.
|
||||
pub fn readdir(&mut self, sess: &ssh2::Session, readdir: &sftp::Readdir) -> anyhow::Result<()> {
|
||||
let result = self
|
||||
.init_sftp(sess)?
|
||||
.readdir(readdir.filename.as_path())?
|
||||
.into_iter()
|
||||
.map(|(path, stat)| (path, Metadata::from(stat)))
|
||||
.collect();
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.readdir(readdir.filename.as_path())
|
||||
.map(|entries| {
|
||||
entries
|
||||
.into_iter()
|
||||
.map(|(path, stat)| (path, Metadata::from(stat)))
|
||||
.collect()
|
||||
})
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
readdir.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
@ -841,9 +882,11 @@ impl SessionInner {
|
||||
///
|
||||
/// See [`Sftp::rmdir`] for more information.
|
||||
pub fn mkdir(&mut self, sess: &ssh2::Session, mkdir: &sftp::Mkdir) -> anyhow::Result<()> {
|
||||
self.init_sftp(sess)?
|
||||
.mkdir(mkdir.filename.as_path(), mkdir.mode)?;
|
||||
mkdir.reply.try_send(())?;
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.mkdir(mkdir.filename.as_path(), mkdir.mode)
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
mkdir.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -852,8 +895,11 @@ impl SessionInner {
|
||||
///
|
||||
/// See [`Sftp::rmdir`] for more information.
|
||||
pub fn rmdir(&mut self, sess: &ssh2::Session, rmdir: &sftp::Rmdir) -> anyhow::Result<()> {
|
||||
self.init_sftp(sess)?.rmdir(rmdir.filename.as_path())?;
|
||||
rmdir.reply.try_send(())?;
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.rmdir(rmdir.filename.as_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
rmdir.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -862,8 +908,12 @@ impl SessionInner {
|
||||
///
|
||||
/// See [`Sftp::stat`] for more information.
|
||||
pub fn stat(&mut self, sess: &ssh2::Session, stat: &sftp::Stat) -> anyhow::Result<()> {
|
||||
let metadata = Metadata::from(self.init_sftp(sess)?.stat(stat.filename.as_path())?);
|
||||
stat.reply.try_send(metadata)?;
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.stat(stat.filename.as_path())
|
||||
.map(Metadata::from)
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
stat.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -872,8 +922,12 @@ impl SessionInner {
|
||||
///
|
||||
/// See [`Sftp::lstat`] for more information.
|
||||
pub fn lstat(&mut self, sess: &ssh2::Session, lstat: &sftp::Lstat) -> anyhow::Result<()> {
|
||||
let metadata = Metadata::from(self.init_sftp(sess)?.lstat(lstat.filename.as_path())?);
|
||||
lstat.reply.try_send(metadata)?;
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.lstat(lstat.filename.as_path())
|
||||
.map(Metadata::from)
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
lstat.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -882,9 +936,11 @@ impl SessionInner {
|
||||
///
|
||||
/// See [`Sftp::setstat`] for more information.
|
||||
pub fn setstat(&mut self, sess: &ssh2::Session, setstat: &sftp::Setstat) -> anyhow::Result<()> {
|
||||
self.init_sftp(sess)?
|
||||
.setstat(setstat.filename.as_path(), setstat.metadata.into())?;
|
||||
setstat.reply.try_send(())?;
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.setstat(setstat.filename.as_path(), setstat.metadata.into())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
setstat.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -893,9 +949,11 @@ impl SessionInner {
|
||||
///
|
||||
/// See [`Sftp::symlink`] for more information.
|
||||
pub fn symlink(&mut self, sess: &ssh2::Session, symlink: &sftp::Symlink) -> anyhow::Result<()> {
|
||||
self.init_sftp(sess)?
|
||||
.symlink(symlink.path.as_path(), symlink.target.as_path())?;
|
||||
symlink.reply.try_send(())?;
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.symlink(symlink.path.as_path(), symlink.target.as_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
symlink.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -908,8 +966,11 @@ impl SessionInner {
|
||||
sess: &ssh2::Session,
|
||||
readlink: &sftp::Readlink,
|
||||
) -> anyhow::Result<()> {
|
||||
let path = self.init_sftp(sess)?.readlink(readlink.path.as_path())?;
|
||||
readlink.reply.try_send(path)?;
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.readlink(readlink.path.as_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
readlink.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -922,8 +983,11 @@ impl SessionInner {
|
||||
sess: &ssh2::Session,
|
||||
realpath: &sftp::Realpath,
|
||||
) -> anyhow::Result<()> {
|
||||
let path = self.init_sftp(sess)?.realpath(realpath.path.as_path())?;
|
||||
realpath.reply.try_send(path)?;
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.realpath(realpath.path.as_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
realpath.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -932,12 +996,15 @@ impl SessionInner {
|
||||
///
|
||||
/// See [`Sftp::rename`] for more information.
|
||||
pub fn rename(&mut self, sess: &ssh2::Session, rename: &sftp::Rename) -> anyhow::Result<()> {
|
||||
self.init_sftp(sess)?.rename(
|
||||
rename.src.as_path(),
|
||||
rename.dst.as_path(),
|
||||
Some(rename.opts.into()),
|
||||
)?;
|
||||
rename.reply.try_send(())?;
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.rename(
|
||||
rename.src.as_path(),
|
||||
rename.dst.as_path(),
|
||||
Some(rename.opts.into()),
|
||||
)
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
rename.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -946,8 +1013,11 @@ impl SessionInner {
|
||||
///
|
||||
/// See [`Sftp::unlink`] for more information.
|
||||
pub fn unlink(&mut self, sess: &ssh2::Session, unlink: &sftp::Unlink) -> anyhow::Result<()> {
|
||||
self.init_sftp(sess)?.unlink(unlink.file.as_path())?;
|
||||
unlink.reply.try_send(())?;
|
||||
let result = self.init_sftp(sess).and_then(|sftp| {
|
||||
sftp.unlink(unlink.file.as_path())
|
||||
.map_err(SftpChannelError::from)
|
||||
});
|
||||
unlink.reply.try_send(result)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -956,28 +1026,19 @@ impl SessionInner {
|
||||
fn init_sftp<'a, 'b>(
|
||||
&'a mut self,
|
||||
sess: &'b ssh2::Session,
|
||||
) -> anyhow::Result<&'a mut ssh2::Sftp> {
|
||||
) -> SftpChannelResult<&'a mut ssh2::Sftp> {
|
||||
if self.sftp.is_none() {
|
||||
self.do_blocking(sess, |this, sess| {
|
||||
this.sftp = Some(sess.sftp()?);
|
||||
Ok(())
|
||||
})?;
|
||||
let blocking = sess.is_blocking();
|
||||
sess.set_blocking(true);
|
||||
|
||||
self.sftp = Some(sess.sftp()?);
|
||||
|
||||
sess.set_blocking(blocking);
|
||||
}
|
||||
|
||||
// NOTE: sftp should have been replaced with Some(sftp) from above
|
||||
Ok(self.sftp.as_mut().unwrap())
|
||||
}
|
||||
|
||||
fn do_blocking<F, R>(&mut self, sess: &ssh2::Session, mut f: F) -> anyhow::Result<R>
|
||||
where
|
||||
F: FnMut(&mut Self, &ssh2::Session) -> anyhow::Result<R>,
|
||||
{
|
||||
let blocking = sess.is_blocking();
|
||||
sess.set_blocking(true);
|
||||
let result = f(self, sess);
|
||||
sess.set_blocking(blocking);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -1,6 +1,10 @@
|
||||
use super::{SessionRequest, SessionSender};
|
||||
use smol::channel::{bounded, Sender};
|
||||
use smol::channel::{bounded, RecvError, Sender};
|
||||
use std::path::PathBuf;
|
||||
use thiserror::Error;
|
||||
|
||||
mod error;
|
||||
pub use error::{SftpError, SftpResult};
|
||||
|
||||
mod file;
|
||||
pub use file::File;
|
||||
@ -14,6 +18,28 @@ pub use types::{
|
||||
FilePermissions, FileType, Metadata, OpenFileType, OpenOptions, RenameOptions, WriteMode,
|
||||
};
|
||||
|
||||
/// Represents the result of some SFTP channel operation
|
||||
pub type SftpChannelResult<T> = Result<T, SftpChannelError>;
|
||||
|
||||
/// Represents an error that can occur when working with the SFTP channel
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SftpChannelError {
|
||||
#[error(transparent)]
|
||||
Sftp(#[from] SftpError),
|
||||
|
||||
#[error("File IO failed: {}", .0)]
|
||||
FileIo(#[from] std::io::Error),
|
||||
|
||||
#[error("Failed to send request: {}", .0)]
|
||||
SendFailed(#[from] anyhow::Error),
|
||||
|
||||
#[error("Failed to receive response: {}", .0)]
|
||||
RecvFailed(#[from] RecvError),
|
||||
|
||||
#[error("Library-specific error: {}", .0)]
|
||||
Other(#[source] ssh2::Error),
|
||||
}
|
||||
|
||||
/// Represents an open sftp channel for performing filesystem operations
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Sftp {
|
||||
@ -28,7 +54,7 @@ impl Sftp {
|
||||
&self,
|
||||
filename: impl Into<PathBuf>,
|
||||
opts: OpenOptions,
|
||||
) -> anyhow::Result<File> {
|
||||
) -> SftpChannelResult<File> {
|
||||
let (reply, rx) = bounded(1);
|
||||
|
||||
self.tx
|
||||
@ -38,7 +64,7 @@ impl Sftp {
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let mut result = rx.recv().await?;
|
||||
let mut result = rx.recv().await??;
|
||||
result.initialize_sender(self.tx.clone());
|
||||
Ok(result)
|
||||
}
|
||||
@ -46,7 +72,7 @@ impl Sftp {
|
||||
/// Helper to open a file in the `Read` mode.
|
||||
///
|
||||
/// See [`Sftp::open`] for more information.
|
||||
pub async fn open(&self, filename: impl Into<PathBuf>) -> anyhow::Result<File> {
|
||||
pub async fn open(&self, filename: impl Into<PathBuf>) -> SftpChannelResult<File> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Open(Open {
|
||||
@ -54,7 +80,7 @@ impl Sftp {
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let mut result = rx.recv().await?;
|
||||
let mut result = rx.recv().await??;
|
||||
result.initialize_sender(self.tx.clone());
|
||||
Ok(result)
|
||||
}
|
||||
@ -62,7 +88,7 @@ impl Sftp {
|
||||
/// Helper to create a file in write-only mode with truncation.
|
||||
///
|
||||
/// See [`Sftp::create`] for more information.
|
||||
pub async fn create(&self, filename: impl Into<PathBuf>) -> anyhow::Result<File> {
|
||||
pub async fn create(&self, filename: impl Into<PathBuf>) -> SftpChannelResult<File> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Create(Create {
|
||||
@ -70,7 +96,7 @@ impl Sftp {
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let mut result = rx.recv().await?;
|
||||
let mut result = rx.recv().await??;
|
||||
result.initialize_sender(self.tx.clone());
|
||||
Ok(result)
|
||||
}
|
||||
@ -78,7 +104,7 @@ impl Sftp {
|
||||
/// Helper to open a directory for reading its contents.
|
||||
///
|
||||
/// See [`Sftp::opendir`] for more information.
|
||||
pub async fn opendir(&self, filename: impl Into<PathBuf>) -> anyhow::Result<File> {
|
||||
pub async fn opendir(&self, filename: impl Into<PathBuf>) -> SftpChannelResult<File> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Opendir(Opendir {
|
||||
@ -86,7 +112,7 @@ impl Sftp {
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let mut result = rx.recv().await?;
|
||||
let mut result = rx.recv().await??;
|
||||
result.initialize_sender(self.tx.clone());
|
||||
Ok(result)
|
||||
}
|
||||
@ -100,7 +126,7 @@ impl Sftp {
|
||||
pub async fn readdir(
|
||||
&self,
|
||||
filename: impl Into<PathBuf>,
|
||||
) -> anyhow::Result<Vec<(PathBuf, Metadata)>> {
|
||||
) -> SftpChannelResult<Vec<(PathBuf, Metadata)>> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Readdir(Readdir {
|
||||
@ -108,14 +134,14 @@ impl Sftp {
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Create a directory on the remote filesystem.
|
||||
///
|
||||
/// See [`Sftp::rmdir`] for more information.
|
||||
pub async fn mkdir(&self, filename: impl Into<PathBuf>, mode: i32) -> anyhow::Result<()> {
|
||||
pub async fn mkdir(&self, filename: impl Into<PathBuf>, mode: i32) -> SftpChannelResult<()> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Mkdir(Mkdir {
|
||||
@ -124,14 +150,14 @@ impl Sftp {
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Remove a directory from the remote filesystem.
|
||||
///
|
||||
/// See [`Sftp::rmdir`] for more information.
|
||||
pub async fn rmdir(&self, filename: impl Into<PathBuf>) -> anyhow::Result<()> {
|
||||
pub async fn rmdir(&self, filename: impl Into<PathBuf>) -> SftpChannelResult<()> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Rmdir(Rmdir {
|
||||
@ -139,14 +165,14 @@ impl Sftp {
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Get the metadata for a file, performed by stat(2).
|
||||
///
|
||||
/// See [`Sftp::stat`] for more information.
|
||||
pub async fn stat(&self, filename: impl Into<PathBuf>) -> anyhow::Result<Metadata> {
|
||||
pub async fn stat(&self, filename: impl Into<PathBuf>) -> SftpChannelResult<Metadata> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Stat(Stat {
|
||||
@ -154,14 +180,14 @@ impl Sftp {
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Get the metadata for a file, performed by lstat(2).
|
||||
///
|
||||
/// See [`Sftp::lstat`] for more information.
|
||||
pub async fn lstat(&self, filename: impl Into<PathBuf>) -> anyhow::Result<Metadata> {
|
||||
pub async fn lstat(&self, filename: impl Into<PathBuf>) -> SftpChannelResult<Metadata> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Lstat(Lstat {
|
||||
@ -169,7 +195,7 @@ impl Sftp {
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@ -180,7 +206,7 @@ impl Sftp {
|
||||
&self,
|
||||
filename: impl Into<PathBuf>,
|
||||
metadata: Metadata,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> SftpChannelResult<()> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Setstat(Setstat {
|
||||
@ -189,7 +215,7 @@ impl Sftp {
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@ -200,7 +226,7 @@ impl Sftp {
|
||||
&self,
|
||||
path: impl Into<PathBuf>,
|
||||
target: impl Into<PathBuf>,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> SftpChannelResult<()> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Symlink(Symlink {
|
||||
@ -209,14 +235,14 @@ impl Sftp {
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Read a symlink at `path`.
|
||||
///
|
||||
/// See [`Sftp::readlink`] for more information.
|
||||
pub async fn readlink(&self, path: impl Into<PathBuf>) -> anyhow::Result<PathBuf> {
|
||||
pub async fn readlink(&self, path: impl Into<PathBuf>) -> SftpChannelResult<PathBuf> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Readlink(Readlink {
|
||||
@ -224,14 +250,14 @@ impl Sftp {
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Resolve the real path for `path`.
|
||||
///
|
||||
/// See [`Sftp::realpath`] for more information.
|
||||
pub async fn realpath(&self, path: impl Into<PathBuf>) -> anyhow::Result<PathBuf> {
|
||||
pub async fn realpath(&self, path: impl Into<PathBuf>) -> SftpChannelResult<PathBuf> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Realpath(Realpath {
|
||||
@ -239,7 +265,7 @@ impl Sftp {
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@ -251,7 +277,7 @@ impl Sftp {
|
||||
src: impl Into<PathBuf>,
|
||||
dst: impl Into<PathBuf>,
|
||||
opts: RenameOptions,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> SftpChannelResult<()> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Rename(Rename {
|
||||
@ -261,14 +287,14 @@ impl Sftp {
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Remove a file on the remote filesystem.
|
||||
///
|
||||
/// See [`Sftp::unlink`] for more information.
|
||||
pub async fn unlink(&self, file: impl Into<PathBuf>) -> anyhow::Result<()> {
|
||||
pub async fn unlink(&self, file: impl Into<PathBuf>) -> SftpChannelResult<()> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.send(SessionRequest::Sftp(SftpRequest::Unlink(Unlink {
|
||||
@ -276,7 +302,7 @@ impl Sftp {
|
||||
reply,
|
||||
})))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
@ -307,82 +333,82 @@ pub(crate) enum SftpRequest {
|
||||
pub(crate) struct OpenMode {
|
||||
pub filename: PathBuf,
|
||||
pub opts: OpenOptions,
|
||||
pub reply: Sender<File>,
|
||||
pub reply: Sender<SftpChannelResult<File>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Open {
|
||||
pub filename: PathBuf,
|
||||
pub reply: Sender<File>,
|
||||
pub reply: Sender<SftpChannelResult<File>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Create {
|
||||
pub filename: PathBuf,
|
||||
pub reply: Sender<File>,
|
||||
pub reply: Sender<SftpChannelResult<File>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Opendir {
|
||||
pub filename: PathBuf,
|
||||
pub reply: Sender<File>,
|
||||
pub reply: Sender<SftpChannelResult<File>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Readdir {
|
||||
pub filename: PathBuf,
|
||||
pub reply: Sender<Vec<(PathBuf, Metadata)>>,
|
||||
pub reply: Sender<SftpChannelResult<Vec<(PathBuf, Metadata)>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Mkdir {
|
||||
pub filename: PathBuf,
|
||||
pub mode: i32,
|
||||
pub reply: Sender<()>,
|
||||
pub reply: Sender<SftpChannelResult<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Rmdir {
|
||||
pub filename: PathBuf,
|
||||
pub reply: Sender<()>,
|
||||
pub reply: Sender<SftpChannelResult<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Stat {
|
||||
pub filename: PathBuf,
|
||||
pub reply: Sender<Metadata>,
|
||||
pub reply: Sender<SftpChannelResult<Metadata>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Lstat {
|
||||
pub filename: PathBuf,
|
||||
pub reply: Sender<Metadata>,
|
||||
pub reply: Sender<SftpChannelResult<Metadata>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Setstat {
|
||||
pub filename: PathBuf,
|
||||
pub metadata: Metadata,
|
||||
pub reply: Sender<()>,
|
||||
pub reply: Sender<SftpChannelResult<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Symlink {
|
||||
pub path: PathBuf,
|
||||
pub target: PathBuf,
|
||||
pub reply: Sender<()>,
|
||||
pub reply: Sender<SftpChannelResult<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Readlink {
|
||||
pub path: PathBuf,
|
||||
pub reply: Sender<PathBuf>,
|
||||
pub reply: Sender<SftpChannelResult<PathBuf>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Realpath {
|
||||
pub path: PathBuf,
|
||||
pub reply: Sender<PathBuf>,
|
||||
pub reply: Sender<SftpChannelResult<PathBuf>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -390,11 +416,25 @@ pub(crate) struct Rename {
|
||||
pub src: PathBuf,
|
||||
pub dst: PathBuf,
|
||||
pub opts: RenameOptions,
|
||||
pub reply: Sender<()>,
|
||||
pub reply: Sender<SftpChannelResult<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Unlink {
|
||||
pub file: PathBuf,
|
||||
pub reply: Sender<()>,
|
||||
pub reply: Sender<SftpChannelResult<()>>,
|
||||
}
|
||||
|
||||
mod ssh2_impl {
|
||||
use super::*;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
impl From<ssh2::Error> for SftpChannelError {
|
||||
fn from(err: ssh2::Error) -> Self {
|
||||
match SftpError::try_from(err) {
|
||||
Ok(x) => Self::Sftp(x),
|
||||
Err(x) => Self::Other(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
138
wezterm-ssh/src/session/sftp/error.rs
Normal file
138
wezterm-ssh/src/session/sftp/error.rs
Normal file
@ -0,0 +1,138 @@
|
||||
use std::convert::TryFrom;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Represents a result whose error is [`SftpError`]
|
||||
pub type SftpResult<T> = Result<T, SftpError>;
|
||||
|
||||
/// Represents errors associated with sftp operations
|
||||
#[derive(Copy, Clone, Debug, Error, Hash, PartialEq, Eq)]
|
||||
pub enum SftpError {
|
||||
// Following are available on libssh and libssh2
|
||||
#[error("End-of-file encountered")]
|
||||
Eof = 1,
|
||||
#[error("File doesn't exist")]
|
||||
NoSuchFile = 2,
|
||||
#[error("Permission denied")]
|
||||
PermissionDenied = 3,
|
||||
#[error("Generic failure")]
|
||||
Failure = 4,
|
||||
#[error("Garbage received from server")]
|
||||
BadMessage = 5,
|
||||
#[error("No connection has been set up")]
|
||||
NoConnection = 6,
|
||||
#[error("There was a connection, but we lost it")]
|
||||
ConnectionLost = 7,
|
||||
#[error("Operation not supported by the server")]
|
||||
OpUnsupported = 8,
|
||||
#[error("Invalid file handle")]
|
||||
InvalidHandle = 9,
|
||||
#[error("No such file or directory path exists")]
|
||||
NoSuchPath = 10,
|
||||
#[error("An attempt to create an already existing file or directory has been made")]
|
||||
FileAlreadyExists = 11,
|
||||
#[error("We are trying to write on a write-protected filesystem")]
|
||||
WriteProtect = 12,
|
||||
#[error("No media in remote drive")]
|
||||
NoMedia = 13,
|
||||
|
||||
// Below are libssh2-specific errors
|
||||
#[error("No space available on filesystem")]
|
||||
NoSpaceOnFilesystem = 14,
|
||||
#[error("Quota exceeded")]
|
||||
QuotaExceeded = 15,
|
||||
#[error("Unknown principal")]
|
||||
UnknownPrincipal = 16,
|
||||
#[error("Filesystem lock conflict")]
|
||||
LockConflict = 17,
|
||||
#[error("Directory is not empty")]
|
||||
DirNotEmpty = 18,
|
||||
#[error("Operation attempted against a path that is not a directory")]
|
||||
NotADirectory = 19,
|
||||
#[error("Filename invalid")]
|
||||
InvalidFilename = 20,
|
||||
#[error("Symlink loop encountered")]
|
||||
LinkLoop = 21,
|
||||
}
|
||||
|
||||
impl SftpError {
|
||||
/// Produces an SFTP error from the given code if it matches a known error type
|
||||
pub fn from_error_code(code: i32) -> Option<SftpError> {
|
||||
Self::try_from(code).ok()
|
||||
}
|
||||
|
||||
/// Converts into an error code
|
||||
pub fn to_error_code(self) -> i32 {
|
||||
self as i32
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<i32> for SftpError {
|
||||
type Error = Result<(), i32>;
|
||||
|
||||
/// Attempt to convert an arbitrary code to an sftp error, returning
|
||||
/// `Ok` if matching an sftp error or `Err` if the code represented a
|
||||
/// success or was unknown
|
||||
fn try_from(code: i32) -> Result<Self, Self::Error> {
|
||||
match code {
|
||||
// 0 means okay in libssh and libssh2, which isn't an error
|
||||
0 => Err(Ok(())),
|
||||
|
||||
1 => Ok(Self::Eof),
|
||||
2 => Ok(Self::NoSuchFile),
|
||||
3 => Ok(Self::PermissionDenied),
|
||||
4 => Ok(Self::Failure),
|
||||
5 => Ok(Self::BadMessage),
|
||||
6 => Ok(Self::NoConnection),
|
||||
7 => Ok(Self::ConnectionLost),
|
||||
8 => Ok(Self::OpUnsupported),
|
||||
9 => Ok(Self::InvalidHandle),
|
||||
10 => Ok(Self::NoSuchPath),
|
||||
11 => Ok(Self::FileAlreadyExists),
|
||||
12 => Ok(Self::WriteProtect),
|
||||
13 => Ok(Self::NoMedia),
|
||||
14 => Ok(Self::NoSpaceOnFilesystem),
|
||||
15 => Ok(Self::QuotaExceeded),
|
||||
16 => Ok(Self::UnknownPrincipal),
|
||||
17 => Ok(Self::LockConflict),
|
||||
18 => Ok(Self::DirNotEmpty),
|
||||
19 => Ok(Self::NotADirectory),
|
||||
20 => Ok(Self::InvalidFilename),
|
||||
21 => Ok(Self::LinkLoop),
|
||||
|
||||
// Unsupported codes get reflected back
|
||||
x => Err(Err(x)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod ssh2_impl {
|
||||
use super::*;
|
||||
|
||||
impl TryFrom<ssh2::Error> for SftpError {
|
||||
type Error = ssh2::Error;
|
||||
|
||||
fn try_from(err: ssh2::Error) -> Result<Self, Self::Error> {
|
||||
match err.code() {
|
||||
ssh2::ErrorCode::SFTP(x) => match Self::from_error_code(x) {
|
||||
Some(err) => Ok(err),
|
||||
None => Err(err),
|
||||
},
|
||||
_ => Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<ssh2::ErrorCode> for SftpError {
|
||||
type Error = ssh2::ErrorCode;
|
||||
|
||||
fn try_from(code: ssh2::ErrorCode) -> Result<Self, Self::Error> {
|
||||
match code {
|
||||
ssh2::ErrorCode::SFTP(x) => match Self::from_error_code(x) {
|
||||
Some(err) => Ok(err),
|
||||
None => Err(code),
|
||||
},
|
||||
x => Err(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +1,8 @@
|
||||
use super::{Metadata, SessionRequest, SessionSender, SftpRequest};
|
||||
use smol::{
|
||||
channel::{bounded, Sender},
|
||||
future::FutureExt,
|
||||
};
|
||||
use std::{
|
||||
fmt,
|
||||
future::Future,
|
||||
io,
|
||||
path::PathBuf,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use super::{Metadata, SessionRequest, SessionSender, SftpChannelResult, SftpRequest};
|
||||
use smol::channel::{bounded, Sender};
|
||||
use smol::future::FutureExt;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{fmt, future::Future, io, path::PathBuf, pin::Pin};
|
||||
|
||||
pub(crate) type FileId = usize;
|
||||
|
||||
@ -45,51 +37,51 @@ pub(crate) enum FileRequest {
|
||||
pub(crate) struct WriteFile {
|
||||
pub file_id: FileId,
|
||||
pub data: Vec<u8>,
|
||||
pub reply: Sender<()>,
|
||||
pub reply: Sender<SftpChannelResult<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ReadFile {
|
||||
pub file_id: FileId,
|
||||
pub max_bytes: usize,
|
||||
pub reply: Sender<Vec<u8>>,
|
||||
pub reply: Sender<SftpChannelResult<Vec<u8>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CloseFile {
|
||||
pub file_id: FileId,
|
||||
pub reply: Sender<()>,
|
||||
pub reply: Sender<SftpChannelResult<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FlushFile {
|
||||
pub file_id: FileId,
|
||||
pub reply: Sender<()>,
|
||||
pub reply: Sender<SftpChannelResult<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SetstatFile {
|
||||
pub file_id: FileId,
|
||||
pub metadata: Metadata,
|
||||
pub reply: Sender<()>,
|
||||
pub reply: Sender<SftpChannelResult<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct StatFile {
|
||||
pub file_id: FileId,
|
||||
pub reply: Sender<Metadata>,
|
||||
pub reply: Sender<SftpChannelResult<Metadata>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ReaddirFile {
|
||||
pub file_id: FileId,
|
||||
pub reply: Sender<(PathBuf, Metadata)>,
|
||||
pub reply: Sender<SftpChannelResult<(PathBuf, Metadata)>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FsyncFile {
|
||||
pub file_id: FileId,
|
||||
pub reply: Sender<()>,
|
||||
pub reply: Sender<SftpChannelResult<()>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for File {
|
||||
@ -131,7 +123,7 @@ impl File {
|
||||
/// Set the metadata for this handle.
|
||||
///
|
||||
/// See [`ssh2::File::setstat`] for more information.
|
||||
pub async fn setstat(&self, metadata: Metadata) -> anyhow::Result<()> {
|
||||
pub async fn setstat(&self, metadata: Metadata) -> SftpChannelResult<()> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.as_ref()
|
||||
@ -144,14 +136,14 @@ impl File {
|
||||
}),
|
||||
)))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Get the metadata for this handle.
|
||||
///
|
||||
/// See [`ssh2::File::stat`] for more information.
|
||||
pub async fn stat(&self) -> anyhow::Result<Metadata> {
|
||||
pub async fn stat(&self) -> SftpChannelResult<Metadata> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.as_ref()
|
||||
@ -163,7 +155,7 @@ impl File {
|
||||
},
|
||||
))))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@ -190,7 +182,7 @@ impl File {
|
||||
}),
|
||||
)))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@ -198,7 +190,7 @@ impl File {
|
||||
/// (like fsync(2)).
|
||||
///
|
||||
/// See [`ssh2::File::fsync`] for more information.
|
||||
pub async fn fsync(&self) -> anyhow::Result<()> {
|
||||
pub async fn fsync(&self) -> SftpChannelResult<()> {
|
||||
let (reply, rx) = bounded(1);
|
||||
self.tx
|
||||
.as_ref()
|
||||
@ -210,7 +202,7 @@ impl File {
|
||||
},
|
||||
))))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
@ -329,7 +321,7 @@ impl smol::io::AsyncWrite for File {
|
||||
}
|
||||
|
||||
/// Writes some bytes to the file.
|
||||
async fn inner_write(tx: SessionSender, file_id: usize, data: Vec<u8>) -> anyhow::Result<()> {
|
||||
async fn inner_write(tx: SessionSender, file_id: usize, data: Vec<u8>) -> SftpChannelResult<()> {
|
||||
let (reply, rx) = bounded(1);
|
||||
tx.send(SessionRequest::Sftp(SftpRequest::File(FileRequest::Write(
|
||||
WriteFile {
|
||||
@ -339,7 +331,7 @@ async fn inner_write(tx: SessionSender, file_id: usize, data: Vec<u8>) -> anyhow
|
||||
},
|
||||
))))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@ -351,7 +343,7 @@ async fn inner_read(
|
||||
tx: SessionSender,
|
||||
file_id: usize,
|
||||
max_bytes: usize,
|
||||
) -> anyhow::Result<Vec<u8>> {
|
||||
) -> SftpChannelResult<Vec<u8>> {
|
||||
let (reply, rx) = bounded(1);
|
||||
tx.send(SessionRequest::Sftp(SftpRequest::File(FileRequest::Read(
|
||||
ReadFile {
|
||||
@ -361,28 +353,28 @@ async fn inner_read(
|
||||
},
|
||||
))))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Flushes the remote file
|
||||
async fn inner_flush(tx: SessionSender, file_id: usize) -> anyhow::Result<()> {
|
||||
async fn inner_flush(tx: SessionSender, file_id: usize) -> SftpChannelResult<()> {
|
||||
let (reply, rx) = bounded(1);
|
||||
tx.send(SessionRequest::Sftp(SftpRequest::File(FileRequest::Flush(
|
||||
FlushFile { file_id, reply },
|
||||
))))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Closes the handle to the remote file
|
||||
async fn inner_close(tx: SessionSender, file_id: usize) -> anyhow::Result<()> {
|
||||
async fn inner_close(tx: SessionSender, file_id: usize) -> SftpChannelResult<()> {
|
||||
let (reply, rx) = bounded(1);
|
||||
tx.send(SessionRequest::Sftp(SftpRequest::File(FileRequest::Close(
|
||||
CloseFile { file_id, reply },
|
||||
))))
|
||||
.await?;
|
||||
let result = rx.recv().await?;
|
||||
let result = rx.recv().await??;
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -23,14 +23,17 @@ pub enum FileType {
|
||||
}
|
||||
|
||||
impl FileType {
|
||||
/// Returns true if file is a type of directory
|
||||
pub fn is_dir(self) -> bool {
|
||||
matches!(self, Self::Dir)
|
||||
}
|
||||
|
||||
/// Returns true if file is a type of regular file
|
||||
pub fn is_file(self) -> bool {
|
||||
matches!(self, Self::File)
|
||||
}
|
||||
|
||||
/// Returns true if file is a type of symlink
|
||||
pub fn is_symlink(self) -> bool {
|
||||
matches!(self, Self::Symlink)
|
||||
}
|
||||
@ -164,17 +167,27 @@ impl Metadata {
|
||||
self.size.unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Returns true if metadata is for a directory
|
||||
pub fn is_dir(self) -> bool {
|
||||
self.ty.is_dir()
|
||||
}
|
||||
|
||||
/// Returns true if metadata is for a regular file
|
||||
pub fn is_file(self) -> bool {
|
||||
self.ty.is_file()
|
||||
}
|
||||
|
||||
/// Returns true if metadata is for a symlink
|
||||
pub fn is_symlink(self) -> bool {
|
||||
self.ty.is_symlink()
|
||||
}
|
||||
|
||||
/// Returns true if metadata permissions indicate file is readonly
|
||||
pub fn is_readonly(self) -> bool {
|
||||
self.permissions
|
||||
.map(FilePermissions::is_readonly)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents options to provide when opening a file or directory
|
||||
|
Loading…
Reference in New Issue
Block a user