From 3aa8096bc4e1c6c762f8553c516b5ce099ba5f71 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Mon, 29 Jul 2019 14:03:34 -0700 Subject: Fix scp_recv ABI issue on Windows * Adopt scp_recv2 instead, which uses compatible 64-bit stat types * Mark scp_recv as deprecated * small version bump Fixes https://github.com/alexcrichton/ssh2-rs/issues/109 Refs https://github.com/alexcrichton/ssh2-rs/pull/117 Co-authored-by: Joyce Babu --- Cargo.toml | 2 +- libssh2-sys/Cargo.toml | 2 +- libssh2-sys/lib.rs | 26 ++++++++++++++++++++++++++ src/session.rs | 6 +++--- systest/build.rs | 11 ++++++++++- 5 files changed, 41 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5f93fcc..731ebb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ vendored-openssl = ["libssh2-sys/vendored-openssl"] [dependencies] bitflags = "1.0.4" libc = "0.2" -libssh2-sys = { path = "libssh2-sys", version = "0.2.4" } +libssh2-sys = { path = "libssh2-sys", version = "0.2.12" } [dev-dependencies] tempdir = "0.3" diff --git a/libssh2-sys/Cargo.toml b/libssh2-sys/Cargo.toml index 4dc882a..06a7caa 100644 --- a/libssh2-sys/Cargo.toml +++ b/libssh2-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libssh2-sys" -version = "0.2.11" +version = "0.2.12" authors = ["Alex Crichton "] links = "ssh2" build = "build.rs" diff --git a/libssh2-sys/lib.rs b/libssh2-sys/lib.rs index e57864f..5ce1dc2 100644 --- a/libssh2-sys/lib.rs +++ b/libssh2-sys/lib.rs @@ -192,6 +192,24 @@ pub enum LIBSSH2_SFTP_HANDLE {} pub type libssh2_int64_t = i64; pub type libssh2_uint64_t = u64; +// libssh2_struct_stat is a typedef for libc::stat on all platforms, however, +// Windows has a bunch of legacy around struct stat that makes things more +// complicated to validate with systest. +// The most reasonable looking solution to this is a newtype that derefs +// to libc::stat. +// We cannot use `pub struct libssh2_struct_stat(pub libc::stat)` because +// that triggers a `no tuple structs in FFI` error. +#[repr(C)] +pub struct libssh2_struct_stat(libc::stat); + +impl std::ops::Deref for libssh2_struct_stat { + type Target = libc::stat; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + #[repr(C)] pub struct libssh2_agent_publickey { pub magic: c_uint, @@ -548,11 +566,19 @@ extern "C" { pub fn libssh2_knownhost_init(sess: *mut LIBSSH2_SESSION) -> *mut LIBSSH2_KNOWNHOSTS; // scp + #[deprecated(note = "dangerously unsafe on windows, use libssh2_scp_recv2 instead")] pub fn libssh2_scp_recv( sess: *mut LIBSSH2_SESSION, path: *const c_char, sb: *mut libc::stat, ) -> *mut LIBSSH2_CHANNEL; + + pub fn libssh2_scp_recv2( + sess: *mut LIBSSH2_SESSION, + path: *const c_char, + sb: *mut libssh2_struct_stat, + ) -> *mut LIBSSH2_CHANNEL; + pub fn libssh2_scp_send64( sess: *mut LIBSSH2_SESSION, path: *const c_char, diff --git a/src/session.rs b/src/session.rs index 3ae3473..bce81ad 100644 --- a/src/session.rs +++ b/src/session.rs @@ -533,8 +533,8 @@ impl Session { pub fn scp_recv(&self, path: &Path) -> Result<(Channel, ScpFileStat), Error> { let path = try!(CString::new(try!(util::path2bytes(path)))); unsafe { - let mut sb: libc::stat = mem::zeroed(); - let ret = raw::libssh2_scp_recv(self.inner.raw, path.as_ptr(), &mut sb); + let mut sb: raw::libssh2_struct_stat = mem::zeroed(); + let ret = raw::libssh2_scp_recv2(self.inner.raw, path.as_ptr(), &mut sb); let mut c = Channel::from_raw_opt(ret, &self.inner)?; // Hm, apparently when we scp_recv() a file the actual channel @@ -543,7 +543,7 @@ impl Session { // artificially limit the channel to a certain amount of bytes that // can be read. c.limit_read(sb.st_size as u64); - Ok((c, ScpFileStat { stat: sb })) + Ok((c, ScpFileStat { stat: *sb })) } } diff --git a/systest/build.rs b/systest/build.rs index 6131643..7d5c281 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -9,9 +9,18 @@ fn main() { .header("libssh2_sftp.h") .include(env::var("DEP_SSH2_INCLUDE").unwrap()) .type_name(|s, is_struct| { - if (is_struct || s == "stat") && !s.starts_with("LIB") { + if s == "stat" { + // Ensure that we emit `struct stat` rather than just a `stat` typedef. + format!("struct stat") + } else if s == "libssh2_struct_stat" { + // libssh2_struct_stat is a typedef so ensure that we don't emit + // `struct libssh2_struct_stat` in the C code we generate + s.to_string() + } else if is_struct && !s.starts_with("LIB") { + // Otherwise we prefer to emit `struct foo` unless the type is `LIB_XXX` format!("struct {}", s) } else { + // All other cases: just emit the type name s.to_string() } }) -- cgit v1.2.3