Skip to content

Commit

Permalink
Fix scp_recv ABI issue on Windows
Browse files Browse the repository at this point in the history
* Adopt scp_recv2 instead, which uses compatible 64-bit stat types
* Mark scp_recv as deprecated
* small version bump

Fixes #109
Refs #117

Co-authored-by: Joyce Babu <[email protected]>
  • Loading branch information
wez and JoyceBabu committed Jul 31, 2019
1 parent ff79079 commit 3aa8096
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion libssh2-sys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "libssh2-sys"
version = "0.2.11"
version = "0.2.12"
authors = ["Alex Crichton <[email protected]>"]
links = "ssh2"
build = "build.rs"
Expand Down
26 changes: 26 additions & 0 deletions libssh2-sys/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 }))
}
}

Expand Down
11 changes: 10 additions & 1 deletion systest/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
})
Expand Down

0 comments on commit 3aa8096

Please sign in to comment.