diff --git a/Cargo.lock b/Cargo.lock index 0ef16332..061b2dda 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -599,9 +599,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", "clap_derive", @@ -609,9 +609,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", @@ -2106,9 +2106,9 @@ checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" [[package]] name = "libc" -version = "0.2.178" +version = "0.2.179" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f" [[package]] name = "libcrux-intrinsics" @@ -2889,9 +2889,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.4" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" +checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" dependencies = [ "memchr", "ucd-trie", @@ -2899,9 +2899,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.4" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" +checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" dependencies = [ "pest", "pest_generator", @@ -2909,9 +2909,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.4" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" +checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" dependencies = [ "pest", "pest_meta", @@ -2922,9 +2922,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.4" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" +checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" dependencies = [ "pest", "sha2 0.10.9", @@ -4412,9 +4412,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.48.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ "bytes", "libc", @@ -5933,9 +5933,9 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de9211a9f64b825911bdf0240f58b7a8dac217fe260fc61f080a07f61372fbd5" +checksum = "317f17ff091ac4515f17cc7a190d2769a8c9a96d227de5d64b500b01cda8f2cd" [[package]] name = "zune-core" diff --git a/Cargo.toml b/Cargo.toml index e564fbac..90cb3cc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ ansi-to-tui = "8.0.0" anyhow = "1.0.100" base64 = "0.22.1" bitflags = { version = "2.10.0", features = [ "serde" ] } -clap = { version = "4.5.53", features = [ "derive" ] } +clap = { version = "4.5.54", features = [ "derive" ] } core-foundation-sys = "0.8.7" crossterm = { version = "0.29.0", features = [ "event-stream" ] } dirs = "6.0.0" @@ -39,7 +39,7 @@ futures = "0.3.31" globset = "0.4.18" hashbrown = { version = "0.16.1", features = [ "serde" ] } indexmap = { version = "2.12.1", features = [ "serde" ] } -libc = "0.2.178" +libc = "0.2.179" lru = "0.16.2" mlua = { version = "0.11.5", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serde" ] } objc2 = "0.6.3" @@ -56,7 +56,7 @@ serde = { version = "1.0.228", features = [ "derive" ] } serde_json = "1.0.148" syntect = { version = "5.3.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } thiserror = "2.0.17" -tokio = { version = "1.48.0", features = [ "full" ] } +tokio = { version = "1.49.0", features = [ "full" ] } tokio-stream = "0.1.17" tokio-util = "0.7.17" toml = { version = "0.9.10" } diff --git a/yazi-sftp/src/error.rs b/yazi-sftp/src/error.rs index 0fe10a89..6e84f1a0 100644 --- a/yazi-sftp/src/error.rs +++ b/yazi-sftp/src/error.rs @@ -42,6 +42,11 @@ impl From for std::io::Error { responses::StatusCode::NoConnection => Self::from(std::io::ErrorKind::NotConnected), responses::StatusCode::ConnectionLost => Self::from(std::io::ErrorKind::ConnectionReset), responses::StatusCode::OpUnsupported => Self::from(std::io::ErrorKind::Unsupported), + responses::StatusCode::InvalidHandle => Self::from(std::io::ErrorKind::InvalidInput), + responses::StatusCode::NoSuchPath => Self::from(std::io::ErrorKind::NotFound), + responses::StatusCode::FileAlreadyExists => Self::from(std::io::ErrorKind::AlreadyExists), + responses::StatusCode::WriteProtect => Self::from(std::io::ErrorKind::PermissionDenied), + responses::StatusCode::NoMedia => Self::from(std::io::ErrorKind::Other), }, Error::Packet(e) => Self::new(std::io::ErrorKind::InvalidData, e), Error::Timeout => Self::from(std::io::ErrorKind::TimedOut), diff --git a/yazi-sftp/src/responses/status.rs b/yazi-sftp/src/responses/status.rs index 137267df..37d83432 100644 --- a/yazi-sftp/src/responses/status.rs +++ b/yazi-sftp/src/responses/status.rs @@ -28,6 +28,8 @@ impl Status { pub fn is_eof(&self) -> bool { self.code == StatusCode::Eof } + pub fn is_failure(&self) -> bool { self.code == StatusCode::Failure } + pub(crate) fn connection_lost(id: u32) -> Self { Self { id, @@ -40,13 +42,18 @@ impl Status { #[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)] pub enum StatusCode { - Ok = 0, - Eof = 1, - NoSuchFile = 2, - PermissionDenied = 3, - Failure = 4, - BadMessage = 5, - NoConnection = 6, - ConnectionLost = 7, - OpUnsupported = 8, + Ok = 0, + Eof = 1, + NoSuchFile = 2, + PermissionDenied = 3, + Failure = 4, + BadMessage = 5, + NoConnection = 6, + ConnectionLost = 7, + OpUnsupported = 8, + InvalidHandle = 9, + NoSuchPath = 10, + FileAlreadyExists = 11, + WriteProtect = 12, + NoMedia = 13, } diff --git a/yazi-vfs/src/provider/sftp/gate.rs b/yazi-vfs/src/provider/sftp/gate.rs index c0588d5d..5ad087c0 100644 --- a/yazi-vfs/src/provider/sftp/gate.rs +++ b/yazi-vfs/src/provider/sftp/gate.rs @@ -68,9 +68,20 @@ impl FileBuilder for Gate { _ => Err(io::Error::new(io::ErrorKind::InvalidInput, format!("Not an SFTP URL: {url:?}")))?, }; + let conn = Conn { name, config }.roll().await?; let flags = Flags::from(*self); let attrs = super::Attrs(self.0.attrs).try_into().unwrap_or_default(); - Ok(Conn { name, config }.roll().await?.open(path, flags, &attrs).await?) + + let result = conn.open(path, flags, &attrs).await; + if self.0.create_new + && let Err(yazi_sftp::Error::Status(status)) = &result + && status.is_failure() + && conn.lstat(path).await.is_ok() + { + return Err(io::Error::from(io::ErrorKind::AlreadyExists)); + } + + Ok(result?) } fn read(&mut self, read: bool) -> &mut Self { diff --git a/yazi-vfs/src/provider/sftp/sftp.rs b/yazi-vfs/src/provider/sftp/sftp.rs index bc309947..364e97ed 100644 --- a/yazi-vfs/src/provider/sftp/sftp.rs +++ b/yazi-vfs/src/provider/sftp/sftp.rs @@ -117,7 +117,17 @@ impl<'a> Provider for Sftp<'a> { } async fn create_dir(&self) -> io::Result<()> { - Ok(self.op().await?.mkdir(self.path, Attrs::default()).await?) + let op = self.op().await?; + let result = op.mkdir(self.path, Attrs::default()).await; + + if let Err(yazi_sftp::Error::Status(status)) = &result + && status.is_failure() + && op.lstat(self.path).await.is_ok() + { + return Err(io::Error::from(io::ErrorKind::AlreadyExists)); + } + + Ok(result?) } async fn hard_link

(&self, to: P) -> io::Result<()>