diff options
author | Alex Crichton <alex@alexcrichton.com> | 2015-02-23 18:18:06 -0800 |
---|---|---|
committer | Alex Crichton <alex@alexcrichton.com> | 2015-02-23 18:18:06 -0800 |
commit | a062f8704731821b591887f27e7ce9fb7515b29f (patch) | |
tree | 21219352b1dee6f425780b0a476d961c30127a7b /src | |
parent | c4c46c2d68dd207371c0565e2de2439bac583edc (diff) | |
download | ssh2-rs-a062f8704731821b591887f27e7ce9fb7515b29f.zip |
Update to std::io
Diffstat (limited to 'src')
-rw-r--r-- | src/channel.rs | 52 | ||||
-rw-r--r-- | src/knownhosts.rs | 13 | ||||
-rw-r--r-- | src/lib.rs | 36 | ||||
-rw-r--r-- | src/session.rs | 140 | ||||
-rw-r--r-- | src/sftp.rs | 157 | ||||
-rw-r--r-- | src/util.rs | 28 |
6 files changed, 237 insertions, 189 deletions
diff --git a/src/channel.rs b/src/channel.rs index e415af7..a27afa1 100644 --- a/src/channel.rs +++ b/src/channel.rs @@ -1,5 +1,6 @@ +use std::io::prelude::*; +use std::io::{self, ErrorKind}; use std::cmp; -use std::old_io; use libc::{c_uint, c_int, size_t, c_char, c_void, c_uchar}; use {raw, Session, Error}; @@ -119,11 +120,14 @@ impl<'sess> Channel<'sess> { /// # Example /// /// ```no_run + /// # use std::io::prelude::*; /// # use ssh2::Session; /// # let session: Session = panic!(); /// let mut channel = session.channel_session().unwrap(); /// channel.exec("ls").unwrap(); - /// println!("{}", channel.read_to_string().unwrap()); + /// let mut s = String::new(); + /// channel.read_to_string(&mut s).unwrap(); + /// println!("{}", s); /// ``` pub fn exec(&mut self, command: &str) -> Result<(), Error> { self.process_startup("exec", Some(command)) @@ -181,7 +185,7 @@ impl<'sess> Channel<'sess> { } /// Write data to the channel stderr stream. - pub fn write_stderr(&mut self, data: &[u8]) -> Result<(), Error> { + pub fn write_stderr(&mut self, data: &[u8]) -> Result<usize, Error> { self.write_stream(::EXTENDED_DATA_STDERR, data) } @@ -192,13 +196,13 @@ impl<'sess> Channel<'sess> { /// selected stream_id. The SSH2 protocol currently defines a stream ID of 1 /// to be the stderr substream. pub fn write_stream(&mut self, stream_id: i32, data: &[u8]) - -> Result<(), Error> { + -> Result<usize, Error> { unsafe { let rc = raw::libssh2_channel_write_ex(self.raw, stream_id as c_int, data.as_ptr() as *mut _, data.len() as size_t); - self.sess.rc(rc) + self.sess.rc(rc).map(|()| rc as usize) } } @@ -215,7 +219,7 @@ impl<'sess> Channel<'sess> { /// to be the stderr substream. pub fn read_stream(&mut self, stream_id: i32, data: &mut [u8]) -> Result<usize, Error> { - if self.eof() { return Err(Error::eof()) } + if self.eof() { return Ok(0) } let data = match self.read_limit { Some(amt) => { @@ -230,7 +234,6 @@ impl<'sess> Channel<'sess> { data.as_mut_ptr() as *mut _, data.len() as size_t); if rc < 0 { try!(self.sess.rc(rc)); } - if rc == 0 && self.eof() { return Err(Error::eof()) } match self.read_limit { Some(ref mut amt) => *amt -= rc as u64, None => {} @@ -397,40 +400,27 @@ impl<'sess> SessionBinding<'sess> for Channel<'sess> { fn raw(&self) -> *mut raw::LIBSSH2_CHANNEL { self.raw } } -impl<'sess> Writer for Channel<'sess> { - fn write_all(&mut self, buf: &[u8]) -> old_io::IoResult<()> { +impl<'sess> Write for Channel<'sess> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.write_stream(0, buf).map_err(|e| { - old_io::IoError { - kind: old_io::OtherIoError, - desc: "ssh write error", - detail: Some(e.to_string()), - } + io::Error::new(ErrorKind::Other, "ssh write error", + Some(e.to_string())) }) } - fn flush(&mut self) -> old_io::IoResult<()> { + fn flush(&mut self) -> io::Result<()> { self.flush_stream(0).map_err(|e| { - old_io::IoError { - kind: old_io::OtherIoError, - desc: "ssh write error", - detail: Some(e.to_string()), - } + io::Error::new(ErrorKind::Other, "ssh write error", + Some(e.to_string())) }) } } -impl<'sess> Reader for Channel<'sess> { - fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<usize> { +impl<'sess> Read for Channel<'sess> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.read_stream(0, buf).map_err(|e| { - if self.eof() { - old_io::standard_error(old_io::EndOfFile) - } else { - old_io::IoError { - kind: old_io::OtherIoError, - desc: "ssh read error", - detail: Some(e.to_string()), - } - } + io::Error::new(ErrorKind::Other, "ssh read error", + Some(e.to_string())) }) } } diff --git a/src/knownhosts.rs b/src/knownhosts.rs index 171c758..20d18a1 100644 --- a/src/knownhosts.rs +++ b/src/knownhosts.rs @@ -1,10 +1,11 @@ use std::ffi::CString; use std::marker; +use std::path::Path; use std::str; use libc::{c_int, size_t}; use {raw, Session, Error, KnownHostFileKind, CheckResult}; -use util::{Binding, SessionBinding}; +use util::{self, Binding, SessionBinding}; /// A set of known hosts which can be used to verify the identity of a remote /// server. @@ -12,8 +13,8 @@ use util::{Binding, SessionBinding}; /// # Example /// /// ```no_run -/// # #![allow(unstable)] -/// use std::os; +/// use std::env; +/// use std::path::Path; /// use ssh2::{self, CheckResult, HostKeyType, KnownHostKeyFormat}; /// use ssh2::KnownHostFileKind; /// @@ -21,7 +22,7 @@ use util::{Binding, SessionBinding}; /// let mut known_hosts = session.known_hosts().unwrap(); /// /// // Initialize the known hosts with a global known hosts file -/// let file = Path::new(os::getenv("HOME").unwrap()).join(".ssh/known_hosts"); +/// let file = Path::new(&env::var("HOME").unwrap()).join(".ssh/known_hosts"); /// known_hosts.read_file(&file, KnownHostFileKind::OpenSSH).unwrap(); /// /// // Now check to see if the seesion's host key is anywhere in the known @@ -68,7 +69,7 @@ impl<'sess> KnownHosts<'sess> { /// the collection of known hosts. pub fn read_file(&mut self, file: &Path, kind: KnownHostFileKind) -> Result<u32, Error> { - let file = try!(CString::new(file.as_vec())); + let file = try!(CString::new(try!(util::path2bytes(file)))); let n = unsafe { raw::libssh2_knownhost_readfile(self.raw, file.as_ptr(), kind as c_int) @@ -92,7 +93,7 @@ impl<'sess> KnownHosts<'sess> { /// file format. pub fn write_file(&self, file: &Path, kind: KnownHostFileKind) -> Result<(), Error> { - let file = try!(CString::new(file.as_vec())); + let file = try!(CString::new(try!(util::path2bytes(file)))); let n = unsafe { raw::libssh2_knownhost_writefile(self.raw, file.as_ptr(), kind as c_int) @@ -32,7 +32,7 @@ //! ## Authenticating with ssh-agent //! //! ```no_run -//! use std::old_io::TcpStream; +//! use std::net::TcpStream; //! use ssh2::Session; //! //! // Connect to the local SSH server @@ -50,7 +50,7 @@ //! ## Authenticating with a password //! //! ```no_run -//! use std::old_io::TcpStream; +//! use std::net::TcpStream; //! use ssh2::Session; //! //! // Connect to the local SSH server @@ -65,7 +65,8 @@ //! ## Run a command //! //! ```no_run -//! use std::old_io::{self, TcpStream}; +//! use std::io::prelude::*; +//! use std::net::{TcpStream}; //! use ssh2::Session; //! //! // Connect to the local SSH server @@ -76,14 +77,18 @@ //! //! let mut channel = sess.channel_session().unwrap(); //! channel.exec("ls").unwrap(); -//! println!("{}", channel.read_to_string().unwrap()); +//! let mut s = String::new(); +//! channel.read_to_string(&mut s).unwrap(); +//! println!("{}", s); //! println!("{}", channel.exit_status().unwrap()); //! ``` //! //! ## Upload a file //! //! ```no_run -//! use std::old_io::{self, TcpStream}; +//! use std::io::prelude::*; +//! use std::net::TcpStream; +//! use std::path::Path; //! use ssh2::Session; //! //! // Connect to the local SSH server @@ -92,15 +97,17 @@ //! sess.handshake(&tcp).unwrap(); //! sess.userauth_agent("username").unwrap(); //! -//! let mut remote_file = sess.scp_send(&Path::new("remote"), -//! old_io::USER_FILE, 10, None).unwrap(); +//! let mut remote_file = sess.scp_send(Path::new("remote"), +//! 0o644, 10, None).unwrap(); //! remote_file.write(b"1234567890").unwrap(); //! ``` //! //! ## Download a file //! //! ```no_run -//! use std::old_io::TcpStream; +//! use std::io::prelude::*; +//! use std::net::TcpStream; +//! use std::path::Path; //! use ssh2::Session; //! //! // Connect to the local SSH server @@ -109,13 +116,14 @@ //! sess.handshake(&tcp).unwrap(); //! sess.userauth_agent("username").unwrap(); //! -//! let (mut remote_file, stat) = sess.scp_recv(&Path::new("remote")).unwrap(); -//! println!("remote file size: {}", stat.size); -//! let contents = remote_file.read_to_end(); +//! let (mut remote_file, stat) = sess.scp_recv(Path::new("remote")).unwrap(); +//! println!("remote file size: {}", stat.size()); +//! let mut contents = Vec::new(); +//! remote_file.read_to_end(&mut contents).unwrap(); +//! // ... //! ``` -#![feature(unsafe_destructor, std_misc, collections, old_io, core, old_path)] -#![feature(io)] +#![feature(unsafe_destructor, std_misc, collections, io, core, path, os, net)] #![deny(missing_docs, unused_results)] #![cfg_attr(test, deny(warnings))] @@ -131,7 +139,7 @@ pub use channel::{Channel, ExitSignal, ReadWindow, WriteWindow}; pub use error::Error; pub use knownhosts::{KnownHosts, Hosts, Host}; pub use listener::Listener; -pub use session::Session; +pub use session::{Session, ScpFileStat}; pub use sftp::{Sftp, OpenFlags, READ, WRITE, APPEND, CREATE, TRUNCATE}; pub use sftp::{EXCLUSIVE, OpenType, File, FileStat}; pub use sftp::{RenameFlags, ATOMIC, OVERWRITE, NATIVE}; diff --git a/src/session.rs b/src/session.rs index 81ed002..c2dfc03 100644 --- a/src/session.rs +++ b/src/session.rs @@ -1,13 +1,14 @@ use std::ffi::CString; -use std::old_io::{self, TcpStream}; use std::mem; +use std::net::TcpStream; +use std::path::Path; use std::slice; use std::str; use libc::{self, c_uint, c_int, c_void, c_long}; use {raw, Error, DisconnectCode, ByApplication, HostKeyType}; use {MethodType, Agent, Channel, Listener, HashType, KnownHosts, Sftp}; -use util::{Binding, SessionBinding}; +use util::{self, Binding, SessionBinding}; /// An SSH session, typically representing one TCP connection. /// @@ -20,6 +21,11 @@ pub struct Session { unsafe impl Send for Session {} +/// Metadata returned about a remote file when received via `scp`. +pub struct ScpFileStat { + stat: libc::stat, +} + impl Session { /// Initializes an SSH session object. /// @@ -193,10 +199,10 @@ impl Session { privatekey: &Path, passphrase: Option<&str>) -> Result<(), Error> { let pubkey = match pubkey { - Some(s) => Some(try!(CString::new(s.as_vec()))), + Some(s) => Some(try!(CString::new(try!(util::path2bytes(s))))), None => None, }; - let privatekey = try!(CString::new(privatekey.as_vec())); + let privatekey = try!(CString::new(try!(util::path2bytes(privatekey)))); let passphrase = match passphrase { Some(s) => Some(try!(CString::new(s))), None => None, @@ -222,8 +228,8 @@ impl Session { hostname: &str, local_username: Option<&str>) -> Result<(), Error> { - let publickey = try!(CString::new(publickey.as_vec())); - let privatekey = try!(CString::new(privatekey.as_vec())); + let publickey = try!(CString::new(try!(util::path2bytes(publickey)))); + let privatekey = try!(CString::new(try!(util::path2bytes(privatekey)))); let passphrase = match passphrase { Some(s) => Some(try!(CString::new(s))), None => None, @@ -415,8 +421,8 @@ impl Session { /// sent over the returned channel. Some stat information is also returned /// about the remote file to prepare for receiving the file. pub fn scp_recv(&self, path: &Path) - -> Result<(Channel, old_io::FileStat), Error> { - let path = try!(CString::new(path.as_vec())); + -> 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.raw, path.as_ptr(), &mut sb); @@ -428,7 +434,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, mkstat(&sb))) + Ok((c, ScpFileStat { stat: sb })) } } @@ -440,15 +446,15 @@ impl Session { /// /// The size of the file, `size`, must be known ahead of time before /// transmission. - pub fn scp_send(&self, remote_path: &Path, mode: old_io::FilePermission, + pub fn scp_send(&self, remote_path: &Path, mode: i32, size: u64, times: Option<(u64, u64)>) -> Result<Channel, Error> { - let path = try!(CString::new(remote_path.as_vec())); + let path = try!(CString::new(try!(util::path2bytes(remote_path)))); let (mtime, atime) = times.unwrap_or((0, 0)); unsafe { let ret = raw::libssh2_scp_send64(self.raw, path.as_ptr(), - mode.bits() as c_int, + mode as c_int, size, mtime as libc::time_t, atime as libc::time_t); @@ -618,57 +624,69 @@ impl Binding for Session { fn raw(&self) -> *mut raw::LIBSSH2_SESSION { self.raw } } -// Sure do wish this was exported in libnative! -fn mkstat(stat: &libc::stat) -> old_io::FileStat { - #[cfg(windows)] type Mode = libc::c_int; - #[cfg(unix)] type Mode = libc::mode_t; - - // FileStat times are in milliseconds - fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 } - - #[cfg(all(not(target_os = "linux"), not(target_os = "android")))] - fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 } - #[cfg(any(target_os = "linux", target_os = "android"))] - fn flags(_stat: &libc::stat) -> u64 { 0 } - - #[cfg(all(not(target_os = "linux"), not(target_os = "android")))] - fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 } - #[cfg(any(target_os = "linux", target_os = "android"))] - fn gen(_stat: &libc::stat) -> u64 { 0 } - - old_io::FileStat { - size: stat.st_size as u64, - kind: match (stat.st_mode as Mode) & libc::S_IFMT { - libc::S_IFREG => old_io::FileType::RegularFile, - libc::S_IFDIR => old_io::FileType::Directory, - libc::S_IFIFO => old_io::FileType::NamedPipe, - libc::S_IFBLK => old_io::FileType::BlockSpecial, - libc::S_IFLNK => old_io::FileType::Symlink, - _ => old_io::FileType::Unknown, - }, - perm: old_io::FilePermission::from_bits_truncate(stat.st_mode as u32), - created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64), - modified: mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64), - accessed: mktime(stat.st_atime as u64, stat.st_atime_nsec as u64), - unstable: old_io::UnstableFileStat { - device: stat.st_dev as u64, - inode: stat.st_ino as u64, - rdev: stat.st_rdev as u64, - nlink: stat.st_nlink as u64, - uid: stat.st_uid as u64, - gid: stat.st_gid as u64, - blksize: stat.st_blksize as u64, - blocks: stat.st_blocks as u64, - flags: flags(stat), - gen: gen(stat), - } +impl Drop for Session { + fn drop(&mut self) { + unsafe { assert_eq!(raw::libssh2_session_free(self.raw), 0); } } } -impl Drop for Session { - fn drop(&mut self) { - unsafe { - assert_eq!(raw::libssh2_session_free(self.raw), 0); - } +impl ScpFileStat { + /// Returns the size of the remote file. + pub fn size(&self) -> u64 { self.stat.st_size as u64 } + /// Returns the listed mode of the remote file. + pub fn mode(&self) -> i32 { self.stat.st_mode as i32 } + /// Returns whether the remote file is a directory. + pub fn is_dir(&self) -> bool { + self.mode() & (libc::S_IFMT as i32) == (libc::S_IFDIR as i32) + } + /// Returns whether the remote file is a regular file. + pub fn is_file(&self) -> bool { + self.mode() & (libc::S_IFMT as i32) == (libc::S_IFREG as i32) } } + +// fn mkstat(stat: &libc::stat) -> old_io::FileStat { +// #[cfg(windows)] type Mode = libc::c_int; +// #[cfg(unix)] type Mode = libc::mode_t; +// +// // FileStat times are in milliseconds +// fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 } +// +// #[cfg(all(not(target_os = "linux"), not(target_os = "android")))] +// fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 } +// #[cfg(any(target_os = "linux", target_os = "android"))] +// fn flags(_stat: &libc::stat) -> u64 { 0 } +// +// #[cfg(all(not(target_os = "linux"), not(target_os = "android")))] +// fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 } +// #[cfg(any(target_os = "linux", target_os = "android"))] +// fn gen(_stat: &libc::stat) -> u64 { 0 } +// +// old_io::FileStat { +// size: stat.st_size as u64, +// kind: match (stat.st_mode as Mode) & libc::S_IFMT { +// libc::S_IFREG => old_io::FileType::RegularFile, +// libc::S_IFDIR => old_io::FileType::Directory, +// libc::S_IFIFO => old_io::FileType::NamedPipe, +// libc::S_IFBLK => old_io::FileType::BlockSpecial, +// libc::S_IFLNK => old_io::FileType::Symlink, +// _ => old_io::FileType::Unknown, +// }, +// perm: old_io::FilePermission::from_bits_truncate(stat.st_mode as u32), +// created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64), +// modified: mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64), +// accessed: mktime(stat.st_atime as u64, stat.st_atime_nsec as u64), +// unstable: old_io::UnstableFileStat { +// device: stat.st_dev as u64, +// inode: stat.st_ino as u64, +// rdev: stat.st_rdev as u64, +// nlink: stat.st_nlink as u64, +// uid: stat.st_uid as u64, +// gid: stat.st_gid as u64, +// blksize: stat.st_blksize as u64, +// blocks: stat.st_blocks as u64, +// flags: flags(stat), +// gen: gen(stat), +// } +// } +// } diff --git a/src/sftp.rs b/src/sftp.rs index d39fc33..009f7cf 100644 --- a/src/sftp.rs +++ b/src/sftp.rs @@ -1,10 +1,12 @@ +use std::io::prelude::*; +use std::io::{self, ErrorKind, Seek, SeekFrom}; use std::marker; use std::mem; -use std::old_io; +use std::path::{Path, PathBuf}; use libc::{c_int, c_ulong, c_long, c_uint, size_t}; use {raw, Session, Error, Channel}; -use util::SessionBinding; +use util::{self, SessionBinding}; /// A handle to a remote filesystem over SFTP. /// @@ -98,15 +100,14 @@ pub enum OpenType { impl<'sess> Sftp<'sess> { /// Open a handle to a file. pub fn open_mode(&self, filename: &Path, flags: OpenFlags, - mode: old_io::FilePermission, - open_type: OpenType) -> Result<File, Error> { - let filename = filename.as_vec(); + mode: i32, open_type: OpenType) -> Result<File, Error> { + let filename = try!(util::path2bytes(filename)); unsafe { let ret = raw::libssh2_sftp_open_ex(self.raw, filename.as_ptr() as *const _, filename.len() as c_uint, flags.bits() as c_ulong, - mode.bits() as c_long, + mode as c_long, open_type as c_int); if ret.is_null() { Err(self.last_error()) @@ -118,17 +119,17 @@ impl<'sess> Sftp<'sess> { /// Helper to open a file in the `Read` mode. pub fn open(&self, filename: &Path) -> Result<File, Error> { - self.open_mode(filename, READ, old_io::USER_FILE, OpenType::File) + self.open_mode(filename, READ, 0o644, OpenType::File) } /// Helper to create a file in write-only mode with truncation. pub fn create(&self, filename: &Path) -> Result<File, Error> { - self.open_mode(filename, WRITE | TRUNCATE, old_io::USER_FILE, OpenType::File) + self.open_mode(filename, WRITE | TRUNCATE, 0o644, OpenType::File) } /// Helper to open a directory for reading its contents. pub fn opendir(&self, dirname: &Path) -> Result<File, Error> { - self.open_mode(dirname, READ, old_io::USER_FILE, OpenType::Dir) + self.open_mode(dirname, READ, 0, OpenType::Dir) } /// Convenience function to read the files in a directory. @@ -136,16 +137,16 @@ impl<'sess> Sftp<'sess> { /// The returned paths are all joined with `dirname` when returned, and the /// paths `.` and `..` are filtered out of the returned list. pub fn readdir(&self, dirname: &Path) - -> Result<Vec<(Path, FileStat)>, Error> { + -> Result<Vec<(PathBuf, FileStat)>, Error> { let mut dir = try!(self.opendir(dirname)); let mut ret = Vec::new(); loop { match dir.readdir() { Ok((filename, stat)) => { - if filename.as_vec() == b"." || - filename.as_vec() == b".." { continue } + if &*filename == Path::new(".") || + &*filename == Path::new("..") { continue } - ret.push((dirname.join(filename), stat)) + ret.push((dirname.join(&filename), stat)) } Err(ref e) if e.code() == raw::LIBSSH2_ERROR_FILE => break, Err(e) => return Err(e), @@ -155,20 +156,20 @@ impl<'sess> Sftp<'sess> { } /// Create a directory on the remote file system. - pub fn mkdir(&self, filename: &Path, mode: old_io::FilePermission) + pub fn mkdir(&self, filename: &Path, mode: i32) -> Result<(), Error> { - let filename = filename.as_vec(); + let filename = try!(util::path2bytes(filename)); self.rc(unsafe { raw::libssh2_sftp_mkdir_ex(self.raw, filename.as_ptr() as *const _, filename.len() as c_uint, - mode.bits() as c_long) + mode as c_long) }) } /// Remove a directory from the remote file system. pub fn rmdir(&self, filename: &Path) -> Result<(), Error> { - let filename = filename.as_vec(); + let filename = try!(util::path2bytes(filename)); self.rc(unsafe { raw::libssh2_sftp_rmdir_ex(self.raw, filename.as_ptr() as *const _, @@ -178,7 +179,7 @@ impl<'sess> Sftp<'sess> { /// Get the metadata for a file, performed by stat(2) pub fn stat(&self, filename: &Path) -> Result<FileStat, Error> { - let filename = filename.as_vec(); + let filename = try!(util::path2bytes(filename)); unsafe { let mut ret = mem::zeroed(); let rc = raw::libssh2_sftp_stat_ex(self.raw, @@ -193,7 +194,7 @@ impl<'sess> Sftp<'sess> { /// Get the metadata for a file, performed by lstat(2) pub fn lstat(&self, filename: &Path) -> Result<FileStat, Error> { - let filename = filename.as_vec(); + let filename = try!(util::path2bytes(filename)); unsafe { let mut ret = mem::zeroed(); let rc = raw::libssh2_sftp_stat_ex(self.raw, @@ -208,7 +209,7 @@ impl<'sess> Sftp<'sess> { /// Set the metadata for a file. pub fn setstat(&self, filename: &Path, stat: FileStat) -> Result<(), Error> { - let filename = filename.as_vec(); + let filename = try!(util::path2bytes(filename)); self.rc(unsafe { let mut raw = stat.raw(); raw::libssh2_sftp_stat_ex(self.raw, @@ -221,8 +222,8 @@ impl<'sess> Sftp<'sess> { /// Create a symlink at `target` pointing at `path`. pub fn symlink(&self, path: &Path, target: &Path) -> Result<(), Error> { - let path = path.as_vec(); - let target = target.as_vec(); + let path = try!(util::path2bytes(path)); + let target = try!(util::path2bytes(target)); self.rc(unsafe { raw::libssh2_sftp_symlink_ex(self.raw, path.as_ptr() as *const _, @@ -234,17 +235,17 @@ impl<'sess> Sftp<'sess> { } /// Read a symlink at `path`. - pub fn readlink(&self, path: &Path) -> Result<Path, Error> { + pub fn readlink(&self, path: &Path) -> Result<PathBuf, Error> { self.readlink_op(path, raw::LIBSSH2_SFTP_READLINK) } /// Resolve the real path for `path`. - pub fn realpath(&self, path: &Path) -> Result<Path, Error> { + pub fn realpath(&self, path: &Path) -> Result<PathBuf, Error> { self.readlink_op(path, raw::LIBSSH2_SFTP_REALPATH) } - fn readlink_op(&self, path: &Path, op: c_int) -> Result<Path, Error> { - let path = path.as_vec(); + fn readlink_op(&self, path: &Path, op: c_int) -> Result<PathBuf, Error> { + let path = try!(util::path2bytes(path)); let mut ret = Vec::<u8>::with_capacity(128); let mut rc; loop { @@ -267,7 +268,7 @@ impl<'sess> Sftp<'sess> { Err(self.last_error()) } else { unsafe { ret.set_len(rc as usize) } - Ok(Path::new(ret)) + Ok(mkpath(ret)) } } @@ -286,8 +287,8 @@ impl<'sess> Sftp<'sess> { pub fn rename(&self, src: &Path, dst: &Path, flags: Option<RenameFlags>) -> Result<(), Error> { let flags = flags.unwrap_or(ATOMIC | OVERWRITE | NATIVE); - let src = src.as_vec(); - let dst = dst.as_vec(); + let src = try!(util::path2bytes(src)); + let dst = try!(util::path2bytes(dst)); self.rc(unsafe { raw::libssh2_sftp_rename_ex(self.raw, src.as_ptr() as *const _, @@ -377,7 +378,7 @@ impl<'sftp> File<'sftp> { /// /// Also note that the return paths will not be absolute paths, they are /// the filenames of the files in this directory. - pub fn readdir(&mut self) -> Result<(Path, FileStat), Error> { + pub fn readdir(&mut self) -> Result<(PathBuf, FileStat), Error> { let mut buf = Vec::<u8>::with_capacity(128); let mut stat = unsafe { mem::zeroed() }; let mut rc; @@ -403,7 +404,7 @@ impl<'sftp> File<'sftp> { } else { unsafe { buf.set_len(rc as usize); } } - Ok((Path::new(buf), FileStat::from_raw(&stat))) + Ok((mkpath(buf), FileStat::from_raw(&stat))) } /// This function causes the remote server to synchronize the file data and @@ -415,51 +416,40 @@ impl<'sftp> File<'sftp> { } } -impl<'sftp> Reader for File<'sftp> { - fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<usize> { +impl<'sftp> Read for File<'sftp> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { unsafe { let rc = raw::libssh2_sftp_read(self.raw, buf.as_mut_ptr() as *mut _, buf.len() as size_t); match rc { - 0 => Err(old_io::standard_error(old_io::EndOfFile)), - n if n < 0 => Err(old_io::IoError { - kind: old_io::OtherIoError, - desc: "read error", - detail: Some(self.sftp.last_error().to_string()), - }), + n if n < 0 => Err(io::Error::new(ErrorKind::Other, "read error", + Some(self.sftp.last_error() + .to_string()))), n => Ok(n as usize) } } } } -impl<'sftp> Writer for File<'sftp> { - fn write_all(&mut self, mut buf: &[u8]) -> old_io::IoResult<()> { - while buf.len() > 0 { - let rc = unsafe { - raw::libssh2_sftp_write(self.raw, - buf.as_ptr() as *const _, - buf.len() as size_t) - }; - if rc < 0 { - return Err(old_io::IoError { - kind: old_io::OtherIoError, - desc: "write error", - detail: Some(self.sftp.last_error().to_string()), - }) - } - buf = &buf[rc as usize..]; +impl<'sftp> Write for File<'sftp> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + let rc = unsafe { + raw::libssh2_sftp_write(self.raw, + buf.as_ptr() as *const _, + buf.len() as size_t) + }; + if rc < 0 { + Err(io::Error::new(ErrorKind::Other, "write error", + Some(self.sftp.last_error().to_string()))) + } else { + Ok(rc as usize) } - Ok(()) } + fn flush(&mut self) -> io::Result<()> { Ok(()) } } impl<'sftp> Seek for File<'sftp> { - fn tell(&self) -> old_io::IoResult<u64> { - Ok(unsafe { raw::libssh2_sftp_tell64(self.raw) }) - } - /// Move the file handle's internal pointer to an arbitrary location. /// /// libssh2 implements file pointers as a localized concept to make file @@ -470,28 +460,31 @@ impl<'sftp> Seek for File<'sftp> { /// You MUST NOT seek during writing or reading a file with SFTP, as the /// internals use outstanding packets and changing the "file position" /// during transit will results in badness. - fn seek(&mut self, offset: i64, whence: old_io::SeekStyle) -> old_io::IoResult<()> { - let next = match whence { - old_io::SeekSet => offset as u64, - old_io::SeekCur => (self.tell().unwrap() as i64 + offset) as u64, - old_io::SeekEnd => match self.stat() { + fn seek(&mut self, how: SeekFrom) -> io::Result<u64> { + let next = match how { + SeekFrom::Start(pos) => pos, + SeekFrom::Current(offset) => { + let cur = unsafe { raw::libssh2_sftp_tell64(self.raw) }; + (cur as i64 + offset) as u64 + } + SeekFrom::End(offset) => match self.stat() { Ok(s) => match s.size { Some(size) => (size as i64 + offset) as u64, - None => return Err(old_io::IoError { - kind: old_io::OtherIoError, - desc: "no file size available", - detail: None, - }) + None => { + return Err(io::Error::new(ErrorKind::Other, + "no file size available", + None)) + } }, - Err(e) => return Err(old_io::IoError { - kind: old_io::OtherIoError, - desc: "failed to stat remote file", - detail: Some(e.to_string()), - }), + Err(e) => { + return Err(io::Error::new(ErrorKind::Other, + "failed to stat remote file", + Some(e.to_string()))) + } } }; unsafe { raw::libssh2_sftp_seek64(self.raw, next) } - Ok(()) + Ok(next) } } @@ -547,3 +540,15 @@ impl FileStat { } } } + +#[cfg(unix)] +fn mkpath(v: Vec<u8>) -> PathBuf { + use std::os::unix::prelude::*; + use std::ffi::OsStr; + PathBuf::new(<OsStr as OsStrExt>::from_bytes(&v)) +} +#[cfg(windows)] +fn mkpath(v: Vec<u8>) -> PathBuf { + use std::str; + PathBuf::new(str::from_utf8(&v).unwrap()) +} diff --git a/src/util.rs b/src/util.rs index 11b7ffb..357cdce 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,6 @@ -use {Session, Error}; +use std::path::Path; + +use {raw, Session, Error}; #[doc(hidden)] pub trait Binding: Sized { @@ -24,3 +26,27 @@ pub trait SessionBinding<'sess>: Sized { } } } + +#[cfg(unix)] +pub fn path2bytes(p: &Path) -> Result<&[u8], Error> { + use std::os::unix::prelude::*; + use std::ffi::AsOsStr; + check(p.as_os_str().as_bytes()) +} +#[cfg(windows)] +pub fn path2bytes(p: &Path) -> Result<&[u8], Error> { + match p.to_str() { + Some(s) => check(s), + None => Error::new(raw::LIBSSH2_ERROR_INVAL, + "only unicode paths on windows may be used"), + } +} + +fn check(b: &[u8]) -> Result<&[u8], Error> { + if b.iter().any(|b| *b == 0) { + Err(Error::new(raw::LIBSSH2_ERROR_INVAL, + "path provided contains a 0 byte")) + } else { + Ok(b) + } +} |