1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-22 22:42:48 +03:00

Switch to camino::{Utf8PathBuf, Utf8Path}

This commit is contained in:
Chip Senkbeil 2021-10-03 20:36:30 -05:00 committed by Wez Furlong
parent 1ff2a1eb82
commit 2415a44d74
8 changed files with 287 additions and 189 deletions

7
Cargo.lock generated
View File

@ -550,6 +550,12 @@ version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
[[package]]
name = "camino"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52d74260d9bf6944e2208aa46841b4b8f0d7ffc0849a06837b2f510337f86b2b"
[[package]]
name = "cassowary"
version = "0.3.0"
@ -5364,6 +5370,7 @@ dependencies = [
"async_ossl",
"base64",
"bitflags",
"camino",
"dirs-next",
"filedescriptor",
"filenamegen",

View File

@ -17,6 +17,7 @@ vendored-openssl = ["ssh2/vendored-openssl"]
anyhow = "1.0"
base64 = "0.13"
bitflags = "1.3"
camino = "1.0"
dirs-next = "2.0"
filedescriptor = { version="0.8", path = "../filedescriptor" }
filenamegen = "0.2"

View File

@ -11,5 +11,6 @@ pub use pty::*;
pub use session::*;
// NOTE: Re-exported as is exposed in a public API of this crate
pub use camino::{Utf8Path, Utf8PathBuf};
pub use filedescriptor::FileDescriptor;
pub use portable_pty::Child;

View File

@ -3,6 +3,7 @@ use crate::config::ConfigMap;
use crate::host::*;
use crate::pty::*;
use anyhow::{anyhow, Context};
use camino::Utf8PathBuf;
use filedescriptor::{
poll, pollfd, socketpair, AsRawSocketDescriptor, FileDescriptor, POLLIN, POLLOUT,
};
@ -10,7 +11,8 @@ use portable_pty::{ExitStatus, PtySize};
use smol::channel::{bounded, Receiver, Sender, TryRecvError};
use ssh2::BlockDirections;
use std::collections::{HashMap, VecDeque};
use std::io::{Read, Write};
use std::convert::TryFrom;
use std::io::{self, Read, Write};
use std::net::TcpStream;
use std::sync::{Arc, Mutex};
use std::time::Duration;
@ -617,8 +619,6 @@ impl SessionInner {
}
/// Open a handle to a file.
///
/// See [`Sftp::open_mode`] for more information.
pub fn open_with_mode(
&mut self,
sess: &ssh2::Session,
@ -629,7 +629,7 @@ impl SessionInner {
let open_type: ssh2::OpenType = msg.opts.ty.into();
let result = self.init_sftp(sess).and_then(|sftp| {
sftp.open_mode(msg.filename.as_path(), flags, mode, open_type)
sftp.open_mode(msg.filename.as_std_path(), flags, mode, open_type)
.map_err(SftpChannelError::from)
});
@ -646,11 +646,9 @@ impl SessionInner {
}
/// Helper to open a file in the `Read` mode.
///
/// See [`Sftp::open`] for more information.
pub fn open(&mut self, sess: &ssh2::Session, msg: &sftp::Open) -> anyhow::Result<()> {
let result = self.init_sftp(sess).and_then(|sftp| {
sftp.open(msg.filename.as_path())
sftp.open(msg.filename.as_std_path())
.map_err(SftpChannelError::from)
});
@ -667,11 +665,9 @@ impl SessionInner {
}
/// Helper to create a file in write-only mode with truncation.
///
/// See [`Sftp::create`] for more information.
pub fn create(&mut self, sess: &ssh2::Session, msg: &sftp::Create) -> anyhow::Result<()> {
let result = self.init_sftp(sess).and_then(|sftp| {
sftp.create(msg.filename.as_path())
sftp.create(msg.filename.as_std_path())
.map_err(SftpChannelError::from)
});
@ -688,11 +684,9 @@ impl SessionInner {
}
/// Helper to open a directory for reading its contents.
///
/// See [`Sftp::opendir`] for more information.
pub fn open_dir(&mut self, sess: &ssh2::Session, msg: &sftp::OpenDir) -> anyhow::Result<()> {
let result = self.init_sftp(sess).and_then(|sftp| {
sftp.opendir(msg.filename.as_path())
sftp.opendir(msg.filename.as_std_path())
.map_err(SftpChannelError::from)
});
@ -821,8 +815,14 @@ impl SessionInner {
if let Some(file) = self.files.get_mut(&msg.file_id) {
let result = file
.readdir()
.map(|(path, stat)| (path, Metadata::from(stat)))
.map_err(SftpChannelError::from);
.map_err(SftpChannelError::from)
.and_then(|(path, stat)| match Utf8PathBuf::try_from(path) {
Ok(path) => Ok((path, Metadata::from(stat))),
Err(x) => Err(SftpChannelError::from(io::Error::new(
io::ErrorKind::InvalidData,
x,
))),
});
msg.reply.try_send(result)?;
}
@ -844,18 +844,26 @@ impl SessionInner {
}
/// Convenience function to read the files in a directory.
///
/// See [`Sftp::readdir`] for more information.
pub fn read_dir(&mut self, sess: &ssh2::Session, msg: &sftp::ReadDir) -> anyhow::Result<()> {
let result = self.init_sftp(sess).and_then(|sftp| {
sftp.readdir(msg.filename.as_path())
.map(|entries| {
entries
.into_iter()
.map(|(path, stat)| (path, Metadata::from(stat)))
.collect()
})
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(io::Error::new(
io::ErrorKind::InvalidData,
x,
)));
}
}
}
Ok(mapped_entries)
})
});
msg.reply.try_send(result)?;
@ -863,15 +871,13 @@ impl SessionInner {
}
/// Create a directory on the remote filesystem.
///
/// See [`Sftp::rmdir`] for more information.
pub fn create_dir(
&mut self,
sess: &ssh2::Session,
msg: &sftp::CreateDir,
) -> anyhow::Result<()> {
let result = self.init_sftp(sess).and_then(|sftp| {
sftp.mkdir(msg.filename.as_path(), msg.mode)
sftp.mkdir(msg.filename.as_std_path(), msg.mode)
.map_err(SftpChannelError::from)
});
msg.reply.try_send(result)?;
@ -880,15 +886,13 @@ impl SessionInner {
}
/// Remove a directory from the remote filesystem.
///
/// See [`Sftp::rmdir`] for more information.
pub fn remove_dir(
&mut self,
sess: &ssh2::Session,
msg: &sftp::RemoveDir,
) -> anyhow::Result<()> {
let result = self.init_sftp(sess).and_then(|sftp| {
sftp.rmdir(msg.filename.as_path())
sftp.rmdir(msg.filename.as_std_path())
.map_err(SftpChannelError::from)
});
msg.reply.try_send(result)?;
@ -897,15 +901,13 @@ impl SessionInner {
}
/// Get the metadata for a file, performed by stat(2).
///
/// See [`Sftp::stat`] for more information.
pub fn metadata(
&mut self,
sess: &ssh2::Session,
msg: &sftp::GetMetadata,
) -> anyhow::Result<()> {
let result = self.init_sftp(sess).and_then(|sftp| {
sftp.stat(msg.filename.as_path())
sftp.stat(msg.filename.as_std_path())
.map(Metadata::from)
.map_err(SftpChannelError::from)
});
@ -915,15 +917,13 @@ impl SessionInner {
}
/// Get the metadata for a file, performed by lstat(2).
///
/// See [`Sftp::lstat`] for more information.
pub fn symlink_metadata(
&mut self,
sess: &ssh2::Session,
msg: &sftp::SymlinkMetadata,
) -> anyhow::Result<()> {
let result = self.init_sftp(sess).and_then(|sftp| {
sftp.lstat(msg.filename.as_path())
sftp.lstat(msg.filename.as_std_path())
.map(Metadata::from)
.map_err(SftpChannelError::from)
});
@ -933,15 +933,13 @@ impl SessionInner {
}
/// Set the metadata for a file.
///
/// See [`Sftp::setstat`] for more information.
pub fn set_metadata(
&mut self,
sess: &ssh2::Session,
msg: &sftp::SetMetadata,
) -> anyhow::Result<()> {
let result = self.init_sftp(sess).and_then(|sftp| {
sftp.setstat(msg.filename.as_path(), msg.metadata.into())
sftp.setstat(msg.filename.as_std_path(), msg.metadata.into())
.map_err(SftpChannelError::from)
});
msg.reply.try_send(result)?;
@ -950,11 +948,9 @@ impl SessionInner {
}
/// Create symlink at `target` pointing at `path`.
///
/// See [`Sftp::symlink`] for more information.
pub fn symlink(&mut self, sess: &ssh2::Session, msg: &sftp::Symlink) -> anyhow::Result<()> {
let result = self.init_sftp(sess).and_then(|sftp| {
sftp.symlink(msg.path.as_path(), msg.target.as_path())
sftp.symlink(msg.path.as_std_path(), msg.target.as_std_path())
.map_err(SftpChannelError::from)
});
msg.reply.try_send(result)?;
@ -963,12 +959,15 @@ impl SessionInner {
}
/// Read a symlink at `path`.
///
/// See [`Sftp::readlink`] for more information.
pub fn read_link(&mut self, sess: &ssh2::Session, msg: &sftp::ReadLink) -> anyhow::Result<()> {
let result = self.init_sftp(sess).and_then(|sftp| {
sftp.readlink(msg.path.as_path())
sftp.readlink(msg.path.as_std_path())
.map_err(SftpChannelError::from)
.and_then(|path| {
Utf8PathBuf::try_from(path).map_err(|x| {
SftpChannelError::from(io::Error::new(io::ErrorKind::InvalidData, x))
})
})
});
msg.reply.try_send(result)?;
@ -976,16 +975,19 @@ impl SessionInner {
}
/// Resolve the real path for `path`.
///
/// See [`Sftp::realpath`] for more information.
pub fn canonicalize(
&mut self,
sess: &ssh2::Session,
msg: &sftp::Canonicalize,
) -> anyhow::Result<()> {
let result = self.init_sftp(sess).and_then(|sftp| {
sftp.realpath(msg.path.as_path())
sftp.realpath(msg.path.as_std_path())
.map_err(SftpChannelError::from)
.and_then(|path| {
Utf8PathBuf::try_from(path).map_err(|x| {
SftpChannelError::from(io::Error::new(io::ErrorKind::InvalidData, x))
})
})
});
msg.reply.try_send(result)?;
@ -993,12 +995,14 @@ impl SessionInner {
}
/// Rename the filesystem object on the remote filesystem.
///
/// See [`Sftp::rename`] for more information.
pub fn rename(&mut self, sess: &ssh2::Session, msg: &sftp::Rename) -> anyhow::Result<()> {
let result = self.init_sftp(sess).and_then(|sftp| {
sftp.rename(msg.src.as_path(), msg.dst.as_path(), Some(msg.opts.into()))
.map_err(SftpChannelError::from)
sftp.rename(
msg.src.as_std_path(),
msg.dst.as_std_path(),
Some(msg.opts.into()),
)
.map_err(SftpChannelError::from)
});
msg.reply.try_send(result)?;
@ -1006,15 +1010,13 @@ impl SessionInner {
}
/// Remove a file on the remote filesystem.
///
/// See [`Sftp::unlink`] for more information.
pub fn remove_file(
&mut self,
sess: &ssh2::Session,
msg: &sftp::RemoveFile,
) -> anyhow::Result<()> {
let result = self.init_sftp(sess).and_then(|sftp| {
sftp.unlink(msg.file.as_path())
sftp.unlink(msg.file.as_std_path())
.map_err(SftpChannelError::from)
});
msg.reply.try_send(result)?;

View File

@ -1,6 +1,8 @@
use super::{SessionRequest, SessionSender};
use camino::Utf8PathBuf;
use smol::channel::{bounded, RecvError, Sender};
use std::path::PathBuf;
use std::convert::TryInto;
use std::io;
use thiserror::Error;
mod error;
@ -18,6 +20,13 @@ pub use types::{
FilePermissions, FileType, Metadata, OpenFileType, OpenOptions, RenameOptions, WriteMode,
};
fn into_invalid_data<E>(err: E) -> io::Error
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
io::Error::new(io::ErrorKind::InvalidData, err)
}
/// Represents the result of some SFTP channel operation
pub type SftpChannelResult<T> = Result<T, SftpChannelError>;
@ -48,17 +57,21 @@ pub struct Sftp {
impl Sftp {
/// Open a handle to a file.
pub async fn open_with_mode(
pub async fn open_with_mode<T, E>(
&self,
filename: impl Into<PathBuf>,
filename: T,
opts: OpenOptions,
) -> SftpChannelResult<File> {
) -> SftpChannelResult<File>
where
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::OpenWithMode(
OpenWithMode {
filename: filename.into(),
filename: filename.try_into().map_err(into_invalid_data)?,
opts,
reply,
},
@ -70,11 +83,15 @@ impl Sftp {
}
/// Helper to open a file in the `Read` mode.
pub async fn open(&self, filename: impl Into<PathBuf>) -> SftpChannelResult<File> {
pub async fn open<T, E>(&self, filename: T) -> SftpChannelResult<File>
where
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.into(),
filename: filename.try_into().map_err(into_invalid_data)?,
reply,
})))
.await?;
@ -84,11 +101,15 @@ impl Sftp {
}
/// Helper to create a file in write-only mode with truncation.
pub async fn create(&self, filename: impl Into<PathBuf>) -> SftpChannelResult<File> {
pub async fn create<T, E>(&self, filename: T) -> SftpChannelResult<File>
where
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.into(),
filename: filename.try_into().map_err(into_invalid_data)?,
reply,
})))
.await?;
@ -98,11 +119,15 @@ impl Sftp {
}
/// Helper to open a directory for reading its contents.
pub async fn open_dir(&self, filename: impl Into<PathBuf>) -> SftpChannelResult<File> {
pub async fn open_dir<T, E>(&self, filename: T) -> SftpChannelResult<File>
where
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::OpenDir(OpenDir {
filename: filename.into(),
filename: filename.try_into().map_err(into_invalid_data)?,
reply,
})))
.await?;
@ -115,14 +140,18 @@ impl Sftp {
///
/// The returned paths are all joined with dirname when returned, and the paths . and .. are
/// filtered out of the returned list.
pub async fn read_dir(
pub async fn read_dir<T, E>(
&self,
filename: impl Into<PathBuf>,
) -> SftpChannelResult<Vec<(PathBuf, Metadata)>> {
filename: T,
) -> SftpChannelResult<Vec<(Utf8PathBuf, Metadata)>>
where
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::ReadDir(ReadDir {
filename: filename.into(),
filename: filename.try_into().map_err(into_invalid_data)?,
reply,
})))
.await?;
@ -131,15 +160,15 @@ impl Sftp {
}
/// Create a directory on the remote filesystem.
pub async fn create_dir(
&self,
filename: impl Into<PathBuf>,
mode: i32,
) -> SftpChannelResult<()> {
pub async fn create_dir<T, E>(&self, filename: T, mode: i32) -> SftpChannelResult<()>
where
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::CreateDir(CreateDir {
filename: filename.into(),
filename: filename.try_into().map_err(into_invalid_data)?,
mode,
reply,
})))
@ -149,11 +178,15 @@ impl Sftp {
}
/// Remove a directory from the remote filesystem.
pub async fn remove_dir(&self, filename: impl Into<PathBuf>) -> SftpChannelResult<()> {
pub async fn remove_dir<T, E>(&self, filename: T) -> SftpChannelResult<()>
where
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::RemoveDir(RemoveDir {
filename: filename.into(),
filename: filename.try_into().map_err(into_invalid_data)?,
reply,
})))
.await?;
@ -162,11 +195,15 @@ impl Sftp {
}
/// Get the metadata for a file, performed by stat(2).
pub async fn metadata(&self, filename: impl Into<PathBuf>) -> SftpChannelResult<Metadata> {
pub async fn metadata<T, E>(&self, filename: T) -> SftpChannelResult<Metadata>
where
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::Metadata(GetMetadata {
filename: filename.into(),
filename: filename.try_into().map_err(into_invalid_data)?,
reply,
})))
.await?;
@ -175,15 +212,16 @@ impl Sftp {
}
/// Get the metadata for a file, performed by lstat(2).
pub async fn symlink_metadata(
&self,
filename: impl Into<PathBuf>,
) -> SftpChannelResult<Metadata> {
pub async fn symlink_metadata<T, E>(&self, filename: T) -> SftpChannelResult<Metadata>
where
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::SymlinkMetadata(
SymlinkMetadata {
filename: filename.into(),
filename: filename.try_into().map_err(into_invalid_data)?,
reply,
},
)))
@ -193,16 +231,16 @@ impl Sftp {
}
/// Set the metadata for a file.
pub async fn set_metadata(
&self,
filename: impl Into<PathBuf>,
metadata: Metadata,
) -> SftpChannelResult<()> {
pub async fn set_metadata<T, E>(&self, filename: T, metadata: Metadata) -> SftpChannelResult<()>
where
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::SetMetadata(
SetMetadata {
filename: filename.into(),
filename: filename.try_into().map_err(into_invalid_data)?,
metadata,
reply,
},
@ -213,16 +251,18 @@ impl Sftp {
}
/// Create symlink at `target` pointing at `path`.
pub async fn symlink(
&self,
path: impl Into<PathBuf>,
target: impl Into<PathBuf>,
) -> SftpChannelResult<()> {
pub async fn symlink<T1, T2, E1, E2>(&self, path: T1, target: T2) -> SftpChannelResult<()>
where
T1: TryInto<Utf8PathBuf, Error = E1>,
T2: TryInto<Utf8PathBuf, Error = E2>,
E1: Into<Box<dyn std::error::Error + Send + Sync>>,
E2: Into<Box<dyn std::error::Error + Send + Sync>>,
{
let (reply, rx) = bounded(1);
self.tx
.send(SessionRequest::Sftp(SftpRequest::Symlink(Symlink {
path: path.into(),
target: target.into(),
path: path.try_into().map_err(into_invalid_data)?,
target: target.try_into().map_err(into_invalid_data)?,
reply,
})))
.await?;
@ -231,11 +271,15 @@ impl Sftp {
}
/// Read a symlink at `path`.
pub async fn read_link(&self, path: impl Into<PathBuf>) -> SftpChannelResult<PathBuf> {
pub async fn read_link<T, E>(&self, path: T) -> SftpChannelResult<Utf8PathBuf>
where
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::ReadLink(ReadLink {
path: path.into(),
path: path.try_into().map_err(into_invalid_data)?,
reply,
})))
.await?;
@ -244,12 +288,16 @@ impl Sftp {
}
/// Resolve the real path for `path`.
pub async fn canonicalize(&self, path: impl Into<PathBuf>) -> SftpChannelResult<PathBuf> {
pub async fn canonicalize<T, E>(&self, path: T) -> SftpChannelResult<Utf8PathBuf>
where
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::Canonicalize(
Canonicalize {
path: path.into(),
path: path.try_into().map_err(into_invalid_data)?,
reply,
},
)))
@ -259,17 +307,23 @@ impl Sftp {
}
/// Rename the filesystem object on the remote filesystem.
pub async fn rename(
pub async fn rename<T1, T2, E1, E2>(
&self,
src: impl Into<PathBuf>,
dst: impl Into<PathBuf>,
src: T1,
dst: T2,
opts: RenameOptions,
) -> SftpChannelResult<()> {
) -> SftpChannelResult<()>
where
T1: TryInto<Utf8PathBuf, Error = E1>,
T2: TryInto<Utf8PathBuf, Error = E2>,
E1: Into<Box<dyn std::error::Error + Send + Sync>>,
E2: Into<Box<dyn std::error::Error + Send + Sync>>,
{
let (reply, rx) = bounded(1);
self.tx
.send(SessionRequest::Sftp(SftpRequest::Rename(Rename {
src: src.into(),
dst: dst.into(),
src: src.try_into().map_err(into_invalid_data)?,
dst: dst.try_into().map_err(into_invalid_data)?,
opts,
reply,
})))
@ -279,11 +333,15 @@ impl Sftp {
}
/// Remove a file on the remote filesystem.
pub async fn remove_file(&self, file: impl Into<PathBuf>) -> SftpChannelResult<()> {
pub async fn remove_file<T, E>(&self, file: T) -> SftpChannelResult<()>
where
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::RemoveFile(RemoveFile {
file: file.into(),
file: file.try_into().map_err(into_invalid_data)?,
reply,
})))
.await?;
@ -316,97 +374,97 @@ pub(crate) enum SftpRequest {
#[derive(Debug)]
pub(crate) struct OpenWithMode {
pub filename: PathBuf,
pub filename: Utf8PathBuf,
pub opts: OpenOptions,
pub reply: Sender<SftpChannelResult<File>>,
}
#[derive(Debug)]
pub(crate) struct Open {
pub filename: PathBuf,
pub filename: Utf8PathBuf,
pub reply: Sender<SftpChannelResult<File>>,
}
#[derive(Debug)]
pub(crate) struct Create {
pub filename: PathBuf,
pub filename: Utf8PathBuf,
pub reply: Sender<SftpChannelResult<File>>,
}
#[derive(Debug)]
pub(crate) struct OpenDir {
pub filename: PathBuf,
pub filename: Utf8PathBuf,
pub reply: Sender<SftpChannelResult<File>>,
}
#[derive(Debug)]
pub(crate) struct ReadDir {
pub filename: PathBuf,
pub reply: Sender<SftpChannelResult<Vec<(PathBuf, Metadata)>>>,
pub filename: Utf8PathBuf,
pub reply: Sender<SftpChannelResult<Vec<(Utf8PathBuf, Metadata)>>>,
}
#[derive(Debug)]
pub(crate) struct CreateDir {
pub filename: PathBuf,
pub filename: Utf8PathBuf,
pub mode: i32,
pub reply: Sender<SftpChannelResult<()>>,
}
#[derive(Debug)]
pub(crate) struct RemoveDir {
pub filename: PathBuf,
pub filename: Utf8PathBuf,
pub reply: Sender<SftpChannelResult<()>>,
}
#[derive(Debug)]
pub(crate) struct GetMetadata {
pub filename: PathBuf,
pub filename: Utf8PathBuf,
pub reply: Sender<SftpChannelResult<Metadata>>,
}
#[derive(Debug)]
pub(crate) struct SymlinkMetadata {
pub filename: PathBuf,
pub filename: Utf8PathBuf,
pub reply: Sender<SftpChannelResult<Metadata>>,
}
#[derive(Debug)]
pub(crate) struct SetMetadata {
pub filename: PathBuf,
pub filename: Utf8PathBuf,
pub metadata: Metadata,
pub reply: Sender<SftpChannelResult<()>>,
}
#[derive(Debug)]
pub(crate) struct Symlink {
pub path: PathBuf,
pub target: PathBuf,
pub path: Utf8PathBuf,
pub target: Utf8PathBuf,
pub reply: Sender<SftpChannelResult<()>>,
}
#[derive(Debug)]
pub(crate) struct ReadLink {
pub path: PathBuf,
pub reply: Sender<SftpChannelResult<PathBuf>>,
pub path: Utf8PathBuf,
pub reply: Sender<SftpChannelResult<Utf8PathBuf>>,
}
#[derive(Debug)]
pub(crate) struct Canonicalize {
pub path: PathBuf,
pub reply: Sender<SftpChannelResult<PathBuf>>,
pub path: Utf8PathBuf,
pub reply: Sender<SftpChannelResult<Utf8PathBuf>>,
}
#[derive(Debug)]
pub(crate) struct Rename {
pub src: PathBuf,
pub dst: PathBuf,
pub src: Utf8PathBuf,
pub dst: Utf8PathBuf,
pub opts: RenameOptions,
pub reply: Sender<SftpChannelResult<()>>,
}
#[derive(Debug)]
pub(crate) struct RemoveFile {
pub file: PathBuf,
pub file: Utf8PathBuf,
pub reply: Sender<SftpChannelResult<()>>,
}

View File

@ -1,8 +1,9 @@
use super::{Metadata, SessionRequest, SessionSender, SftpChannelResult, SftpRequest};
use camino::Utf8PathBuf;
use smol::channel::{bounded, Sender};
use smol::future::FutureExt;
use std::task::{Context, Poll};
use std::{fmt, future::Future, io, path::PathBuf, pin::Pin};
use std::{fmt, future::Future, io, pin::Pin};
pub(crate) type FileId = usize;
@ -75,7 +76,7 @@ pub(crate) struct MetadataFile {
#[derive(Debug)]
pub(crate) struct ReadDirFile {
pub file_id: FileId,
pub reply: Sender<SftpChannelResult<(PathBuf, Metadata)>>,
pub reply: Sender<SftpChannelResult<(Utf8PathBuf, Metadata)>>,
}
#[derive(Debug)]
@ -170,7 +171,7 @@ impl File {
/// files in this directory.
///
/// See [`ssh2::File::readdir`] for more information.
pub async fn read_dir(&self) -> anyhow::Result<(PathBuf, Metadata)> {
pub async fn read_dir(&self) -> anyhow::Result<(Utf8PathBuf, Metadata)> {
let (reply, rx) = bounded(1);
self.tx
.as_ref()

View File

@ -2,8 +2,8 @@ use crate::sshd::session;
use assert_fs::{prelude::*, TempDir};
use predicates::prelude::*;
use rstest::*;
use std::path::PathBuf;
use wezterm_ssh::{FileType, Session, SftpChannelError, SftpError};
use std::convert::TryInto;
use wezterm_ssh::{FileType, Session, SftpChannelError, SftpError, Utf8PathBuf};
// Sftp file tests
mod file;
@ -47,23 +47,26 @@ async fn read_dir_should_return_list_of_directories_files_and_symlinks(#[future]
let mut contents = session
.sftp()
.read_dir(temp.path())
.read_dir(temp.path().to_path_buf())
.await
.expect("Failed to read directory")
.into_iter()
.map(|(p, s)| (p, file_type_to_str(s.ty)))
.collect::<Vec<(PathBuf, &'static str)>>();
.collect::<Vec<(Utf8PathBuf, &'static str)>>();
contents.sort_unstable_by_key(|(p, _)| p.to_path_buf());
assert_eq!(
contents,
vec![
(dir1.path().to_path_buf(), "dir"),
(dir2.path().to_path_buf(), "dir"),
(file1.path().to_path_buf(), "file"),
(file2.path().to_path_buf(), "file"),
(link_dir.path().to_path_buf(), "symlink"),
(link_file.path().to_path_buf(), "symlink"),
(dir1.path().to_path_buf().try_into().unwrap(), "dir"),
(dir2.path().to_path_buf().try_into().unwrap(), "dir"),
(file1.path().to_path_buf().try_into().unwrap(), "file"),
(file2.path().to_path_buf().try_into().unwrap(), "file"),
(link_dir.path().to_path_buf().try_into().unwrap(), "symlink"),
(
link_file.path().to_path_buf().try_into().unwrap(),
"symlink"
),
]
);
}
@ -78,7 +81,7 @@ async fn create_dir_should_create_a_directory_on_the_remote_filesystem(#[future]
session
.sftp()
.create_dir(temp.child("dir").path(), 0o644)
.create_dir(temp.child("dir").path().to_path_buf(), 0o644)
.await
.expect("Failed to create directory");
@ -97,7 +100,7 @@ async fn create_dir_should_return_error_if_unable_to_create_directory(#[future]
// Attempt to create a nested directory structure, which is not supported
let result = session
.sftp()
.create_dir(temp.child("dir").child("dir").path(), 0o644)
.create_dir(temp.child("dir").child("dir").path().to_path_buf(), 0o644)
.await;
assert!(
result.is_err(),
@ -125,7 +128,7 @@ async fn remove_dir_should_remove_a_remote_directory(#[future] session: Session)
dir.create_dir_all().unwrap();
session
.sftp()
.remove_dir(dir.path())
.remove_dir(dir.path().to_path_buf())
.await
.expect("Failed to remove directory");
@ -146,7 +149,7 @@ async fn remove_dir_should_return_an_error_if_failed_to_remove_directory(
// Attempt to remove a missing path
let result = session
.sftp()
.remove_dir(temp.child("missing-dir").path())
.remove_dir(temp.child("missing-dir").path().to_path_buf())
.await;
assert!(
result.is_err(),
@ -159,7 +162,7 @@ async fn remove_dir_should_return_an_error_if_failed_to_remove_directory(
dir.create_dir_all().unwrap();
dir.child("file").touch().unwrap();
let result = session.sftp().remove_dir(dir.path()).await;
let result = session.sftp().remove_dir(dir.path().to_path_buf()).await;
assert!(
result.is_err(),
"Unexpectedly succeeded in removing non-empty directory: {:?}",
@ -172,7 +175,7 @@ async fn remove_dir_should_return_an_error_if_failed_to_remove_directory(
// Attempt to remove a file (not a directory)
let file = temp.child("file");
file.touch().unwrap();
let result = session.sftp().remove_dir(file.path()).await;
let result = session.sftp().remove_dir(file.path().to_path_buf()).await;
assert!(
result.is_err(),
"Unexpectedly succeeded in removing file: {:?}",
@ -195,7 +198,7 @@ async fn metadata_should_return_metadata_about_a_file(#[future] session: Session
let metadata = session
.sftp()
.metadata(file.path())
.metadata(file.path().to_path_buf())
.await
.expect("Failed to get metadata for file");
@ -215,7 +218,7 @@ async fn metadata_should_return_metadata_about_a_directory(#[future] session: Se
let metadata = session
.sftp()
.metadata(dir.path())
.metadata(dir.path().to_path_buf())
.await
.expect("Failed to get metadata for dir");
@ -240,7 +243,7 @@ async fn metadata_should_return_metadata_about_the_file_pointed_to_by_a_symlink(
let metadata = session
.sftp()
.metadata(link.path())
.metadata(link.path().to_path_buf())
.await
.expect("Failed to get metadata for symlink");
@ -267,7 +270,7 @@ async fn metadata_should_return_metadata_about_the_dir_pointed_to_by_a_symlink(
let metadata = session
.sftp()
.metadata(link.path())
.metadata(link.path().to_path_buf())
.await
.expect("Failed to get metadata for symlink");
@ -285,7 +288,10 @@ async fn metadata_should_fail_if_path_missing(#[future] session: Session) {
let temp = TempDir::new().unwrap();
let result = session.sftp().metadata(temp.child("missing").path()).await;
let result = session
.sftp()
.metadata(temp.child("missing").path().to_path_buf())
.await;
assert!(
result.is_err(),
"Metadata unexpectedly succeeded: {:?}",
@ -305,7 +311,7 @@ async fn symlink_metadata_should_return_metadata_about_a_file(#[future] session:
let symlink_metadata = session
.sftp()
.symlink_metadata(file.path())
.symlink_metadata(file.path().to_path_buf())
.await
.expect("Failed to get metadata for file");
@ -325,7 +331,7 @@ async fn symlink_metadata_should_return_metadata_about_a_directory(#[future] ses
let symlink_metadata = session
.sftp()
.symlink_metadata(dir.path())
.symlink_metadata(dir.path().to_path_buf())
.await
.expect("Failed to metadata for dir");
@ -350,7 +356,7 @@ async fn symlink_metadata_should_return_metadata_about_symlink_pointing_to_a_fil
let metadata = session
.sftp()
.symlink_metadata(link.path())
.symlink_metadata(link.path().to_path_buf())
.await
.expect("Failed to get metadata for symlink");
@ -377,7 +383,7 @@ async fn symlink_metadata_should_return_metadata_about_symlink_pointing_to_a_dir
let metadata = session
.sftp()
.symlink_metadata(link.path())
.symlink_metadata(link.path().to_path_buf())
.await
.expect("Failed to get metadata for symlink");
@ -397,7 +403,7 @@ async fn symlink_metadata_should_fail_if_path_missing(#[future] session: Session
let result = session
.sftp()
.symlink_metadata(temp.child("missing").path())
.symlink_metadata(temp.child("missing").path().to_path_buf())
.await;
assert!(
result.is_err(),
@ -420,7 +426,7 @@ async fn symlink_should_create_symlink_pointing_to_file(#[future] session: Sessi
session
.sftp()
.symlink(file.path(), link.path())
.symlink(file.path().to_path_buf(), link.path().to_path_buf())
.await
.expect("Failed to create symlink");
@ -451,7 +457,7 @@ async fn symlink_should_create_symlink_pointing_to_directory(#[future] session:
session
.sftp()
.symlink(dir.path(), link.path())
.symlink(dir.path().to_path_buf(), link.path().to_path_buf())
.await
.expect("Failed to create symlink");
@ -471,7 +477,7 @@ async fn symlink_should_succeed_even_if_path_missing(#[future] session: Session)
session
.sftp()
.symlink(file.path(), link.path())
.symlink(file.path().to_path_buf(), link.path().to_path_buf())
.await
.expect("Failed to create symlink");
@ -494,7 +500,7 @@ async fn read_link_should_return_the_target_of_the_symlink(#[future] session: Se
let path = session
.sftp()
.read_link(link.path())
.read_link(link.path().to_path_buf())
.await
.expect("Failed to read symlink");
assert_eq!(path, dir.path());
@ -507,7 +513,7 @@ async fn read_link_should_return_the_target_of_the_symlink(#[future] session: Se
let path = session
.sftp()
.read_link(link.path())
.read_link(link.path().to_path_buf())
.await
.expect("Failed to read symlink");
assert_eq!(path, file.path());
@ -522,7 +528,10 @@ async fn read_link_should_fail_if_path_is_not_a_symlink(#[future] session: Sessi
let temp = TempDir::new().unwrap();
// Test missing path
let result = session.sftp().read_link(temp.child("missing").path()).await;
let result = session
.sftp()
.read_link(temp.child("missing").path().to_path_buf())
.await;
assert!(
result.is_err(),
"Unexpectedly read link for missing path: {:?}",
@ -532,7 +541,7 @@ async fn read_link_should_fail_if_path_is_not_a_symlink(#[future] session: Sessi
// Test a directory
let dir = temp.child("dir");
dir.create_dir_all().unwrap();
let result = session.sftp().read_link(dir.path()).await;
let result = session.sftp().read_link(dir.path().to_path_buf()).await;
assert!(
result.is_err(),
"Unexpectedly read link for directory: {:?}",
@ -542,7 +551,7 @@ async fn read_link_should_fail_if_path_is_not_a_symlink(#[future] session: Sessi
// Test a file
let file = temp.child("file");
file.touch().unwrap();
let result = session.sftp().read_link(file.path()).await;
let result = session.sftp().read_link(file.path().to_path_buf()).await;
assert!(
result.is_err(),
"Unexpectedly read link for file: {:?}",
@ -568,7 +577,7 @@ async fn canonicalize_should_resolve_absolute_path_for_relative_path(#[future] s
// on mac the /tmp dir is a symlink to /private/tmp; so, we cannot successfully
// check the accuracy of the path itself, meaning that we can only validate
// that the operation was okay.
let result = session.sftp().canonicalize(rel.path()).await;
let result = session.sftp().canonicalize(rel.path().to_path_buf()).await;
assert!(
result.is_ok(),
"Canonicalize unexpectedly failed: {:?}",
@ -596,7 +605,10 @@ async fn canonicalize_should_either_return_resolved_path_or_error_if_missing(
// Additionally, this has divergent behavior. On some platforms, this returns
// the path as is whereas on others this returns a missing path error. We
// have to support both checks.
let result = session.sftp().canonicalize(missing.path()).await;
let result = session
.sftp()
.canonicalize(missing.path().to_path_buf())
.await;
match result {
Ok(_) => {}
Err(SftpChannelError::Sftp(SftpError::NoSuchFile)) => {}
@ -613,7 +625,10 @@ async fn canonicalize_should_fail_if_resolving_missing_path_with_dots(#[future]
let temp = TempDir::new().unwrap();
let missing = temp.child(".").child("hello").child("..").child("world");
let result = session.sftp().canonicalize(missing.path()).await;
let result = session
.sftp()
.canonicalize(missing.path().to_path_buf())
.await;
assert!(result.is_err(), "Canonicalize unexpectedly succeeded");
}
@ -631,7 +646,11 @@ async fn rename_should_support_singular_file(#[future] session: Session) {
session
.sftp()
.rename(file.path(), dst.path(), Default::default())
.rename(
file.path().to_path_buf(),
dst.path().to_path_buf(),
Default::default(),
)
.await
.expect("Failed to rename file");
@ -658,7 +677,11 @@ async fn rename_should_support_dirtectory(#[future] session: Session) {
session
.sftp()
.rename(dir.path(), dst.path(), Default::default())
.rename(
dir.path().to_path_buf(),
dst.path().to_path_buf(),
Default::default(),
)
.await
.expect("Failed to rename directory");
@ -684,7 +707,11 @@ async fn rename_should_fail_if_source_path_missing(#[future] session: Session) {
let result = session
.sftp()
.rename(missing.path(), dst.path(), Default::default())
.rename(
missing.path().to_path_buf(),
dst.path().to_path_buf(),
Default::default(),
)
.await;
assert!(
result.is_err(),
@ -705,7 +732,7 @@ async fn remove_file_should_remove_file(#[future] session: Session) {
session
.sftp()
.remove_file(file.path())
.remove_file(file.path().to_path_buf())
.await
.expect("Failed to remove file");
@ -726,7 +753,7 @@ async fn remove_file_should_remove_symlink_to_file(#[future] session: Session) {
session
.sftp()
.remove_file(link.path())
.remove_file(link.path().to_path_buf())
.await
.expect("Failed to remove symlink");
@ -749,7 +776,7 @@ async fn remove_file_should_remove_symlink_to_directory(#[future] session: Sessi
session
.sftp()
.remove_file(link.path())
.remove_file(link.path().to_path_buf())
.await
.expect("Failed to remove symlink");
@ -768,7 +795,7 @@ async fn remove_file_should_fail_if_path_to_directory(#[future] session: Session
let dir = temp.child("dir");
dir.create_dir_all().unwrap();
let result = session.sftp().remove_file(dir.path()).await;
let result = session.sftp().remove_file(dir.path().to_path_buf()).await;
assert!(
result.is_err(),
"Unexpectedly removed directory: {:?}",
@ -789,7 +816,7 @@ async fn remove_file_should_fail_if_path_missing(#[future] session: Session) {
let result = session
.sftp()
.remove_file(temp.child("missing").path())
.remove_file(temp.child("missing").path().to_path_buf())
.await;
assert!(
result.is_err(),

View File

@ -2,6 +2,7 @@ use crate::sshd::session;
use assert_fs::{prelude::*, TempDir};
use rstest::*;
use smol::io::{AsyncReadExt, AsyncWriteExt};
use std::convert::TryInto;
use std::path::PathBuf;
use wezterm_ssh::Session;
@ -17,7 +18,7 @@ async fn metadata_should_retrieve_file_stat(#[future] session: Session) {
let remote_file = session
.sftp()
.open(file.path())
.open(file.path().to_path_buf())
.await
.expect("Failed to open remote file");
@ -46,7 +47,7 @@ async fn read_dir_should_retrieve_next_dir_entry(#[future] session: Session) {
let remote_dir = session
.sftp()
.open_dir(temp.path())
.open_dir(temp.path().to_path_buf())
.await
.expect("Failed to open remote directory");
@ -70,11 +71,11 @@ async fn read_dir_should_retrieve_next_dir_entry(#[future] session: Session) {
assert_eq!(
contents,
vec![
(PathBuf::from("."), "dir"),
(PathBuf::from(".."), "dir"),
(PathBuf::from("dir"), "dir"),
(PathBuf::from("file"), "file"),
(PathBuf::from("link"), "symlink"),
(PathBuf::from(".").try_into().unwrap(), "dir"),
(PathBuf::from("..").try_into().unwrap(), "dir"),
(PathBuf::from("dir").try_into().unwrap(), "dir"),
(PathBuf::from("file").try_into().unwrap(), "file"),
(PathBuf::from("link").try_into().unwrap(), "symlink"),
]
);
}
@ -91,7 +92,7 @@ async fn should_support_async_reading(#[future] session: Session) {
let mut remote_file = session
.sftp()
.open(file.path())
.open(file.path().to_path_buf())
.await
.expect("Failed to open remote file");
@ -123,7 +124,7 @@ async fn should_support_async_writing(#[future] session: Session) {
let mut remote_file = session
.sftp()
.create(file.path())
.create(file.path().to_path_buf())
.await
.expect("Failed to open remote file");
@ -153,7 +154,7 @@ async fn should_support_async_flush(#[future] session: Session) {
let mut remote_file = session
.sftp()
.create(file.path())
.create(file.path().to_path_buf())
.await
.expect("Failed to open remote file");
@ -178,7 +179,7 @@ async fn should_support_async_close(#[future] session: Session) {
let mut remote_file = session
.sftp()
.create(file.path())
.create(file.path().to_path_buf())
.await
.expect("Failed to open remote file");