From 377b1adc36af65ed79be2b79a4e1caf240fc457a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 5 May 2015 16:35:15 -0700 Subject: std: Rename sys::foo2 modules to sys::foo Now that `std::old_io` has been removed for quite some time the naming real estate here has opened up to allow these modules to move back to their proper names. --- src/libstd/fs.rs | 2 +- src/libstd/net/mod.rs | 2 +- src/libstd/net/tcp.rs | 2 +- src/libstd/net/udp.rs | 2 +- src/libstd/os/android/mod.rs | 2 +- src/libstd/os/bitrig/mod.rs | 2 +- src/libstd/os/dragonfly/mod.rs | 2 +- src/libstd/os/freebsd/mod.rs | 2 +- src/libstd/os/ios/mod.rs | 2 +- src/libstd/os/linux/mod.rs | 2 +- src/libstd/os/macos/mod.rs | 2 +- src/libstd/os/nacl/mod.rs | 2 +- src/libstd/os/openbsd/mod.rs | 2 +- src/libstd/process.rs | 12 +- src/libstd/sys/common/mod.rs | 2 +- src/libstd/sys/common/net.rs | 494 +++++++++++++++++++++++++++++++ src/libstd/sys/common/net2.rs | 494 ------------------------------- src/libstd/sys/unix/ext/fs.rs | 6 +- src/libstd/sys/unix/ext/io.rs | 10 +- src/libstd/sys/unix/ext/process.rs | 2 +- src/libstd/sys/unix/fs.rs | 528 +++++++++++++++++++++++++++++++++ src/libstd/sys/unix/fs2.rs | 528 --------------------------------- src/libstd/sys/unix/mod.rs | 6 +- src/libstd/sys/unix/pipe.rs | 50 ++++ src/libstd/sys/unix/pipe2.rs | 50 ---- src/libstd/sys/unix/process.rs | 414 ++++++++++++++++++++++++++ src/libstd/sys/unix/process2.rs | 414 -------------------------- src/libstd/sys/windows/ext/fs.rs | 4 +- src/libstd/sys/windows/ext/io.rs | 10 +- src/libstd/sys/windows/fs.rs | 579 +++++++++++++++++++++++++++++++++++++ src/libstd/sys/windows/fs2.rs | 579 ------------------------------------- src/libstd/sys/windows/mod.rs | 6 +- src/libstd/sys/windows/pipe.rs | 48 +++ src/libstd/sys/windows/pipe2.rs | 48 --- src/libstd/sys/windows/process.rs | 429 +++++++++++++++++++++++++++ src/libstd/sys/windows/process2.rs | 429 --------------------------- 36 files changed, 2584 insertions(+), 2584 deletions(-) create mode 100644 src/libstd/sys/common/net.rs delete mode 100644 src/libstd/sys/common/net2.rs create mode 100644 src/libstd/sys/unix/fs.rs delete mode 100644 src/libstd/sys/unix/fs2.rs create mode 100644 src/libstd/sys/unix/pipe.rs delete mode 100644 src/libstd/sys/unix/pipe2.rs create mode 100644 src/libstd/sys/unix/process.rs delete mode 100644 src/libstd/sys/unix/process2.rs create mode 100644 src/libstd/sys/windows/fs.rs delete mode 100644 src/libstd/sys/windows/fs2.rs create mode 100644 src/libstd/sys/windows/pipe.rs delete mode 100644 src/libstd/sys/windows/pipe2.rs create mode 100644 src/libstd/sys/windows/process.rs delete mode 100644 src/libstd/sys/windows/process2.rs (limited to 'src/libstd') diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 5a05c61e064..fc5405ea7f6 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -23,7 +23,7 @@ use fmt; use ffi::OsString; use io::{self, Error, ErrorKind, SeekFrom, Seek, Read, Write}; use path::{Path, PathBuf}; -use sys::fs2 as fs_imp; +use sys::fs as fs_imp; use sys_common::{AsInnerMut, FromInner, AsInner}; use vec::Vec; diff --git a/src/libstd/net/mod.rs b/src/libstd/net/mod.rs index bf444ce671d..bff9774bcd0 100644 --- a/src/libstd/net/mod.rs +++ b/src/libstd/net/mod.rs @@ -15,7 +15,7 @@ use prelude::v1::*; use io::{self, Error, ErrorKind}; -use sys_common::net2 as net_imp; +use sys_common::net as net_imp; pub use self::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; pub use self::addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index c0d880877b5..db2cdb73198 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -17,7 +17,7 @@ use io::prelude::*; use fmt; use io; use net::{ToSocketAddrs, SocketAddr, Shutdown}; -use sys_common::net2 as net_imp; +use sys_common::net as net_imp; use sys_common::{AsInner, FromInner}; /// A structure which represents a TCP stream between a local socket and a diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index 4360f62c1bf..67c7096904d 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -16,7 +16,7 @@ use prelude::v1::*; use fmt; use io::{self, Error, ErrorKind}; use net::{ToSocketAddrs, SocketAddr, IpAddr}; -use sys_common::net2 as net_imp; +use sys_common::net as net_imp; use sys_common::{AsInner, FromInner}; /// A User Datagram Protocol socket. diff --git a/src/libstd/os/android/mod.rs b/src/libstd/os/android/mod.rs index 346a903c4d9..a94abba5d12 100644 --- a/src/libstd/os/android/mod.rs +++ b/src/libstd/os/android/mod.rs @@ -15,5 +15,5 @@ pub mod raw; pub mod fs { - pub use sys::fs2::MetadataExt; + pub use sys::fs::MetadataExt; } diff --git a/src/libstd/os/bitrig/mod.rs b/src/libstd/os/bitrig/mod.rs index 01ea542b3b7..1fe5fdd4e14 100644 --- a/src/libstd/os/bitrig/mod.rs +++ b/src/libstd/os/bitrig/mod.rs @@ -15,5 +15,5 @@ pub mod raw; pub mod fs { - pub use sys::fs2::MetadataExt; + pub use sys::fs::MetadataExt; } diff --git a/src/libstd/os/dragonfly/mod.rs b/src/libstd/os/dragonfly/mod.rs index 677f8b706cd..d5c7c581733 100644 --- a/src/libstd/os/dragonfly/mod.rs +++ b/src/libstd/os/dragonfly/mod.rs @@ -15,5 +15,5 @@ pub mod raw; pub mod fs { - pub use sys::fs2::MetadataExt; + pub use sys::fs::MetadataExt; } diff --git a/src/libstd/os/freebsd/mod.rs b/src/libstd/os/freebsd/mod.rs index 73b6fd21137..28c9f8321f8 100644 --- a/src/libstd/os/freebsd/mod.rs +++ b/src/libstd/os/freebsd/mod.rs @@ -15,5 +15,5 @@ pub mod raw; pub mod fs { - pub use sys::fs2::MetadataExt; + pub use sys::fs::MetadataExt; } diff --git a/src/libstd/os/ios/mod.rs b/src/libstd/os/ios/mod.rs index d471cf12fe6..dd2878c6e38 100644 --- a/src/libstd/os/ios/mod.rs +++ b/src/libstd/os/ios/mod.rs @@ -15,5 +15,5 @@ pub mod raw; pub mod fs { - pub use sys::fs2::MetadataExt; + pub use sys::fs::MetadataExt; } diff --git a/src/libstd/os/linux/mod.rs b/src/libstd/os/linux/mod.rs index 43376a1baeb..d2f9bcc3bcf 100644 --- a/src/libstd/os/linux/mod.rs +++ b/src/libstd/os/linux/mod.rs @@ -15,5 +15,5 @@ pub mod raw; pub mod fs { - pub use sys::fs2::MetadataExt; + pub use sys::fs::MetadataExt; } diff --git a/src/libstd/os/macos/mod.rs b/src/libstd/os/macos/mod.rs index bc5ff5b25d2..6c96909f382 100644 --- a/src/libstd/os/macos/mod.rs +++ b/src/libstd/os/macos/mod.rs @@ -15,5 +15,5 @@ pub mod raw; pub mod fs { - pub use sys::fs2::MetadataExt; + pub use sys::fs::MetadataExt; } diff --git a/src/libstd/os/nacl/mod.rs b/src/libstd/os/nacl/mod.rs index 6baed039514..413bb72f6e1 100644 --- a/src/libstd/os/nacl/mod.rs +++ b/src/libstd/os/nacl/mod.rs @@ -15,5 +15,5 @@ pub mod raw; pub mod fs { - pub use sys::fs2::MetadataExt; + pub use sys::fs::MetadataExt; } diff --git a/src/libstd/os/openbsd/mod.rs b/src/libstd/os/openbsd/mod.rs index 1b1a1005590..5654a7a0229 100644 --- a/src/libstd/os/openbsd/mod.rs +++ b/src/libstd/os/openbsd/mod.rs @@ -15,5 +15,5 @@ pub mod raw; pub mod fs { - pub use sys::fs2::MetadataExt; + pub use sys::fs::MetadataExt; } diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 8f8699f4b9f..61398e16ba0 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -21,11 +21,11 @@ use fmt; use io::{self, Error, ErrorKind}; use path; use sync::mpsc::{channel, Receiver}; -use sys::pipe2::{self, AnonPipe}; -use sys::process2::Command as CommandImp; -use sys::process2::Process as ProcessImp; -use sys::process2::ExitStatus as ExitStatusImp; -use sys::process2::Stdio as StdioImp2; +use sys::pipe::{self, AnonPipe}; +use sys::process::Command as CommandImp; +use sys::process::Process as ProcessImp; +use sys::process::ExitStatus as ExitStatusImp; +use sys::process::Stdio as StdioImp2; use sys_common::{AsInner, AsInnerMut}; use thread; @@ -334,7 +334,7 @@ fn setup_io(io: &StdioImp, readable: bool) Null => (StdioImp2::None, None), Inherit => (StdioImp2::Inherit, None), Piped => { - let (reader, writer) = try!(pipe2::anon_pipe()); + let (reader, writer) = try!(pipe::anon_pipe()); if readable { (StdioImp2::Piped(reader), Some(writer)) } else { diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index 95294b813ea..b528575bbed 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -15,7 +15,7 @@ use prelude::v1::*; pub mod backtrace; pub mod condvar; pub mod mutex; -pub mod net2; +pub mod net; pub mod poison; pub mod remutex; pub mod rwlock; diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs new file mode 100644 index 00000000000..7da7071670a --- /dev/null +++ b/src/libstd/sys/common/net.rs @@ -0,0 +1,494 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use ffi::{CStr, CString}; +use fmt; +use io::{self, Error, ErrorKind}; +use libc::{self, c_int, c_char, c_void, socklen_t}; +use mem; +use net::{SocketAddr, Shutdown, IpAddr}; +use str::from_utf8; +use sys::c; +use sys::net::{cvt, cvt_r, cvt_gai, Socket, init, wrlen_t}; +use sys_common::{AsInner, FromInner, IntoInner}; + +//////////////////////////////////////////////////////////////////////////////// +// sockaddr and misc bindings +//////////////////////////////////////////////////////////////////////////////// + +fn setsockopt(sock: &Socket, opt: c_int, val: c_int, + payload: T) -> io::Result<()> { + unsafe { + let payload = &payload as *const T as *const c_void; + try!(cvt(libc::setsockopt(*sock.as_inner(), opt, val, payload, + mem::size_of::() as socklen_t))); + Ok(()) + } +} + +#[allow(dead_code)] +fn getsockopt(sock: &Socket, opt: c_int, + val: c_int) -> io::Result { + unsafe { + let mut slot: T = mem::zeroed(); + let mut len = mem::size_of::() as socklen_t; + let ret = try!(cvt(c::getsockopt(*sock.as_inner(), opt, val, + &mut slot as *mut _ as *mut _, + &mut len))); + assert_eq!(ret as usize, mem::size_of::()); + Ok(slot) + } +} + +fn sockname(f: F) -> io::Result + where F: FnOnce(*mut libc::sockaddr, *mut socklen_t) -> c_int +{ + unsafe { + let mut storage: libc::sockaddr_storage = mem::zeroed(); + let mut len = mem::size_of_val(&storage) as socklen_t; + try!(cvt(f(&mut storage as *mut _ as *mut _, &mut len))); + sockaddr_to_addr(&storage, len as usize) + } +} + +fn sockaddr_to_addr(storage: &libc::sockaddr_storage, + len: usize) -> io::Result { + match storage.ss_family as libc::c_int { + libc::AF_INET => { + assert!(len as usize >= mem::size_of::()); + Ok(SocketAddr::V4(FromInner::from_inner(unsafe { + *(storage as *const _ as *const libc::sockaddr_in) + }))) + } + libc::AF_INET6 => { + assert!(len as usize >= mem::size_of::()); + Ok(SocketAddr::V6(FromInner::from_inner(unsafe { + *(storage as *const _ as *const libc::sockaddr_in6) + }))) + } + _ => { + Err(Error::new(ErrorKind::InvalidInput, "invalid argument")) + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// get_host_addresses +//////////////////////////////////////////////////////////////////////////////// + +extern "system" { + fn getaddrinfo(node: *const c_char, service: *const c_char, + hints: *const libc::addrinfo, + res: *mut *mut libc::addrinfo) -> c_int; + fn freeaddrinfo(res: *mut libc::addrinfo); +} + +pub struct LookupHost { + original: *mut libc::addrinfo, + cur: *mut libc::addrinfo, +} + +impl Iterator for LookupHost { + type Item = io::Result; + fn next(&mut self) -> Option> { + unsafe { + if self.cur.is_null() { return None } + let ret = sockaddr_to_addr(mem::transmute((*self.cur).ai_addr), + (*self.cur).ai_addrlen as usize); + self.cur = (*self.cur).ai_next as *mut libc::addrinfo; + Some(ret) + } + } +} + +impl Drop for LookupHost { + fn drop(&mut self) { + unsafe { freeaddrinfo(self.original) } + } +} + +pub fn lookup_host(host: &str) -> io::Result { + init(); + + let c_host = try!(CString::new(host)); + let mut res = 0 as *mut _; + unsafe { + try!(cvt_gai(getaddrinfo(c_host.as_ptr(), 0 as *const _, 0 as *const _, + &mut res))); + Ok(LookupHost { original: res, cur: res }) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// lookup_addr +//////////////////////////////////////////////////////////////////////////////// + +extern "system" { + fn getnameinfo(sa: *const libc::sockaddr, salen: socklen_t, + host: *mut c_char, hostlen: libc::size_t, + serv: *mut c_char, servlen: libc::size_t, + flags: c_int) -> c_int; +} + +const NI_MAXHOST: usize = 1025; + +pub fn lookup_addr(addr: &IpAddr) -> io::Result { + init(); + + let saddr = SocketAddr::new(*addr, 0); + let (inner, len) = saddr.into_inner(); + let mut hostbuf = [0 as c_char; NI_MAXHOST]; + + let data = unsafe { + try!(cvt_gai(getnameinfo(inner, len, + hostbuf.as_mut_ptr(), NI_MAXHOST as libc::size_t, + 0 as *mut _, 0, 0))); + + CStr::from_ptr(hostbuf.as_ptr()) + }; + + match from_utf8(data.to_bytes()) { + Ok(name) => Ok(name.to_string()), + Err(_) => Err(io::Error::new(io::ErrorKind::Other, + "failed to lookup address information")) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// TCP streams +//////////////////////////////////////////////////////////////////////////////// + +pub struct TcpStream { + inner: Socket, +} + +impl TcpStream { + pub fn connect(addr: &SocketAddr) -> io::Result { + init(); + + let sock = try!(Socket::new(addr, libc::SOCK_STREAM)); + + let (addrp, len) = addr.into_inner(); + try!(cvt_r(|| unsafe { libc::connect(*sock.as_inner(), addrp, len) })); + Ok(TcpStream { inner: sock }) + } + + pub fn socket(&self) -> &Socket { &self.inner } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(&self.inner, libc::IPPROTO_TCP, libc::TCP_NODELAY, + nodelay as c_int) + } + + pub fn set_keepalive(&self, seconds: Option) -> io::Result<()> { + let ret = setsockopt(&self.inner, libc::SOL_SOCKET, libc::SO_KEEPALIVE, + seconds.is_some() as c_int); + match seconds { + Some(n) => ret.and_then(|()| self.set_tcp_keepalive(n)), + None => ret, + } + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + fn set_tcp_keepalive(&self, seconds: u32) -> io::Result<()> { + setsockopt(&self.inner, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, + seconds as c_int) + } + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly", + target_os = "linux"))] + fn set_tcp_keepalive(&self, seconds: u32) -> io::Result<()> { + setsockopt(&self.inner, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, + seconds as c_int) + } + + #[cfg(not(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "linux")))] + fn set_tcp_keepalive(&self, _seconds: u32) -> io::Result<()> { + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let ret = try!(cvt(unsafe { + libc::send(*self.inner.as_inner(), + buf.as_ptr() as *const c_void, + buf.len() as wrlen_t, + 0) + })); + Ok(ret as usize) + } + + pub fn peer_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { + libc::getpeername(*self.inner.as_inner(), buf, len) + }) + } + + pub fn socket_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { + libc::getsockname(*self.inner.as_inner(), buf, len) + }) + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + use libc::consts::os::bsd44::SHUT_RDWR; + + let how = match how { + Shutdown::Write => libc::SHUT_WR, + Shutdown::Read => libc::SHUT_RD, + Shutdown::Both => SHUT_RDWR, + }; + try!(cvt(unsafe { libc::shutdown(*self.inner.as_inner(), how) })); + Ok(()) + } + + pub fn duplicate(&self) -> io::Result { + self.inner.duplicate().map(|s| TcpStream { inner: s }) + } +} + +impl FromInner for TcpStream { + fn from_inner(socket: Socket) -> TcpStream { + TcpStream { inner: socket } + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut res = f.debug_struct("TcpStream"); + + if let Ok(addr) = self.socket_addr() { + res = res.field("addr", &addr); + } + + if let Ok(peer) = self.peer_addr() { + res = res.field("peer", &peer); + } + + let name = if cfg!(windows) {"socket"} else {"fd"}; + res = res.field(name, &self.inner.as_inner()); + res.finish() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// TCP listeners +//////////////////////////////////////////////////////////////////////////////// + +pub struct TcpListener { + inner: Socket, +} + +impl TcpListener { + pub fn bind(addr: &SocketAddr) -> io::Result { + init(); + + let sock = try!(Socket::new(addr, libc::SOCK_STREAM)); + + // On platforms with Berkeley-derived sockets, this allows + // to quickly rebind a socket, without needing to wait for + // the OS to clean up the previous one. + if !cfg!(windows) { + try!(setsockopt(&sock, libc::SOL_SOCKET, libc::SO_REUSEADDR, + 1 as c_int)); + } + + // Bind our new socket + let (addrp, len) = addr.into_inner(); + try!(cvt(unsafe { libc::bind(*sock.as_inner(), addrp, len) })); + + // Start listening + try!(cvt(unsafe { libc::listen(*sock.as_inner(), 128) })); + Ok(TcpListener { inner: sock }) + } + + pub fn socket(&self) -> &Socket { &self.inner } + + pub fn socket_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { + libc::getsockname(*self.inner.as_inner(), buf, len) + }) + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut len = mem::size_of_val(&storage) as socklen_t; + let sock = try!(self.inner.accept(&mut storage as *mut _ as *mut _, + &mut len)); + let addr = try!(sockaddr_to_addr(&storage, len as usize)); + Ok((TcpStream { inner: sock, }, addr)) + } + + pub fn duplicate(&self) -> io::Result { + self.inner.duplicate().map(|s| TcpListener { inner: s }) + } +} + +impl FromInner for TcpListener { + fn from_inner(socket: Socket) -> TcpListener { + TcpListener { inner: socket } + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut res = f.debug_struct("TcpListener"); + + if let Ok(addr) = self.socket_addr() { + res = res.field("addr", &addr); + } + + let name = if cfg!(windows) {"socket"} else {"fd"}; + res = res.field(name, &self.inner.as_inner()); + res.finish() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// UDP +//////////////////////////////////////////////////////////////////////////////// + +pub struct UdpSocket { + inner: Socket, +} + +impl UdpSocket { + pub fn bind(addr: &SocketAddr) -> io::Result { + init(); + + let sock = try!(Socket::new(addr, libc::SOCK_DGRAM)); + let (addrp, len) = addr.into_inner(); + try!(cvt(unsafe { libc::bind(*sock.as_inner(), addrp, len) })); + Ok(UdpSocket { inner: sock }) + } + + pub fn socket(&self) -> &Socket { &self.inner } + + pub fn socket_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { + libc::getsockname(*self.inner.as_inner(), buf, len) + }) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as socklen_t; + + let n = try!(cvt(unsafe { + libc::recvfrom(*self.inner.as_inner(), + buf.as_mut_ptr() as *mut c_void, + buf.len() as wrlen_t, 0, + &mut storage as *mut _ as *mut _, &mut addrlen) + })); + Ok((n as usize, try!(sockaddr_to_addr(&storage, addrlen as usize)))) + } + + pub fn send_to(&self, buf: &[u8], dst: &SocketAddr) -> io::Result { + let (dstp, dstlen) = dst.into_inner(); + let ret = try!(cvt(unsafe { + libc::sendto(*self.inner.as_inner(), + buf.as_ptr() as *const c_void, buf.len() as wrlen_t, + 0, dstp, dstlen) + })); + Ok(ret as usize) + } + + pub fn set_broadcast(&self, on: bool) -> io::Result<()> { + setsockopt(&self.inner, libc::SOL_SOCKET, libc::SO_BROADCAST, + on as c_int) + } + + pub fn set_multicast_loop(&self, on: bool) -> io::Result<()> { + setsockopt(&self.inner, libc::IPPROTO_IP, + libc::IP_MULTICAST_LOOP, on as c_int) + } + + pub fn join_multicast(&self, multi: &IpAddr) -> io::Result<()> { + match *multi { + IpAddr::V4(..) => { + self.set_membership(multi, libc::IP_ADD_MEMBERSHIP) + } + IpAddr::V6(..) => { + self.set_membership(multi, libc::IPV6_ADD_MEMBERSHIP) + } + } + } + pub fn leave_multicast(&self, multi: &IpAddr) -> io::Result<()> { + match *multi { + IpAddr::V4(..) => { + self.set_membership(multi, libc::IP_DROP_MEMBERSHIP) + } + IpAddr::V6(..) => { + self.set_membership(multi, libc::IPV6_DROP_MEMBERSHIP) + } + } + } + fn set_membership(&self, addr: &IpAddr, opt: c_int) -> io::Result<()> { + match *addr { + IpAddr::V4(ref addr) => { + let mreq = libc::ip_mreq { + imr_multiaddr: *addr.as_inner(), + // interface == INADDR_ANY + imr_interface: libc::in_addr { s_addr: 0x0 }, + }; + setsockopt(&self.inner, libc::IPPROTO_IP, opt, mreq) + } + IpAddr::V6(ref addr) => { + let mreq = libc::ip6_mreq { + ipv6mr_multiaddr: *addr.as_inner(), + ipv6mr_interface: 0, + }; + setsockopt(&self.inner, libc::IPPROTO_IPV6, opt, mreq) + } + } + } + + pub fn multicast_time_to_live(&self, ttl: i32) -> io::Result<()> { + setsockopt(&self.inner, libc::IPPROTO_IP, libc::IP_MULTICAST_TTL, + ttl as c_int) + } + + pub fn time_to_live(&self, ttl: i32) -> io::Result<()> { + setsockopt(&self.inner, libc::IPPROTO_IP, libc::IP_TTL, ttl as c_int) + } + + pub fn duplicate(&self) -> io::Result { + self.inner.duplicate().map(|s| UdpSocket { inner: s }) + } +} + +impl FromInner for UdpSocket { + fn from_inner(socket: Socket) -> UdpSocket { + UdpSocket { inner: socket } + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut res = f.debug_struct("UdpSocket"); + + if let Ok(addr) = self.socket_addr() { + res = res.field("addr", &addr); + } + + let name = if cfg!(windows) {"socket"} else {"fd"}; + res = res.field(name, &self.inner.as_inner()); + res.finish() + } +} diff --git a/src/libstd/sys/common/net2.rs b/src/libstd/sys/common/net2.rs deleted file mode 100644 index 7da7071670a..00000000000 --- a/src/libstd/sys/common/net2.rs +++ /dev/null @@ -1,494 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use prelude::v1::*; - -use ffi::{CStr, CString}; -use fmt; -use io::{self, Error, ErrorKind}; -use libc::{self, c_int, c_char, c_void, socklen_t}; -use mem; -use net::{SocketAddr, Shutdown, IpAddr}; -use str::from_utf8; -use sys::c; -use sys::net::{cvt, cvt_r, cvt_gai, Socket, init, wrlen_t}; -use sys_common::{AsInner, FromInner, IntoInner}; - -//////////////////////////////////////////////////////////////////////////////// -// sockaddr and misc bindings -//////////////////////////////////////////////////////////////////////////////// - -fn setsockopt(sock: &Socket, opt: c_int, val: c_int, - payload: T) -> io::Result<()> { - unsafe { - let payload = &payload as *const T as *const c_void; - try!(cvt(libc::setsockopt(*sock.as_inner(), opt, val, payload, - mem::size_of::() as socklen_t))); - Ok(()) - } -} - -#[allow(dead_code)] -fn getsockopt(sock: &Socket, opt: c_int, - val: c_int) -> io::Result { - unsafe { - let mut slot: T = mem::zeroed(); - let mut len = mem::size_of::() as socklen_t; - let ret = try!(cvt(c::getsockopt(*sock.as_inner(), opt, val, - &mut slot as *mut _ as *mut _, - &mut len))); - assert_eq!(ret as usize, mem::size_of::()); - Ok(slot) - } -} - -fn sockname(f: F) -> io::Result - where F: FnOnce(*mut libc::sockaddr, *mut socklen_t) -> c_int -{ - unsafe { - let mut storage: libc::sockaddr_storage = mem::zeroed(); - let mut len = mem::size_of_val(&storage) as socklen_t; - try!(cvt(f(&mut storage as *mut _ as *mut _, &mut len))); - sockaddr_to_addr(&storage, len as usize) - } -} - -fn sockaddr_to_addr(storage: &libc::sockaddr_storage, - len: usize) -> io::Result { - match storage.ss_family as libc::c_int { - libc::AF_INET => { - assert!(len as usize >= mem::size_of::()); - Ok(SocketAddr::V4(FromInner::from_inner(unsafe { - *(storage as *const _ as *const libc::sockaddr_in) - }))) - } - libc::AF_INET6 => { - assert!(len as usize >= mem::size_of::()); - Ok(SocketAddr::V6(FromInner::from_inner(unsafe { - *(storage as *const _ as *const libc::sockaddr_in6) - }))) - } - _ => { - Err(Error::new(ErrorKind::InvalidInput, "invalid argument")) - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// get_host_addresses -//////////////////////////////////////////////////////////////////////////////// - -extern "system" { - fn getaddrinfo(node: *const c_char, service: *const c_char, - hints: *const libc::addrinfo, - res: *mut *mut libc::addrinfo) -> c_int; - fn freeaddrinfo(res: *mut libc::addrinfo); -} - -pub struct LookupHost { - original: *mut libc::addrinfo, - cur: *mut libc::addrinfo, -} - -impl Iterator for LookupHost { - type Item = io::Result; - fn next(&mut self) -> Option> { - unsafe { - if self.cur.is_null() { return None } - let ret = sockaddr_to_addr(mem::transmute((*self.cur).ai_addr), - (*self.cur).ai_addrlen as usize); - self.cur = (*self.cur).ai_next as *mut libc::addrinfo; - Some(ret) - } - } -} - -impl Drop for LookupHost { - fn drop(&mut self) { - unsafe { freeaddrinfo(self.original) } - } -} - -pub fn lookup_host(host: &str) -> io::Result { - init(); - - let c_host = try!(CString::new(host)); - let mut res = 0 as *mut _; - unsafe { - try!(cvt_gai(getaddrinfo(c_host.as_ptr(), 0 as *const _, 0 as *const _, - &mut res))); - Ok(LookupHost { original: res, cur: res }) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// lookup_addr -//////////////////////////////////////////////////////////////////////////////// - -extern "system" { - fn getnameinfo(sa: *const libc::sockaddr, salen: socklen_t, - host: *mut c_char, hostlen: libc::size_t, - serv: *mut c_char, servlen: libc::size_t, - flags: c_int) -> c_int; -} - -const NI_MAXHOST: usize = 1025; - -pub fn lookup_addr(addr: &IpAddr) -> io::Result { - init(); - - let saddr = SocketAddr::new(*addr, 0); - let (inner, len) = saddr.into_inner(); - let mut hostbuf = [0 as c_char; NI_MAXHOST]; - - let data = unsafe { - try!(cvt_gai(getnameinfo(inner, len, - hostbuf.as_mut_ptr(), NI_MAXHOST as libc::size_t, - 0 as *mut _, 0, 0))); - - CStr::from_ptr(hostbuf.as_ptr()) - }; - - match from_utf8(data.to_bytes()) { - Ok(name) => Ok(name.to_string()), - Err(_) => Err(io::Error::new(io::ErrorKind::Other, - "failed to lookup address information")) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// TCP streams -//////////////////////////////////////////////////////////////////////////////// - -pub struct TcpStream { - inner: Socket, -} - -impl TcpStream { - pub fn connect(addr: &SocketAddr) -> io::Result { - init(); - - let sock = try!(Socket::new(addr, libc::SOCK_STREAM)); - - let (addrp, len) = addr.into_inner(); - try!(cvt_r(|| unsafe { libc::connect(*sock.as_inner(), addrp, len) })); - Ok(TcpStream { inner: sock }) - } - - pub fn socket(&self) -> &Socket { &self.inner } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(&self.inner, libc::IPPROTO_TCP, libc::TCP_NODELAY, - nodelay as c_int) - } - - pub fn set_keepalive(&self, seconds: Option) -> io::Result<()> { - let ret = setsockopt(&self.inner, libc::SOL_SOCKET, libc::SO_KEEPALIVE, - seconds.is_some() as c_int); - match seconds { - Some(n) => ret.and_then(|()| self.set_tcp_keepalive(n)), - None => ret, - } - } - - #[cfg(any(target_os = "macos", target_os = "ios"))] - fn set_tcp_keepalive(&self, seconds: u32) -> io::Result<()> { - setsockopt(&self.inner, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, - seconds as c_int) - } - #[cfg(any(target_os = "freebsd", - target_os = "dragonfly", - target_os = "linux"))] - fn set_tcp_keepalive(&self, seconds: u32) -> io::Result<()> { - setsockopt(&self.inner, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, - seconds as c_int) - } - - #[cfg(not(any(target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "linux")))] - fn set_tcp_keepalive(&self, _seconds: u32) -> io::Result<()> { - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - let ret = try!(cvt(unsafe { - libc::send(*self.inner.as_inner(), - buf.as_ptr() as *const c_void, - buf.len() as wrlen_t, - 0) - })); - Ok(ret as usize) - } - - pub fn peer_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { - libc::getpeername(*self.inner.as_inner(), buf, len) - }) - } - - pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { - libc::getsockname(*self.inner.as_inner(), buf, len) - }) - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - use libc::consts::os::bsd44::SHUT_RDWR; - - let how = match how { - Shutdown::Write => libc::SHUT_WR, - Shutdown::Read => libc::SHUT_RD, - Shutdown::Both => SHUT_RDWR, - }; - try!(cvt(unsafe { libc::shutdown(*self.inner.as_inner(), how) })); - Ok(()) - } - - pub fn duplicate(&self) -> io::Result { - self.inner.duplicate().map(|s| TcpStream { inner: s }) - } -} - -impl FromInner for TcpStream { - fn from_inner(socket: Socket) -> TcpStream { - TcpStream { inner: socket } - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut res = f.debug_struct("TcpStream"); - - if let Ok(addr) = self.socket_addr() { - res = res.field("addr", &addr); - } - - if let Ok(peer) = self.peer_addr() { - res = res.field("peer", &peer); - } - - let name = if cfg!(windows) {"socket"} else {"fd"}; - res = res.field(name, &self.inner.as_inner()); - res.finish() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// TCP listeners -//////////////////////////////////////////////////////////////////////////////// - -pub struct TcpListener { - inner: Socket, -} - -impl TcpListener { - pub fn bind(addr: &SocketAddr) -> io::Result { - init(); - - let sock = try!(Socket::new(addr, libc::SOCK_STREAM)); - - // On platforms with Berkeley-derived sockets, this allows - // to quickly rebind a socket, without needing to wait for - // the OS to clean up the previous one. - if !cfg!(windows) { - try!(setsockopt(&sock, libc::SOL_SOCKET, libc::SO_REUSEADDR, - 1 as c_int)); - } - - // Bind our new socket - let (addrp, len) = addr.into_inner(); - try!(cvt(unsafe { libc::bind(*sock.as_inner(), addrp, len) })); - - // Start listening - try!(cvt(unsafe { libc::listen(*sock.as_inner(), 128) })); - Ok(TcpListener { inner: sock }) - } - - pub fn socket(&self) -> &Socket { &self.inner } - - pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { - libc::getsockname(*self.inner.as_inner(), buf, len) - }) - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut len = mem::size_of_val(&storage) as socklen_t; - let sock = try!(self.inner.accept(&mut storage as *mut _ as *mut _, - &mut len)); - let addr = try!(sockaddr_to_addr(&storage, len as usize)); - Ok((TcpStream { inner: sock, }, addr)) - } - - pub fn duplicate(&self) -> io::Result { - self.inner.duplicate().map(|s| TcpListener { inner: s }) - } -} - -impl FromInner for TcpListener { - fn from_inner(socket: Socket) -> TcpListener { - TcpListener { inner: socket } - } -} - -impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut res = f.debug_struct("TcpListener"); - - if let Ok(addr) = self.socket_addr() { - res = res.field("addr", &addr); - } - - let name = if cfg!(windows) {"socket"} else {"fd"}; - res = res.field(name, &self.inner.as_inner()); - res.finish() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// UDP -//////////////////////////////////////////////////////////////////////////////// - -pub struct UdpSocket { - inner: Socket, -} - -impl UdpSocket { - pub fn bind(addr: &SocketAddr) -> io::Result { - init(); - - let sock = try!(Socket::new(addr, libc::SOCK_DGRAM)); - let (addrp, len) = addr.into_inner(); - try!(cvt(unsafe { libc::bind(*sock.as_inner(), addrp, len) })); - Ok(UdpSocket { inner: sock }) - } - - pub fn socket(&self) -> &Socket { &self.inner } - - pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { - libc::getsockname(*self.inner.as_inner(), buf, len) - }) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as socklen_t; - - let n = try!(cvt(unsafe { - libc::recvfrom(*self.inner.as_inner(), - buf.as_mut_ptr() as *mut c_void, - buf.len() as wrlen_t, 0, - &mut storage as *mut _ as *mut _, &mut addrlen) - })); - Ok((n as usize, try!(sockaddr_to_addr(&storage, addrlen as usize)))) - } - - pub fn send_to(&self, buf: &[u8], dst: &SocketAddr) -> io::Result { - let (dstp, dstlen) = dst.into_inner(); - let ret = try!(cvt(unsafe { - libc::sendto(*self.inner.as_inner(), - buf.as_ptr() as *const c_void, buf.len() as wrlen_t, - 0, dstp, dstlen) - })); - Ok(ret as usize) - } - - pub fn set_broadcast(&self, on: bool) -> io::Result<()> { - setsockopt(&self.inner, libc::SOL_SOCKET, libc::SO_BROADCAST, - on as c_int) - } - - pub fn set_multicast_loop(&self, on: bool) -> io::Result<()> { - setsockopt(&self.inner, libc::IPPROTO_IP, - libc::IP_MULTICAST_LOOP, on as c_int) - } - - pub fn join_multicast(&self, multi: &IpAddr) -> io::Result<()> { - match *multi { - IpAddr::V4(..) => { - self.set_membership(multi, libc::IP_ADD_MEMBERSHIP) - } - IpAddr::V6(..) => { - self.set_membership(multi, libc::IPV6_ADD_MEMBERSHIP) - } - } - } - pub fn leave_multicast(&self, multi: &IpAddr) -> io::Result<()> { - match *multi { - IpAddr::V4(..) => { - self.set_membership(multi, libc::IP_DROP_MEMBERSHIP) - } - IpAddr::V6(..) => { - self.set_membership(multi, libc::IPV6_DROP_MEMBERSHIP) - } - } - } - fn set_membership(&self, addr: &IpAddr, opt: c_int) -> io::Result<()> { - match *addr { - IpAddr::V4(ref addr) => { - let mreq = libc::ip_mreq { - imr_multiaddr: *addr.as_inner(), - // interface == INADDR_ANY - imr_interface: libc::in_addr { s_addr: 0x0 }, - }; - setsockopt(&self.inner, libc::IPPROTO_IP, opt, mreq) - } - IpAddr::V6(ref addr) => { - let mreq = libc::ip6_mreq { - ipv6mr_multiaddr: *addr.as_inner(), - ipv6mr_interface: 0, - }; - setsockopt(&self.inner, libc::IPPROTO_IPV6, opt, mreq) - } - } - } - - pub fn multicast_time_to_live(&self, ttl: i32) -> io::Result<()> { - setsockopt(&self.inner, libc::IPPROTO_IP, libc::IP_MULTICAST_TTL, - ttl as c_int) - } - - pub fn time_to_live(&self, ttl: i32) -> io::Result<()> { - setsockopt(&self.inner, libc::IPPROTO_IP, libc::IP_TTL, ttl as c_int) - } - - pub fn duplicate(&self) -> io::Result { - self.inner.duplicate().map(|s| UdpSocket { inner: s }) - } -} - -impl FromInner for UdpSocket { - fn from_inner(socket: Socket) -> UdpSocket { - UdpSocket { inner: socket } - } -} - -impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut res = f.debug_struct("UdpSocket"); - - if let Ok(addr) = self.socket_addr() { - res = res.field("addr", &addr); - } - - let name = if cfg!(windows) {"socket"} else {"fd"}; - res = res.field(name, &self.inner.as_inner()); - res.finish() - } -} diff --git a/src/libstd/sys/unix/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs index 39910f509f9..a6953437497 100644 --- a/src/libstd/sys/unix/ext/fs.rs +++ b/src/libstd/sys/unix/ext/fs.rs @@ -102,7 +102,7 @@ impl OpenOptionsExt for OpenOptions { } #[unstable(feature = "metadata_ext", reason = "recently added API")] -pub struct Metadata(sys::fs2::FileAttr); +pub struct Metadata(sys::fs::FileAttr); #[unstable(feature = "metadata_ext", reason = "recently added API")] pub trait MetadataExt { @@ -111,7 +111,7 @@ pub trait MetadataExt { impl MetadataExt for fs::Metadata { fn as_raw(&self) -> &Metadata { - let inner: &sys::fs2::FileAttr = self.as_inner(); + let inner: &sys::fs::FileAttr = self.as_inner(); unsafe { mem::transmute(inner) } } } @@ -187,7 +187,7 @@ impl DirEntryExt for fs::DirEntry { #[stable(feature = "rust1", since = "1.0.0")] pub fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { - sys::fs2::symlink(src.as_ref(), dst.as_ref()) + sys::fs::symlink(src.as_ref(), dst.as_ref()) } #[unstable(feature = "dir_builder", reason = "recently added API")] diff --git a/src/libstd/sys/unix/ext/io.rs b/src/libstd/sys/unix/ext/io.rs index c4a37241f64..79e59ddab5b 100644 --- a/src/libstd/sys/unix/ext/io.rs +++ b/src/libstd/sys/unix/ext/io.rs @@ -16,7 +16,7 @@ use fs; use net; use os::raw; use sys; -use sys_common::{net2, AsInner, FromInner}; +use sys_common::{self, AsInner, FromInner}; /// Raw file descriptors. #[stable(feature = "rust1", since = "1.0.0")] @@ -68,7 +68,7 @@ impl AsRawFd for fs::File { #[stable(feature = "from_raw_os", since = "1.1.0")] impl FromRawFd for fs::File { unsafe fn from_raw_fd(fd: RawFd) -> fs::File { - fs::File::from_inner(sys::fs2::File::from_inner(fd)) + fs::File::from_inner(sys::fs::File::from_inner(fd)) } } @@ -89,20 +89,20 @@ impl AsRawFd for net::UdpSocket { impl FromRawFd for net::TcpStream { unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { let socket = sys::net::Socket::from_inner(fd); - net::TcpStream::from_inner(net2::TcpStream::from_inner(socket)) + net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(socket)) } } #[stable(feature = "from_raw_os", since = "1.1.0")] impl FromRawFd for net::TcpListener { unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { let socket = sys::net::Socket::from_inner(fd); - net::TcpListener::from_inner(net2::TcpListener::from_inner(socket)) + net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(socket)) } } #[stable(feature = "from_raw_os", since = "1.1.0")] impl FromRawFd for net::UdpSocket { unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { let socket = sys::net::Socket::from_inner(fd); - net::UdpSocket::from_inner(net2::UdpSocket::from_inner(socket)) + net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(socket)) } } diff --git a/src/libstd/sys/unix/ext/process.rs b/src/libstd/sys/unix/ext/process.rs index 8c9d0a86583..45d0d62a015 100644 --- a/src/libstd/sys/unix/ext/process.rs +++ b/src/libstd/sys/unix/ext/process.rs @@ -58,7 +58,7 @@ pub trait ExitStatusExt { impl ExitStatusExt for process::ExitStatus { fn signal(&self) -> Option { match *self.as_inner() { - sys::process2::ExitStatus::Signal(s) => Some(s), + sys::process::ExitStatus::Signal(s) => Some(s), _ => None } } diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs new file mode 100644 index 00000000000..350161c751c --- /dev/null +++ b/src/libstd/sys/unix/fs.rs @@ -0,0 +1,528 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::prelude::*; +use io::prelude::*; +use os::unix::prelude::*; + +use ffi::{CString, CStr, OsString, OsStr}; +use fmt; +use io::{self, Error, SeekFrom}; +use libc::{self, c_int, size_t, off_t, c_char, mode_t}; +use mem; +use path::{Path, PathBuf}; +use ptr; +use sync::Arc; +use sys::fd::FileDesc; +use sys::platform::raw; +use sys::{c, cvt, cvt_r}; +use sys_common::{AsInner, FromInner}; +use vec::Vec; + +pub struct File(FileDesc); + +pub struct FileAttr { + stat: raw::stat, +} + +pub struct ReadDir { + dirp: Dir, + root: Arc, +} + +struct Dir(*mut libc::DIR); + +unsafe impl Send for Dir {} +unsafe impl Sync for Dir {} + +pub struct DirEntry { + buf: Vec, // actually *mut libc::dirent_t + root: Arc, +} + +#[derive(Clone)] +pub struct OpenOptions { + flags: c_int, + read: bool, + write: bool, + mode: mode_t, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { mode: mode_t } + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct FileType { mode: mode_t } + +pub struct DirBuilder { mode: mode_t } + +impl FileAttr { + pub fn size(&self) -> u64 { self.stat.st_size as u64 } + pub fn perm(&self) -> FilePermissions { + FilePermissions { mode: (self.stat.st_mode as mode_t) & 0o777 } + } + + pub fn accessed(&self) -> u64 { + self.mktime(self.stat.st_atime as u64, self.stat.st_atime_nsec as u64) + } + pub fn modified(&self) -> u64 { + self.mktime(self.stat.st_mtime as u64, self.stat.st_mtime_nsec as u64) + } + + pub fn file_type(&self) -> FileType { + FileType { mode: self.stat.st_mode as mode_t } + } + + pub fn raw(&self) -> &raw::stat { &self.stat } + + // times are in milliseconds (currently) + fn mktime(&self, secs: u64, nsecs: u64) -> u64 { + secs * 1000 + nsecs / 1000000 + } +} + +impl AsInner for FileAttr { + fn as_inner(&self) -> &raw::stat { &self.stat } +} + +#[unstable(feature = "metadata_ext", reason = "recently added API")] +pub trait MetadataExt { + fn as_raw_stat(&self) -> &raw::stat; +} + +impl MetadataExt for ::fs::Metadata { + fn as_raw_stat(&self) -> &raw::stat { &self.as_inner().stat } +} + +impl MetadataExt for ::os::unix::fs::Metadata { + fn as_raw_stat(&self) -> &raw::stat { self.as_inner() } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { self.mode & 0o222 == 0 } + pub fn set_readonly(&mut self, readonly: bool) { + if readonly { + self.mode &= !0o222; + } else { + self.mode |= 0o222; + } + } + pub fn mode(&self) -> raw::mode_t { self.mode } +} + +impl FileType { + pub fn is_dir(&self) -> bool { self.is(libc::S_IFDIR) } + pub fn is_file(&self) -> bool { self.is(libc::S_IFREG) } + pub fn is_symlink(&self) -> bool { self.is(libc::S_IFLNK) } + + fn is(&self, mode: mode_t) -> bool { self.mode & libc::S_IFMT == mode } +} + +impl FromInner for FilePermissions { + fn from_inner(mode: raw::mode_t) -> FilePermissions { + FilePermissions { mode: mode as mode_t } + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + extern { + fn rust_dirent_t_size() -> c_int; + } + + let mut buf: Vec = Vec::with_capacity(unsafe { + rust_dirent_t_size() as usize + }); + let ptr = buf.as_mut_ptr() as *mut libc::dirent_t; + + let mut entry_ptr = ptr::null_mut(); + loop { + if unsafe { libc::readdir_r(self.dirp.0, ptr, &mut entry_ptr) != 0 } { + return Some(Err(Error::last_os_error())) + } + if entry_ptr.is_null() { + return None + } + + let entry = DirEntry { + buf: buf, + root: self.root.clone() + }; + if entry.name_bytes() == b"." || entry.name_bytes() == b".." { + buf = entry.buf; + } else { + return Some(Ok(entry)) + } + } + } +} + +impl Drop for Dir { + fn drop(&mut self) { + let r = unsafe { libc::closedir(self.0) }; + debug_assert_eq!(r, 0); + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.root.join(::from_bytes(self.name_bytes())) + } + + pub fn file_name(&self) -> OsString { + OsStr::from_bytes(self.name_bytes()).to_os_string() + } + + pub fn metadata(&self) -> io::Result { + lstat(&self.path()) + } + + pub fn file_type(&self) -> io::Result { + extern { + fn rust_dir_get_mode(ptr: *mut libc::dirent_t) -> c_int; + } + unsafe { + match rust_dir_get_mode(self.dirent()) { + -1 => lstat(&self.path()).map(|m| m.file_type()), + n => Ok(FileType { mode: n as mode_t }), + } + } + } + + pub fn ino(&self) -> raw::ino_t { + extern { + fn rust_dir_get_ino(ptr: *mut libc::dirent_t) -> raw::ino_t; + } + unsafe { rust_dir_get_ino(self.dirent()) } + } + + fn name_bytes(&self) -> &[u8] { + extern { + fn rust_list_dir_val(ptr: *mut libc::dirent_t) -> *const c_char; + } + unsafe { + CStr::from_ptr(rust_list_dir_val(self.dirent())).to_bytes() + } + } + + fn dirent(&self) -> *mut libc::dirent_t { + self.buf.as_ptr() as *mut _ + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { + flags: 0, + read: false, + write: false, + mode: 0o666, + } + } + + pub fn read(&mut self, read: bool) { + self.read = read; + } + + pub fn write(&mut self, write: bool) { + self.write = write; + } + + pub fn append(&mut self, append: bool) { + self.flag(libc::O_APPEND, append); + } + + pub fn truncate(&mut self, truncate: bool) { + self.flag(libc::O_TRUNC, truncate); + } + + pub fn create(&mut self, create: bool) { + self.flag(libc::O_CREAT, create); + } + + pub fn mode(&mut self, mode: raw::mode_t) { + self.mode = mode as mode_t; + } + + fn flag(&mut self, bit: c_int, on: bool) { + if on { + self.flags |= bit; + } else { + self.flags &= !bit; + } + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + let path = try!(cstr(path)); + File::open_c(&path, opts) + } + + pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result { + let flags = opts.flags | match (opts.read, opts.write) { + (true, true) => libc::O_RDWR, + (false, true) => libc::O_WRONLY, + (true, false) | + (false, false) => libc::O_RDONLY, + }; + let fd = try!(cvt_r(|| unsafe { + libc::open(path.as_ptr(), flags, opts.mode) + })); + let fd = FileDesc::new(fd); + fd.set_cloexec(); + Ok(File(fd)) + } + + pub fn into_fd(self) -> FileDesc { self.0 } + + pub fn file_attr(&self) -> io::Result { + let mut stat: raw::stat = unsafe { mem::zeroed() }; + try!(cvt(unsafe { + libc::fstat(self.0.raw(), &mut stat as *mut _ as *mut _) + })); + Ok(FileAttr { stat: stat }) + } + + pub fn fsync(&self) -> io::Result<()> { + try!(cvt_r(|| unsafe { libc::fsync(self.0.raw()) })); + Ok(()) + } + + pub fn datasync(&self) -> io::Result<()> { + try!(cvt_r(|| unsafe { os_datasync(self.0.raw()) })); + return Ok(()); + + #[cfg(any(target_os = "macos", target_os = "ios"))] + unsafe fn os_datasync(fd: c_int) -> c_int { + libc::fcntl(fd, libc::F_FULLFSYNC) + } + #[cfg(target_os = "linux")] + unsafe fn os_datasync(fd: c_int) -> c_int { libc::fdatasync(fd) } + #[cfg(not(any(target_os = "macos", + target_os = "ios", + target_os = "linux")))] + unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) } + } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + try!(cvt_r(|| unsafe { + libc::ftruncate(self.0.raw(), size as libc::off_t) + })); + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + pub fn flush(&self) -> io::Result<()> { Ok(()) } + + pub fn seek(&self, pos: SeekFrom) -> io::Result { + let (whence, pos) = match pos { + SeekFrom::Start(off) => (libc::SEEK_SET, off as off_t), + SeekFrom::End(off) => (libc::SEEK_END, off as off_t), + SeekFrom::Current(off) => (libc::SEEK_CUR, off as off_t), + }; + let n = try!(cvt(unsafe { libc::lseek(self.0.raw(), pos, whence) })); + Ok(n as u64) + } + + pub fn fd(&self) -> &FileDesc { &self.0 } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder { mode: 0o777 } + } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + let p = try!(cstr(p)); + try!(cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })); + Ok(()) + } + + pub fn set_mode(&mut self, mode: mode_t) { + self.mode = mode; + } +} + +fn cstr(path: &Path) -> io::Result { + path.as_os_str().to_cstring().ok_or( + io::Error::new(io::ErrorKind::InvalidInput, "path contained a null")) +} + +impl FromInner for File { + fn from_inner(fd: c_int) -> File { + File(FileDesc::new(fd)) + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + #[cfg(target_os = "linux")] + fn get_path(fd: c_int) -> Option { + use string::ToString; + let mut p = PathBuf::from("/proc/self/fd"); + p.push(&fd.to_string()); + readlink(&p).ok() + } + + #[cfg(not(target_os = "linux"))] + fn get_path(_fd: c_int) -> Option { + // FIXME(#24570): implement this for other Unix platforms + None + } + + #[cfg(target_os = "linux")] + fn get_mode(fd: c_int) -> Option<(bool, bool)> { + let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) }; + if mode == -1 { + return None; + } + match mode & libc::O_ACCMODE { + libc::O_RDONLY => Some((true, false)), + libc::O_RDWR => Some((true, true)), + libc::O_WRONLY => Some((false, true)), + _ => None + } + } + + #[cfg(not(target_os = "linux"))] + fn get_mode(_fd: c_int) -> Option<(bool, bool)> { + // FIXME(#24570): implement this for other Unix platforms + None + } + + let fd = self.0.raw(); + let mut b = f.debug_struct("File").field("fd", &fd); + if let Some(path) = get_path(fd) { + b = b.field("path", &path); + } + if let Some((read, write)) = get_mode(fd) { + b = b.field("read", &read).field("write", &write); + } + b.finish() + } +} + +pub fn readdir(p: &Path) -> io::Result { + let root = Arc::new(p.to_path_buf()); + let p = try!(cstr(p)); + unsafe { + let ptr = libc::opendir(p.as_ptr()); + if ptr.is_null() { + Err(Error::last_os_error()) + } else { + Ok(ReadDir { dirp: Dir(ptr), root: root }) + } + } +} + +pub fn unlink(p: &Path) -> io::Result<()> { + let p = try!(cstr(p)); + try!(cvt(unsafe { libc::unlink(p.as_ptr()) })); + Ok(()) +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + let old = try!(cstr(old)); + let new = try!(cstr(new)); + try!(cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })); + Ok(()) +} + +pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + let p = try!(cstr(p)); + try!(cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })); + Ok(()) +} + +pub fn rmdir(p: &Path) -> io::Result<()> { + let p = try!(cstr(p)); + try!(cvt(unsafe { libc::rmdir(p.as_ptr()) })); + Ok(()) +} + +pub fn readlink(p: &Path) -> io::Result { + let c_path = try!(cstr(p)); + let p = c_path.as_ptr(); + let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) }; + if len < 0 { + len = 1024; // FIXME: read PATH_MAX from C ffi? + } + let mut buf: Vec = Vec::with_capacity(len as usize); + unsafe { + let n = try!(cvt({ + libc::readlink(p, buf.as_ptr() as *mut c_char, len as size_t) + })); + buf.set_len(n as usize); + } + Ok(PathBuf::from(OsString::from_vec(buf))) +} + +pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { + let src = try!(cstr(src)); + let dst = try!(cstr(dst)); + try!(cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })); + Ok(()) +} + +pub fn link(src: &Path, dst: &Path) -> io::Result<()> { + let src = try!(cstr(src)); + let dst = try!(cstr(dst)); + try!(cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })); + Ok(()) +} + +pub fn stat(p: &Path) -> io::Result { + let p = try!(cstr(p)); + let mut stat: raw::stat = unsafe { mem::zeroed() }; + try!(cvt(unsafe { + libc::stat(p.as_ptr(), &mut stat as *mut _ as *mut _) + })); + Ok(FileAttr { stat: stat }) +} + +pub fn lstat(p: &Path) -> io::Result { + let p = try!(cstr(p)); + let mut stat: raw::stat = unsafe { mem::zeroed() }; + try!(cvt(unsafe { + libc::lstat(p.as_ptr(), &mut stat as *mut _ as *mut _) + })); + Ok(FileAttr { stat: stat }) +} + +pub fn utimes(p: &Path, atime: u64, mtime: u64) -> io::Result<()> { + let p = try!(cstr(p)); + let buf = [super::ms_to_timeval(atime), super::ms_to_timeval(mtime)]; + try!(cvt(unsafe { c::utimes(p.as_ptr(), buf.as_ptr()) })); + Ok(()) +} + +pub fn canonicalize(p: &Path) -> io::Result { + let path = try!(CString::new(p.as_os_str().as_bytes())); + let mut buf = vec![0u8; 16 * 1024]; + unsafe { + let r = c::realpath(path.as_ptr(), buf.as_mut_ptr() as *mut _); + if r.is_null() { + return Err(io::Error::last_os_error()) + } + } + let p = buf.iter().position(|i| *i == 0).unwrap(); + buf.truncate(p); + Ok(PathBuf::from(OsString::from_vec(buf))) +} diff --git a/src/libstd/sys/unix/fs2.rs b/src/libstd/sys/unix/fs2.rs deleted file mode 100644 index 350161c751c..00000000000 --- a/src/libstd/sys/unix/fs2.rs +++ /dev/null @@ -1,528 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use core::prelude::*; -use io::prelude::*; -use os::unix::prelude::*; - -use ffi::{CString, CStr, OsString, OsStr}; -use fmt; -use io::{self, Error, SeekFrom}; -use libc::{self, c_int, size_t, off_t, c_char, mode_t}; -use mem; -use path::{Path, PathBuf}; -use ptr; -use sync::Arc; -use sys::fd::FileDesc; -use sys::platform::raw; -use sys::{c, cvt, cvt_r}; -use sys_common::{AsInner, FromInner}; -use vec::Vec; - -pub struct File(FileDesc); - -pub struct FileAttr { - stat: raw::stat, -} - -pub struct ReadDir { - dirp: Dir, - root: Arc, -} - -struct Dir(*mut libc::DIR); - -unsafe impl Send for Dir {} -unsafe impl Sync for Dir {} - -pub struct DirEntry { - buf: Vec, // actually *mut libc::dirent_t - root: Arc, -} - -#[derive(Clone)] -pub struct OpenOptions { - flags: c_int, - read: bool, - write: bool, - mode: mode_t, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct FilePermissions { mode: mode_t } - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct FileType { mode: mode_t } - -pub struct DirBuilder { mode: mode_t } - -impl FileAttr { - pub fn size(&self) -> u64 { self.stat.st_size as u64 } - pub fn perm(&self) -> FilePermissions { - FilePermissions { mode: (self.stat.st_mode as mode_t) & 0o777 } - } - - pub fn accessed(&self) -> u64 { - self.mktime(self.stat.st_atime as u64, self.stat.st_atime_nsec as u64) - } - pub fn modified(&self) -> u64 { - self.mktime(self.stat.st_mtime as u64, self.stat.st_mtime_nsec as u64) - } - - pub fn file_type(&self) -> FileType { - FileType { mode: self.stat.st_mode as mode_t } - } - - pub fn raw(&self) -> &raw::stat { &self.stat } - - // times are in milliseconds (currently) - fn mktime(&self, secs: u64, nsecs: u64) -> u64 { - secs * 1000 + nsecs / 1000000 - } -} - -impl AsInner for FileAttr { - fn as_inner(&self) -> &raw::stat { &self.stat } -} - -#[unstable(feature = "metadata_ext", reason = "recently added API")] -pub trait MetadataExt { - fn as_raw_stat(&self) -> &raw::stat; -} - -impl MetadataExt for ::fs::Metadata { - fn as_raw_stat(&self) -> &raw::stat { &self.as_inner().stat } -} - -impl MetadataExt for ::os::unix::fs::Metadata { - fn as_raw_stat(&self) -> &raw::stat { self.as_inner() } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { self.mode & 0o222 == 0 } - pub fn set_readonly(&mut self, readonly: bool) { - if readonly { - self.mode &= !0o222; - } else { - self.mode |= 0o222; - } - } - pub fn mode(&self) -> raw::mode_t { self.mode } -} - -impl FileType { - pub fn is_dir(&self) -> bool { self.is(libc::S_IFDIR) } - pub fn is_file(&self) -> bool { self.is(libc::S_IFREG) } - pub fn is_symlink(&self) -> bool { self.is(libc::S_IFLNK) } - - fn is(&self, mode: mode_t) -> bool { self.mode & libc::S_IFMT == mode } -} - -impl FromInner for FilePermissions { - fn from_inner(mode: raw::mode_t) -> FilePermissions { - FilePermissions { mode: mode as mode_t } - } -} - -impl Iterator for ReadDir { - type Item = io::Result; - - fn next(&mut self) -> Option> { - extern { - fn rust_dirent_t_size() -> c_int; - } - - let mut buf: Vec = Vec::with_capacity(unsafe { - rust_dirent_t_size() as usize - }); - let ptr = buf.as_mut_ptr() as *mut libc::dirent_t; - - let mut entry_ptr = ptr::null_mut(); - loop { - if unsafe { libc::readdir_r(self.dirp.0, ptr, &mut entry_ptr) != 0 } { - return Some(Err(Error::last_os_error())) - } - if entry_ptr.is_null() { - return None - } - - let entry = DirEntry { - buf: buf, - root: self.root.clone() - }; - if entry.name_bytes() == b"." || entry.name_bytes() == b".." { - buf = entry.buf; - } else { - return Some(Ok(entry)) - } - } - } -} - -impl Drop for Dir { - fn drop(&mut self) { - let r = unsafe { libc::closedir(self.0) }; - debug_assert_eq!(r, 0); - } -} - -impl DirEntry { - pub fn path(&self) -> PathBuf { - self.root.join(::from_bytes(self.name_bytes())) - } - - pub fn file_name(&self) -> OsString { - OsStr::from_bytes(self.name_bytes()).to_os_string() - } - - pub fn metadata(&self) -> io::Result { - lstat(&self.path()) - } - - pub fn file_type(&self) -> io::Result { - extern { - fn rust_dir_get_mode(ptr: *mut libc::dirent_t) -> c_int; - } - unsafe { - match rust_dir_get_mode(self.dirent()) { - -1 => lstat(&self.path()).map(|m| m.file_type()), - n => Ok(FileType { mode: n as mode_t }), - } - } - } - - pub fn ino(&self) -> raw::ino_t { - extern { - fn rust_dir_get_ino(ptr: *mut libc::dirent_t) -> raw::ino_t; - } - unsafe { rust_dir_get_ino(self.dirent()) } - } - - fn name_bytes(&self) -> &[u8] { - extern { - fn rust_list_dir_val(ptr: *mut libc::dirent_t) -> *const c_char; - } - unsafe { - CStr::from_ptr(rust_list_dir_val(self.dirent())).to_bytes() - } - } - - fn dirent(&self) -> *mut libc::dirent_t { - self.buf.as_ptr() as *mut _ - } -} - -impl OpenOptions { - pub fn new() -> OpenOptions { - OpenOptions { - flags: 0, - read: false, - write: false, - mode: 0o666, - } - } - - pub fn read(&mut self, read: bool) { - self.read = read; - } - - pub fn write(&mut self, write: bool) { - self.write = write; - } - - pub fn append(&mut self, append: bool) { - self.flag(libc::O_APPEND, append); - } - - pub fn truncate(&mut self, truncate: bool) { - self.flag(libc::O_TRUNC, truncate); - } - - pub fn create(&mut self, create: bool) { - self.flag(libc::O_CREAT, create); - } - - pub fn mode(&mut self, mode: raw::mode_t) { - self.mode = mode as mode_t; - } - - fn flag(&mut self, bit: c_int, on: bool) { - if on { - self.flags |= bit; - } else { - self.flags &= !bit; - } - } -} - -impl File { - pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { - let path = try!(cstr(path)); - File::open_c(&path, opts) - } - - pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result { - let flags = opts.flags | match (opts.read, opts.write) { - (true, true) => libc::O_RDWR, - (false, true) => libc::O_WRONLY, - (true, false) | - (false, false) => libc::O_RDONLY, - }; - let fd = try!(cvt_r(|| unsafe { - libc::open(path.as_ptr(), flags, opts.mode) - })); - let fd = FileDesc::new(fd); - fd.set_cloexec(); - Ok(File(fd)) - } - - pub fn into_fd(self) -> FileDesc { self.0 } - - pub fn file_attr(&self) -> io::Result { - let mut stat: raw::stat = unsafe { mem::zeroed() }; - try!(cvt(unsafe { - libc::fstat(self.0.raw(), &mut stat as *mut _ as *mut _) - })); - Ok(FileAttr { stat: stat }) - } - - pub fn fsync(&self) -> io::Result<()> { - try!(cvt_r(|| unsafe { libc::fsync(self.0.raw()) })); - Ok(()) - } - - pub fn datasync(&self) -> io::Result<()> { - try!(cvt_r(|| unsafe { os_datasync(self.0.raw()) })); - return Ok(()); - - #[cfg(any(target_os = "macos", target_os = "ios"))] - unsafe fn os_datasync(fd: c_int) -> c_int { - libc::fcntl(fd, libc::F_FULLFSYNC) - } - #[cfg(target_os = "linux")] - unsafe fn os_datasync(fd: c_int) -> c_int { libc::fdatasync(fd) } - #[cfg(not(any(target_os = "macos", - target_os = "ios", - target_os = "linux")))] - unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) } - } - - pub fn truncate(&self, size: u64) -> io::Result<()> { - try!(cvt_r(|| unsafe { - libc::ftruncate(self.0.raw(), size as libc::off_t) - })); - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn flush(&self) -> io::Result<()> { Ok(()) } - - pub fn seek(&self, pos: SeekFrom) -> io::Result { - let (whence, pos) = match pos { - SeekFrom::Start(off) => (libc::SEEK_SET, off as off_t), - SeekFrom::End(off) => (libc::SEEK_END, off as off_t), - SeekFrom::Current(off) => (libc::SEEK_CUR, off as off_t), - }; - let n = try!(cvt(unsafe { libc::lseek(self.0.raw(), pos, whence) })); - Ok(n as u64) - } - - pub fn fd(&self) -> &FileDesc { &self.0 } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder { mode: 0o777 } - } - - pub fn mkdir(&self, p: &Path) -> io::Result<()> { - let p = try!(cstr(p)); - try!(cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })); - Ok(()) - } - - pub fn set_mode(&mut self, mode: mode_t) { - self.mode = mode; - } -} - -fn cstr(path: &Path) -> io::Result { - path.as_os_str().to_cstring().ok_or( - io::Error::new(io::ErrorKind::InvalidInput, "path contained a null")) -} - -impl FromInner for File { - fn from_inner(fd: c_int) -> File { - File(FileDesc::new(fd)) - } -} - -impl fmt::Debug for File { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - #[cfg(target_os = "linux")] - fn get_path(fd: c_int) -> Option { - use string::ToString; - let mut p = PathBuf::from("/proc/self/fd"); - p.push(&fd.to_string()); - readlink(&p).ok() - } - - #[cfg(not(target_os = "linux"))] - fn get_path(_fd: c_int) -> Option { - // FIXME(#24570): implement this for other Unix platforms - None - } - - #[cfg(target_os = "linux")] - fn get_mode(fd: c_int) -> Option<(bool, bool)> { - let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) }; - if mode == -1 { - return None; - } - match mode & libc::O_ACCMODE { - libc::O_RDONLY => Some((true, false)), - libc::O_RDWR => Some((true, true)), - libc::O_WRONLY => Some((false, true)), - _ => None - } - } - - #[cfg(not(target_os = "linux"))] - fn get_mode(_fd: c_int) -> Option<(bool, bool)> { - // FIXME(#24570): implement this for other Unix platforms - None - } - - let fd = self.0.raw(); - let mut b = f.debug_struct("File").field("fd", &fd); - if let Some(path) = get_path(fd) { - b = b.field("path", &path); - } - if let Some((read, write)) = get_mode(fd) { - b = b.field("read", &read).field("write", &write); - } - b.finish() - } -} - -pub fn readdir(p: &Path) -> io::Result { - let root = Arc::new(p.to_path_buf()); - let p = try!(cstr(p)); - unsafe { - let ptr = libc::opendir(p.as_ptr()); - if ptr.is_null() { - Err(Error::last_os_error()) - } else { - Ok(ReadDir { dirp: Dir(ptr), root: root }) - } - } -} - -pub fn unlink(p: &Path) -> io::Result<()> { - let p = try!(cstr(p)); - try!(cvt(unsafe { libc::unlink(p.as_ptr()) })); - Ok(()) -} - -pub fn rename(old: &Path, new: &Path) -> io::Result<()> { - let old = try!(cstr(old)); - let new = try!(cstr(new)); - try!(cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })); - Ok(()) -} - -pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { - let p = try!(cstr(p)); - try!(cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })); - Ok(()) -} - -pub fn rmdir(p: &Path) -> io::Result<()> { - let p = try!(cstr(p)); - try!(cvt(unsafe { libc::rmdir(p.as_ptr()) })); - Ok(()) -} - -pub fn readlink(p: &Path) -> io::Result { - let c_path = try!(cstr(p)); - let p = c_path.as_ptr(); - let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) }; - if len < 0 { - len = 1024; // FIXME: read PATH_MAX from C ffi? - } - let mut buf: Vec = Vec::with_capacity(len as usize); - unsafe { - let n = try!(cvt({ - libc::readlink(p, buf.as_ptr() as *mut c_char, len as size_t) - })); - buf.set_len(n as usize); - } - Ok(PathBuf::from(OsString::from_vec(buf))) -} - -pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { - let src = try!(cstr(src)); - let dst = try!(cstr(dst)); - try!(cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })); - Ok(()) -} - -pub fn link(src: &Path, dst: &Path) -> io::Result<()> { - let src = try!(cstr(src)); - let dst = try!(cstr(dst)); - try!(cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })); - Ok(()) -} - -pub fn stat(p: &Path) -> io::Result { - let p = try!(cstr(p)); - let mut stat: raw::stat = unsafe { mem::zeroed() }; - try!(cvt(unsafe { - libc::stat(p.as_ptr(), &mut stat as *mut _ as *mut _) - })); - Ok(FileAttr { stat: stat }) -} - -pub fn lstat(p: &Path) -> io::Result { - let p = try!(cstr(p)); - let mut stat: raw::stat = unsafe { mem::zeroed() }; - try!(cvt(unsafe { - libc::lstat(p.as_ptr(), &mut stat as *mut _ as *mut _) - })); - Ok(FileAttr { stat: stat }) -} - -pub fn utimes(p: &Path, atime: u64, mtime: u64) -> io::Result<()> { - let p = try!(cstr(p)); - let buf = [super::ms_to_timeval(atime), super::ms_to_timeval(mtime)]; - try!(cvt(unsafe { c::utimes(p.as_ptr(), buf.as_ptr()) })); - Ok(()) -} - -pub fn canonicalize(p: &Path) -> io::Result { - let path = try!(CString::new(p.as_os_str().as_bytes())); - let mut buf = vec![0u8; 16 * 1024]; - unsafe { - let r = c::realpath(path.as_ptr(), buf.as_mut_ptr() as *mut _); - if r.is_null() { - return Err(io::Error::last_os_error()) - } - } - let p = buf.iter().position(|i| *i == 0).unwrap(); - buf.truncate(p); - Ok(PathBuf::from(OsString::from_vec(buf))) -} diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 78b798d3bff..c1a4e8cee9e 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -33,13 +33,13 @@ pub mod c; pub mod condvar; pub mod ext; pub mod fd; -pub mod fs2; +pub mod fs; pub mod mutex; pub mod net; pub mod os; pub mod os_str; -pub mod pipe2; -pub mod process2; +pub mod pipe; +pub mod process; pub mod rwlock; pub mod stack_overflow; pub mod sync; diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs new file mode 100644 index 00000000000..e9d8c69fefb --- /dev/null +++ b/src/libstd/sys/unix/pipe.rs @@ -0,0 +1,50 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use sys::fd::FileDesc; +use io; +use libc; + +//////////////////////////////////////////////////////////////////////////////// +// Anonymous pipes +//////////////////////////////////////////////////////////////////////////////// + +pub struct AnonPipe(FileDesc); + +pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { + let mut fds = [0; 2]; + if unsafe { libc::pipe(fds.as_mut_ptr()) == 0 } { + Ok((AnonPipe::from_fd(fds[0]), AnonPipe::from_fd(fds[1]))) + } else { + Err(io::Error::last_os_error()) + } +} + +impl AnonPipe { + pub fn from_fd(fd: libc::c_int) -> AnonPipe { + let fd = FileDesc::new(fd); + fd.set_cloexec(); + AnonPipe(fd) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + pub fn into_fd(self) -> FileDesc { + self.0 + } +} diff --git a/src/libstd/sys/unix/pipe2.rs b/src/libstd/sys/unix/pipe2.rs deleted file mode 100644 index e9d8c69fefb..00000000000 --- a/src/libstd/sys/unix/pipe2.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use prelude::v1::*; - -use sys::fd::FileDesc; -use io; -use libc; - -//////////////////////////////////////////////////////////////////////////////// -// Anonymous pipes -//////////////////////////////////////////////////////////////////////////////// - -pub struct AnonPipe(FileDesc); - -pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { - let mut fds = [0; 2]; - if unsafe { libc::pipe(fds.as_mut_ptr()) == 0 } { - Ok((AnonPipe::from_fd(fds[0]), AnonPipe::from_fd(fds[1]))) - } else { - Err(io::Error::last_os_error()) - } -} - -impl AnonPipe { - pub fn from_fd(fd: libc::c_int) -> AnonPipe { - let fd = FileDesc::new(fd); - fd.set_cloexec(); - AnonPipe(fd) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn into_fd(self) -> FileDesc { - self.0 - } -} diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs new file mode 100644 index 00000000000..290310f4ad9 --- /dev/null +++ b/src/libstd/sys/unix/process.rs @@ -0,0 +1,414 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; +use os::unix::prelude::*; + +use collections::HashMap; +use env; +use ffi::{OsString, OsStr, CString, CStr}; +use fmt; +use io::{self, Error, ErrorKind}; +use libc::{self, pid_t, c_void, c_int, gid_t, uid_t}; +use ptr; +use sys::pipe::AnonPipe; +use sys::{self, c, cvt, cvt_r}; +use sys::fs::{File, OpenOptions}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +#[derive(Clone)] +pub struct Command { + pub program: CString, + pub args: Vec, + pub env: Option>, + pub cwd: Option, + pub uid: Option, + pub gid: Option, + pub detach: bool, // not currently exposed in std::process +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + Command { + program: program.to_cstring().unwrap(), + args: Vec::new(), + env: None, + cwd: None, + uid: None, + gid: None, + detach: false, + } + } + + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(arg.to_cstring().unwrap()) + } + pub fn args<'a, I: Iterator>(&mut self, args: I) { + self.args.extend(args.map(|s| s.to_cstring().unwrap())) + } + fn init_env_map(&mut self) { + if self.env.is_none() { + self.env = Some(env::vars_os().collect()); + } + } + pub fn env(&mut self, key: &OsStr, val: &OsStr) { + self.init_env_map(); + self.env.as_mut().unwrap().insert(key.to_os_string(), val.to_os_string()); + } + pub fn env_remove(&mut self, key: &OsStr) { + self.init_env_map(); + self.env.as_mut().unwrap().remove(&key.to_os_string()); + } + pub fn env_clear(&mut self) { + self.env = Some(HashMap::new()) + } + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(dir.to_cstring().unwrap()) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +/// Unix exit statuses +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum ExitStatus { + /// Normal termination with an exit code. + Code(i32), + + /// Termination by signal, with the signal number. + /// + /// Never generated on Windows. + Signal(i32), +} + +impl ExitStatus { + pub fn success(&self) -> bool { + *self == ExitStatus::Code(0) + } + pub fn code(&self) -> Option { + match *self { + ExitStatus::Code(c) => Some(c), + _ => None + } + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ExitStatus::Code(code) => write!(f, "exit code: {}", code), + ExitStatus::Signal(code) => write!(f, "signal: {}", code), + } + } +} + +/// The unique id of the process (this should never be negative). +pub struct Process { + pid: pid_t +} + +pub enum Stdio { + Inherit, + Piped(AnonPipe), + None, +} + +const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; + +impl Process { + pub unsafe fn kill(&self) -> io::Result<()> { + try!(cvt(libc::funcs::posix88::signal::kill(self.pid, libc::SIGKILL))); + Ok(()) + } + + pub fn spawn(cfg: &Command, + in_fd: Stdio, + out_fd: Stdio, + err_fd: Stdio) -> io::Result { + let dirp = cfg.cwd.as_ref().map(|c| c.as_ptr()).unwrap_or(ptr::null()); + + let (envp, _a, _b) = make_envp(cfg.env.as_ref()); + let (argv, _a) = make_argv(&cfg.program, &cfg.args); + let (input, output) = try!(sys::pipe::anon_pipe()); + + let pid = unsafe { + match libc::fork() { + 0 => { + drop(input); + Process::child_after_fork(cfg, output, argv, envp, dirp, + in_fd, out_fd, err_fd) + } + n if n < 0 => return Err(Error::last_os_error()), + n => n, + } + }; + + let p = Process{ pid: pid }; + drop(output); + let mut bytes = [0; 8]; + + // loop to handle EINTR + loop { + match input.read(&mut bytes) { + Ok(0) => return Ok(p), + Ok(8) => { + assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]), + "Validation on the CLOEXEC pipe failed: {:?}", bytes); + let errno = combine(&bytes[0.. 4]); + assert!(p.wait().is_ok(), + "wait() should either return Ok or panic"); + return Err(Error::from_raw_os_error(errno)) + } + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => { + assert!(p.wait().is_ok(), + "wait() should either return Ok or panic"); + panic!("the CLOEXEC pipe failed: {:?}", e) + }, + Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic + assert!(p.wait().is_ok(), + "wait() should either return Ok or panic"); + panic!("short read on the CLOEXEC pipe") + } + } + } + + fn combine(arr: &[u8]) -> i32 { + let a = arr[0] as u32; + let b = arr[1] as u32; + let c = arr[2] as u32; + let d = arr[3] as u32; + + ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 + } + } + + // And at this point we've reached a special time in the life of the + // child. The child must now be considered hamstrung and unable to + // do anything other than syscalls really. Consider the following + // scenario: + // + // 1. Thread A of process 1 grabs the malloc() mutex + // 2. Thread B of process 1 forks(), creating thread C + // 3. Thread C of process 2 then attempts to malloc() + // 4. The memory of process 2 is the same as the memory of + // process 1, so the mutex is locked. + // + // This situation looks a lot like deadlock, right? It turns out + // that this is what pthread_atfork() takes care of, which is + // presumably implemented across platforms. The first thing that + // threads to *before* forking is to do things like grab the malloc + // mutex, and then after the fork they unlock it. + // + // Despite this information, libnative's spawn has been witnessed to + // deadlock on both OSX and FreeBSD. I'm not entirely sure why, but + // all collected backtraces point at malloc/free traffic in the + // child spawned process. + // + // For this reason, the block of code below should contain 0 + // invocations of either malloc of free (or their related friends). + // + // As an example of not having malloc/free traffic, we don't close + // this file descriptor by dropping the FileDesc (which contains an + // allocation). Instead we just close it manually. This will never + // have the drop glue anyway because this code never returns (the + // child will either exec() or invoke libc::exit) + unsafe fn child_after_fork(cfg: &Command, + mut output: AnonPipe, + argv: *const *const libc::c_char, + envp: *const libc::c_void, + dirp: *const libc::c_char, + in_fd: Stdio, + out_fd: Stdio, + err_fd: Stdio) -> ! { + fn fail(output: &mut AnonPipe) -> ! { + let errno = sys::os::errno() as u32; + let bytes = [ + (errno >> 24) as u8, + (errno >> 16) as u8, + (errno >> 8) as u8, + (errno >> 0) as u8, + CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], + CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] + ]; + // pipe I/O up to PIPE_BUF bytes should be atomic, and then we want + // to be sure we *don't* run at_exit destructors as we're being torn + // down regardless + assert!(output.write(&bytes).is_ok()); + unsafe { libc::_exit(1) } + } + + let setup = |src: Stdio, dst: c_int| { + let fd = match src { + Stdio::Inherit => return true, + Stdio::Piped(pipe) => pipe.into_fd(), + + // If a stdio file descriptor is set to be ignored, we open up + // /dev/null into that file descriptor. Otherwise, the first + // file descriptor opened up in the child would be numbered as + // one of the stdio file descriptors, which is likely to wreak + // havoc. + Stdio::None => { + let mut opts = OpenOptions::new(); + opts.read(dst == libc::STDIN_FILENO); + opts.write(dst != libc::STDIN_FILENO); + let devnull = CStr::from_ptr(b"/dev/null\0".as_ptr() + as *const _); + if let Ok(f) = File::open_c(devnull, &opts) { + f.into_fd() + } else { + return false + } + } + }; + cvt_r(|| libc::dup2(fd.raw(), dst)).is_ok() + }; + + if !setup(in_fd, libc::STDIN_FILENO) { fail(&mut output) } + if !setup(out_fd, libc::STDOUT_FILENO) { fail(&mut output) } + if !setup(err_fd, libc::STDERR_FILENO) { fail(&mut output) } + + if let Some(u) = cfg.gid { + if libc::setgid(u as libc::gid_t) != 0 { + fail(&mut output); + } + } + if let Some(u) = cfg.uid { + // When dropping privileges from root, the `setgroups` call + // will remove any extraneous groups. If we don't call this, + // then even though our uid has dropped, we may still have + // groups that enable us to do super-user things. This will + // fail if we aren't root, so don't bother checking the + // return value, this is just done as an optimistic + // privilege dropping function. + let _ = c::setgroups(0, ptr::null()); + + if libc::setuid(u as libc::uid_t) != 0 { + fail(&mut output); + } + } + if cfg.detach { + // Don't check the error of setsid because it fails if we're the + // process leader already. We just forked so it shouldn't return + // error, but ignore it anyway. + let _ = libc::setsid(); + } + if !dirp.is_null() && libc::chdir(dirp) == -1 { + fail(&mut output); + } + if !envp.is_null() { + *sys::os::environ() = envp as *const _; + } + let _ = libc::execvp(*argv, argv as *mut _); + fail(&mut output) + } + + pub fn wait(&self) -> io::Result { + let mut status = 0 as c_int; + try!(cvt_r(|| unsafe { c::waitpid(self.pid, &mut status, 0) })); + Ok(translate_status(status)) + } + + pub fn try_wait(&self) -> Option { + let mut status = 0 as c_int; + match cvt_r(|| unsafe { + c::waitpid(self.pid, &mut status, c::WNOHANG) + }) { + Ok(0) => None, + Ok(n) if n == self.pid => Some(translate_status(status)), + Ok(n) => panic!("unknown pid: {}", n), + Err(e) => panic!("unknown waitpid error: {}", e), + } + } +} + +fn make_argv(prog: &CString, args: &[CString]) + -> (*const *const libc::c_char, Vec<*const libc::c_char>) +{ + let mut ptrs: Vec<*const libc::c_char> = Vec::with_capacity(args.len()+1); + + // Convert the CStrings into an array of pointers. Note: the + // lifetime of the various CStrings involved is guaranteed to be + // larger than the lifetime of our invocation of cb, but this is + // technically unsafe as the callback could leak these pointers + // out of our scope. + ptrs.push(prog.as_ptr()); + ptrs.extend(args.iter().map(|tmp| tmp.as_ptr())); + + // Add a terminating null pointer (required by libc). + ptrs.push(ptr::null()); + + (ptrs.as_ptr(), ptrs) +} + +fn make_envp(env: Option<&HashMap>) + -> (*const c_void, Vec>, Vec<*const libc::c_char>) +{ + // On posixy systems we can pass a char** for envp, which is a + // null-terminated array of "k=v\0" strings. Since we must create + // these strings locally, yet expose a raw pointer to them, we + // create a temporary vector to own the CStrings that outlives the + // call to cb. + if let Some(env) = env { + let mut tmps = Vec::with_capacity(env.len()); + + for pair in env { + let mut kv = Vec::new(); + kv.push_all(pair.0.as_bytes()); + kv.push('=' as u8); + kv.push_all(pair.1.as_bytes()); + kv.push(0); // terminating null + tmps.push(kv); + } + + let mut ptrs: Vec<*const libc::c_char> = + tmps.iter() + .map(|tmp| tmp.as_ptr() as *const libc::c_char) + .collect(); + ptrs.push(ptr::null()); + + (ptrs.as_ptr() as *const _, tmps, ptrs) + } else { + (0 as *const _, Vec::new(), Vec::new()) + } +} + +fn translate_status(status: c_int) -> ExitStatus { + #![allow(non_snake_case)] + #[cfg(any(target_os = "linux", target_os = "android"))] + mod imp { + pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 } + pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff } + pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f } + } + + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "bitrig", + target_os = "openbsd"))] + mod imp { + pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 } + pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 } + pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 } + } + + if imp::WIFEXITED(status) { + ExitStatus::Code(imp::WEXITSTATUS(status)) + } else { + ExitStatus::Signal(imp::WTERMSIG(status)) + } +} diff --git a/src/libstd/sys/unix/process2.rs b/src/libstd/sys/unix/process2.rs deleted file mode 100644 index 6f3c0fd63aa..00000000000 --- a/src/libstd/sys/unix/process2.rs +++ /dev/null @@ -1,414 +0,0 @@ -// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use prelude::v1::*; -use os::unix::prelude::*; - -use collections::HashMap; -use env; -use ffi::{OsString, OsStr, CString, CStr}; -use fmt; -use io::{self, Error, ErrorKind}; -use libc::{self, pid_t, c_void, c_int, gid_t, uid_t}; -use ptr; -use sys::pipe2::AnonPipe; -use sys::{self, c, cvt, cvt_r}; -use sys::fs2::{File, OpenOptions}; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -#[derive(Clone)] -pub struct Command { - pub program: CString, - pub args: Vec, - pub env: Option>, - pub cwd: Option, - pub uid: Option, - pub gid: Option, - pub detach: bool, // not currently exposed in std::process -} - -impl Command { - pub fn new(program: &OsStr) -> Command { - Command { - program: program.to_cstring().unwrap(), - args: Vec::new(), - env: None, - cwd: None, - uid: None, - gid: None, - detach: false, - } - } - - pub fn arg(&mut self, arg: &OsStr) { - self.args.push(arg.to_cstring().unwrap()) - } - pub fn args<'a, I: Iterator>(&mut self, args: I) { - self.args.extend(args.map(|s| s.to_cstring().unwrap())) - } - fn init_env_map(&mut self) { - if self.env.is_none() { - self.env = Some(env::vars_os().collect()); - } - } - pub fn env(&mut self, key: &OsStr, val: &OsStr) { - self.init_env_map(); - self.env.as_mut().unwrap().insert(key.to_os_string(), val.to_os_string()); - } - pub fn env_remove(&mut self, key: &OsStr) { - self.init_env_map(); - self.env.as_mut().unwrap().remove(&key.to_os_string()); - } - pub fn env_clear(&mut self) { - self.env = Some(HashMap::new()) - } - pub fn cwd(&mut self, dir: &OsStr) { - self.cwd = Some(dir.to_cstring().unwrap()) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Processes -//////////////////////////////////////////////////////////////////////////////// - -/// Unix exit statuses -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub enum ExitStatus { - /// Normal termination with an exit code. - Code(i32), - - /// Termination by signal, with the signal number. - /// - /// Never generated on Windows. - Signal(i32), -} - -impl ExitStatus { - pub fn success(&self) -> bool { - *self == ExitStatus::Code(0) - } - pub fn code(&self) -> Option { - match *self { - ExitStatus::Code(c) => Some(c), - _ => None - } - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ExitStatus::Code(code) => write!(f, "exit code: {}", code), - ExitStatus::Signal(code) => write!(f, "signal: {}", code), - } - } -} - -/// The unique id of the process (this should never be negative). -pub struct Process { - pid: pid_t -} - -pub enum Stdio { - Inherit, - Piped(AnonPipe), - None, -} - -const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; - -impl Process { - pub unsafe fn kill(&self) -> io::Result<()> { - try!(cvt(libc::funcs::posix88::signal::kill(self.pid, libc::SIGKILL))); - Ok(()) - } - - pub fn spawn(cfg: &Command, - in_fd: Stdio, - out_fd: Stdio, - err_fd: Stdio) -> io::Result { - let dirp = cfg.cwd.as_ref().map(|c| c.as_ptr()).unwrap_or(ptr::null()); - - let (envp, _a, _b) = make_envp(cfg.env.as_ref()); - let (argv, _a) = make_argv(&cfg.program, &cfg.args); - let (input, output) = try!(sys::pipe2::anon_pipe()); - - let pid = unsafe { - match libc::fork() { - 0 => { - drop(input); - Process::child_after_fork(cfg, output, argv, envp, dirp, - in_fd, out_fd, err_fd) - } - n if n < 0 => return Err(Error::last_os_error()), - n => n, - } - }; - - let p = Process{ pid: pid }; - drop(output); - let mut bytes = [0; 8]; - - // loop to handle EINTR - loop { - match input.read(&mut bytes) { - Ok(0) => return Ok(p), - Ok(8) => { - assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]), - "Validation on the CLOEXEC pipe failed: {:?}", bytes); - let errno = combine(&bytes[0.. 4]); - assert!(p.wait().is_ok(), - "wait() should either return Ok or panic"); - return Err(Error::from_raw_os_error(errno)) - } - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} - Err(e) => { - assert!(p.wait().is_ok(), - "wait() should either return Ok or panic"); - panic!("the CLOEXEC pipe failed: {:?}", e) - }, - Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic - assert!(p.wait().is_ok(), - "wait() should either return Ok or panic"); - panic!("short read on the CLOEXEC pipe") - } - } - } - - fn combine(arr: &[u8]) -> i32 { - let a = arr[0] as u32; - let b = arr[1] as u32; - let c = arr[2] as u32; - let d = arr[3] as u32; - - ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 - } - } - - // And at this point we've reached a special time in the life of the - // child. The child must now be considered hamstrung and unable to - // do anything other than syscalls really. Consider the following - // scenario: - // - // 1. Thread A of process 1 grabs the malloc() mutex - // 2. Thread B of process 1 forks(), creating thread C - // 3. Thread C of process 2 then attempts to malloc() - // 4. The memory of process 2 is the same as the memory of - // process 1, so the mutex is locked. - // - // This situation looks a lot like deadlock, right? It turns out - // that this is what pthread_atfork() takes care of, which is - // presumably implemented across platforms. The first thing that - // threads to *before* forking is to do things like grab the malloc - // mutex, and then after the fork they unlock it. - // - // Despite this information, libnative's spawn has been witnessed to - // deadlock on both OSX and FreeBSD. I'm not entirely sure why, but - // all collected backtraces point at malloc/free traffic in the - // child spawned process. - // - // For this reason, the block of code below should contain 0 - // invocations of either malloc of free (or their related friends). - // - // As an example of not having malloc/free traffic, we don't close - // this file descriptor by dropping the FileDesc (which contains an - // allocation). Instead we just close it manually. This will never - // have the drop glue anyway because this code never returns (the - // child will either exec() or invoke libc::exit) - unsafe fn child_after_fork(cfg: &Command, - mut output: AnonPipe, - argv: *const *const libc::c_char, - envp: *const libc::c_void, - dirp: *const libc::c_char, - in_fd: Stdio, - out_fd: Stdio, - err_fd: Stdio) -> ! { - fn fail(output: &mut AnonPipe) -> ! { - let errno = sys::os::errno() as u32; - let bytes = [ - (errno >> 24) as u8, - (errno >> 16) as u8, - (errno >> 8) as u8, - (errno >> 0) as u8, - CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], - CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] - ]; - // pipe I/O up to PIPE_BUF bytes should be atomic, and then we want - // to be sure we *don't* run at_exit destructors as we're being torn - // down regardless - assert!(output.write(&bytes).is_ok()); - unsafe { libc::_exit(1) } - } - - let setup = |src: Stdio, dst: c_int| { - let fd = match src { - Stdio::Inherit => return true, - Stdio::Piped(pipe) => pipe.into_fd(), - - // If a stdio file descriptor is set to be ignored, we open up - // /dev/null into that file descriptor. Otherwise, the first - // file descriptor opened up in the child would be numbered as - // one of the stdio file descriptors, which is likely to wreak - // havoc. - Stdio::None => { - let mut opts = OpenOptions::new(); - opts.read(dst == libc::STDIN_FILENO); - opts.write(dst != libc::STDIN_FILENO); - let devnull = CStr::from_ptr(b"/dev/null\0".as_ptr() - as *const _); - if let Ok(f) = File::open_c(devnull, &opts) { - f.into_fd() - } else { - return false - } - } - }; - cvt_r(|| libc::dup2(fd.raw(), dst)).is_ok() - }; - - if !setup(in_fd, libc::STDIN_FILENO) { fail(&mut output) } - if !setup(out_fd, libc::STDOUT_FILENO) { fail(&mut output) } - if !setup(err_fd, libc::STDERR_FILENO) { fail(&mut output) } - - if let Some(u) = cfg.gid { - if libc::setgid(u as libc::gid_t) != 0 { - fail(&mut output); - } - } - if let Some(u) = cfg.uid { - // When dropping privileges from root, the `setgroups` call - // will remove any extraneous groups. If we don't call this, - // then even though our uid has dropped, we may still have - // groups that enable us to do super-user things. This will - // fail if we aren't root, so don't bother checking the - // return value, this is just done as an optimistic - // privilege dropping function. - let _ = c::setgroups(0, ptr::null()); - - if libc::setuid(u as libc::uid_t) != 0 { - fail(&mut output); - } - } - if cfg.detach { - // Don't check the error of setsid because it fails if we're the - // process leader already. We just forked so it shouldn't return - // error, but ignore it anyway. - let _ = libc::setsid(); - } - if !dirp.is_null() && libc::chdir(dirp) == -1 { - fail(&mut output); - } - if !envp.is_null() { - *sys::os::environ() = envp as *const _; - } - let _ = libc::execvp(*argv, argv as *mut _); - fail(&mut output) - } - - pub fn wait(&self) -> io::Result { - let mut status = 0 as c_int; - try!(cvt_r(|| unsafe { c::waitpid(self.pid, &mut status, 0) })); - Ok(translate_status(status)) - } - - pub fn try_wait(&self) -> Option { - let mut status = 0 as c_int; - match cvt_r(|| unsafe { - c::waitpid(self.pid, &mut status, c::WNOHANG) - }) { - Ok(0) => None, - Ok(n) if n == self.pid => Some(translate_status(status)), - Ok(n) => panic!("unknown pid: {}", n), - Err(e) => panic!("unknown waitpid error: {}", e), - } - } -} - -fn make_argv(prog: &CString, args: &[CString]) - -> (*const *const libc::c_char, Vec<*const libc::c_char>) -{ - let mut ptrs: Vec<*const libc::c_char> = Vec::with_capacity(args.len()+1); - - // Convert the CStrings into an array of pointers. Note: the - // lifetime of the various CStrings involved is guaranteed to be - // larger than the lifetime of our invocation of cb, but this is - // technically unsafe as the callback could leak these pointers - // out of our scope. - ptrs.push(prog.as_ptr()); - ptrs.extend(args.iter().map(|tmp| tmp.as_ptr())); - - // Add a terminating null pointer (required by libc). - ptrs.push(ptr::null()); - - (ptrs.as_ptr(), ptrs) -} - -fn make_envp(env: Option<&HashMap>) - -> (*const c_void, Vec>, Vec<*const libc::c_char>) -{ - // On posixy systems we can pass a char** for envp, which is a - // null-terminated array of "k=v\0" strings. Since we must create - // these strings locally, yet expose a raw pointer to them, we - // create a temporary vector to own the CStrings that outlives the - // call to cb. - if let Some(env) = env { - let mut tmps = Vec::with_capacity(env.len()); - - for pair in env { - let mut kv = Vec::new(); - kv.push_all(pair.0.as_bytes()); - kv.push('=' as u8); - kv.push_all(pair.1.as_bytes()); - kv.push(0); // terminating null - tmps.push(kv); - } - - let mut ptrs: Vec<*const libc::c_char> = - tmps.iter() - .map(|tmp| tmp.as_ptr() as *const libc::c_char) - .collect(); - ptrs.push(ptr::null()); - - (ptrs.as_ptr() as *const _, tmps, ptrs) - } else { - (0 as *const _, Vec::new(), Vec::new()) - } -} - -fn translate_status(status: c_int) -> ExitStatus { - #![allow(non_snake_case)] - #[cfg(any(target_os = "linux", target_os = "android"))] - mod imp { - pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 } - pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff } - pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f } - } - - #[cfg(any(target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "bitrig", - target_os = "openbsd"))] - mod imp { - pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 } - pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 } - pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 } - } - - if imp::WIFEXITED(status) { - ExitStatus::Code(imp::WEXITSTATUS(status)) - } else { - ExitStatus::Signal(imp::WTERMSIG(status)) - } -} diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs index 23c1fcf4b3c..822e1b370c2 100644 --- a/src/libstd/sys/windows/ext/fs.rs +++ b/src/libstd/sys/windows/ext/fs.rs @@ -125,7 +125,7 @@ impl MetadataExt for Metadata { #[stable(feature = "rust1", since = "1.0.0")] pub fn symlink_file, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { - sys::fs2::symlink_inner(src.as_ref(), dst.as_ref(), false) + sys::fs::symlink_inner(src.as_ref(), dst.as_ref(), false) } /// Creates a new directory symlink on the filesystem. @@ -146,5 +146,5 @@ pub fn symlink_file, Q: AsRef>(src: P, dst: Q) #[stable(feature = "rust1", since = "1.0.0")] pub fn symlink_dir, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { - sys::fs2::symlink_inner(src.as_ref(), dst.as_ref(), true) + sys::fs::symlink_inner(src.as_ref(), dst.as_ref(), true) } diff --git a/src/libstd/sys/windows/ext/io.rs b/src/libstd/sys/windows/ext/io.rs index eb48022f055..f4717eb2425 100644 --- a/src/libstd/sys/windows/ext/io.rs +++ b/src/libstd/sys/windows/ext/io.rs @@ -13,7 +13,7 @@ use fs; use os::windows::raw; use net; -use sys_common::{net2, AsInner, FromInner}; +use sys_common::{self, AsInner, FromInner}; use sys; /// Raw HANDLEs. @@ -61,7 +61,7 @@ impl AsRawHandle for fs::File { impl FromRawHandle for fs::File { unsafe fn from_raw_handle(handle: RawHandle) -> fs::File { let handle = handle as ::libc::HANDLE; - fs::File::from_inner(sys::fs2::File::from_inner(handle)) + fs::File::from_inner(sys::fs::File::from_inner(handle)) } } @@ -113,20 +113,20 @@ impl AsRawSocket for net::UdpSocket { impl FromRawSocket for net::TcpStream { unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream { let sock = sys::net::Socket::from_inner(sock); - net::TcpStream::from_inner(net2::TcpStream::from_inner(sock)) + net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock)) } } #[stable(feature = "from_raw_os", since = "1.1.0")] impl FromRawSocket for net::TcpListener { unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener { let sock = sys::net::Socket::from_inner(sock); - net::TcpListener::from_inner(net2::TcpListener::from_inner(sock)) + net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock)) } } #[stable(feature = "from_raw_os", since = "1.1.0")] impl FromRawSocket for net::UdpSocket { unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket { let sock = sys::net::Socket::from_inner(sock); - net::UdpSocket::from_inner(net2::UdpSocket::from_inner(sock)) + net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock)) } } diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs new file mode 100644 index 00000000000..03a56e2958a --- /dev/null +++ b/src/libstd/sys/windows/fs.rs @@ -0,0 +1,579 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::prelude::*; +use io::prelude::*; +use os::windows::prelude::*; + +use ffi::OsString; +use fmt; +use io::{self, Error, SeekFrom}; +use libc::{self, HANDLE}; +use mem; +use path::{Path, PathBuf}; +use ptr; +use slice; +use sync::Arc; +use sys::handle::Handle; +use sys::{c, cvt}; +use sys_common::FromInner; +use vec::Vec; + +pub struct File { handle: Handle } + +pub struct FileAttr { + data: c::WIN32_FILE_ATTRIBUTE_DATA, + is_symlink: bool, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum FileType { + Dir, File, Symlink, ReparsePoint +} + +pub struct ReadDir { + handle: FindNextFileHandle, + root: Arc, + first: Option, +} + +struct FindNextFileHandle(libc::HANDLE); + +unsafe impl Send for FindNextFileHandle {} +unsafe impl Sync for FindNextFileHandle {} + +pub struct DirEntry { + root: Arc, + data: libc::WIN32_FIND_DATAW, +} + +#[derive(Clone, Default)] +pub struct OpenOptions { + create: bool, + append: bool, + read: bool, + write: bool, + truncate: bool, + desired_access: Option, + share_mode: Option, + creation_disposition: Option, + flags_and_attributes: Option, + security_attributes: usize, // *mut T doesn't have a Default impl +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { attrs: libc::DWORD } + +pub struct DirBuilder; + +impl Iterator for ReadDir { + type Item = io::Result; + fn next(&mut self) -> Option> { + if let Some(first) = self.first.take() { + if let Some(e) = DirEntry::new(&self.root, &first) { + return Some(Ok(e)); + } + } + unsafe { + let mut wfd = mem::zeroed(); + loop { + if libc::FindNextFileW(self.handle.0, &mut wfd) == 0 { + if libc::GetLastError() == + c::ERROR_NO_MORE_FILES as libc::DWORD { + return None + } else { + return Some(Err(Error::last_os_error())) + } + } + if let Some(e) = DirEntry::new(&self.root, &wfd) { + return Some(Ok(e)) + } + } + } + } +} + +impl Drop for FindNextFileHandle { + fn drop(&mut self) { + let r = unsafe { libc::FindClose(self.0) }; + debug_assert!(r != 0); + } +} + +impl DirEntry { + fn new(root: &Arc, wfd: &libc::WIN32_FIND_DATAW) -> Option { + match &wfd.cFileName[0..3] { + // check for '.' and '..' + [46, 0, ..] | + [46, 46, 0, ..] => return None, + _ => {} + } + + Some(DirEntry { + root: root.clone(), + data: *wfd, + }) + } + + pub fn path(&self) -> PathBuf { + self.root.join(&self.file_name()) + } + + pub fn file_name(&self) -> OsString { + let filename = super::truncate_utf16_at_nul(&self.data.cFileName); + OsString::from_wide(filename) + } + + pub fn file_type(&self) -> io::Result { + Ok(FileType::new(self.data.dwFileAttributes, + self.data.dwReserved0 == c::IO_REPARSE_TAG_SYMLINK)) + } + + pub fn metadata(&self) -> io::Result { + Ok(FileAttr { + data: c::WIN32_FILE_ATTRIBUTE_DATA { + dwFileAttributes: self.data.dwFileAttributes, + ftCreationTime: self.data.ftCreationTime, + ftLastAccessTime: self.data.ftLastAccessTime, + ftLastWriteTime: self.data.ftLastWriteTime, + nFileSizeHigh: self.data.nFileSizeHigh, + nFileSizeLow: self.data.nFileSizeLow, + }, + is_symlink: self.data.dwReserved0 == c::IO_REPARSE_TAG_SYMLINK, + }) + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { Default::default() } + pub fn read(&mut self, read: bool) { self.read = read; } + pub fn write(&mut self, write: bool) { self.write = write; } + pub fn append(&mut self, append: bool) { self.append = append; } + pub fn create(&mut self, create: bool) { self.create = create; } + pub fn truncate(&mut self, truncate: bool) { self.truncate = truncate; } + pub fn creation_disposition(&mut self, val: i32) { + self.creation_disposition = Some(val as libc::DWORD); + } + pub fn flags_and_attributes(&mut self, val: i32) { + self.flags_and_attributes = Some(val as libc::DWORD); + } + pub fn desired_access(&mut self, val: i32) { + self.desired_access = Some(val as libc::DWORD); + } + pub fn share_mode(&mut self, val: i32) { + self.share_mode = Some(val as libc::DWORD); + } + pub fn security_attributes(&mut self, attrs: libc::LPSECURITY_ATTRIBUTES) { + self.security_attributes = attrs as usize; + } + + fn get_desired_access(&self) -> libc::DWORD { + self.desired_access.unwrap_or({ + let mut base = if self.read {libc::FILE_GENERIC_READ} else {0} | + if self.write {libc::FILE_GENERIC_WRITE} else {0}; + if self.append { + base &= !libc::FILE_WRITE_DATA; + base |= libc::FILE_APPEND_DATA; + } + base + }) + } + + fn get_share_mode(&self) -> libc::DWORD { + // libuv has a good comment about this, but the basic idea is that + // we try to emulate unix semantics by enabling all sharing by + // allowing things such as deleting a file while it's still open. + self.share_mode.unwrap_or(libc::FILE_SHARE_READ | + libc::FILE_SHARE_WRITE | + libc::FILE_SHARE_DELETE) + } + + fn get_creation_disposition(&self) -> libc::DWORD { + self.creation_disposition.unwrap_or({ + match (self.create, self.truncate) { + (true, true) => libc::CREATE_ALWAYS, + (true, false) => libc::OPEN_ALWAYS, + (false, false) => libc::OPEN_EXISTING, + (false, true) => { + if self.write && !self.append { + libc::CREATE_ALWAYS + } else { + libc::TRUNCATE_EXISTING + } + } + } + }) + } + + fn get_flags_and_attributes(&self) -> libc::DWORD { + self.flags_and_attributes.unwrap_or(libc::FILE_ATTRIBUTE_NORMAL) + } +} + +impl File { + fn open_reparse_point(path: &Path) -> io::Result { + let mut opts = OpenOptions::new(); + opts.read(true); + opts.flags_and_attributes(c::FILE_FLAG_OPEN_REPARSE_POINT as i32); + File::open(path, &opts) + } + + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + let path = to_utf16(path); + let handle = unsafe { + libc::CreateFileW(path.as_ptr(), + opts.get_desired_access(), + opts.get_share_mode(), + opts.security_attributes as *mut _, + opts.get_creation_disposition(), + opts.get_flags_and_attributes(), + ptr::null_mut()) + }; + if handle == libc::INVALID_HANDLE_VALUE { + Err(Error::last_os_error()) + } else { + Ok(File { handle: Handle::new(handle) }) + } + } + + pub fn fsync(&self) -> io::Result<()> { + try!(cvt(unsafe { libc::FlushFileBuffers(self.handle.raw()) })); + Ok(()) + } + + pub fn datasync(&self) -> io::Result<()> { self.fsync() } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + let mut info = c::FILE_END_OF_FILE_INFO { + EndOfFile: size as libc::LARGE_INTEGER, + }; + let size = mem::size_of_val(&info); + try!(cvt(unsafe { + c::SetFileInformationByHandle(self.handle.raw(), + c::FileEndOfFileInfo, + &mut info as *mut _ as *mut _, + size as libc::DWORD) + })); + Ok(()) + } + + pub fn file_attr(&self) -> io::Result { + unsafe { + let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed(); + try!(cvt(c::GetFileInformationByHandle(self.handle.raw(), + &mut info))); + let mut attr = FileAttr { + data: c::WIN32_FILE_ATTRIBUTE_DATA { + dwFileAttributes: info.dwFileAttributes, + ftCreationTime: info.ftCreationTime, + ftLastAccessTime: info.ftLastAccessTime, + ftLastWriteTime: info.ftLastWriteTime, + nFileSizeHigh: info.nFileSizeHigh, + nFileSizeLow: info.nFileSizeLow, + }, + is_symlink: false, + }; + if attr.is_reparse_point() { + attr.is_symlink = self.is_symlink(); + } + Ok(attr) + } + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.handle.read(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.handle.write(buf) + } + + pub fn flush(&self) -> io::Result<()> { Ok(()) } + + pub fn seek(&self, pos: SeekFrom) -> io::Result { + let (whence, pos) = match pos { + SeekFrom::Start(n) => (libc::FILE_BEGIN, n as i64), + SeekFrom::End(n) => (libc::FILE_END, n), + SeekFrom::Current(n) => (libc::FILE_CURRENT, n), + }; + let pos = pos as libc::LARGE_INTEGER; + let mut newpos = 0; + try!(cvt(unsafe { + libc::SetFilePointerEx(self.handle.raw(), pos, + &mut newpos, whence) + })); + Ok(newpos as u64) + } + + pub fn handle(&self) -> &Handle { &self.handle } + + fn is_symlink(&self) -> bool { + self.readlink().is_ok() + } + + fn readlink(&self) -> io::Result { + let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + let mut bytes = 0; + + unsafe { + try!(cvt({ + c::DeviceIoControl(self.handle.raw(), + c::FSCTL_GET_REPARSE_POINT, + 0 as *mut _, + 0, + space.as_mut_ptr() as *mut _, + space.len() as libc::DWORD, + &mut bytes, + 0 as *mut _) + })); + let buf: *const c::REPARSE_DATA_BUFFER = space.as_ptr() as *const _; + if (*buf).ReparseTag != c::IO_REPARSE_TAG_SYMLINK { + return Err(io::Error::new(io::ErrorKind::Other, "not a symlink")) + } + let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER = + &(*buf).rest as *const _ as *const _; + let path_buffer = &(*info).PathBuffer as *const _ as *const u16; + let subst_off = (*info).SubstituteNameOffset / 2; + let subst_ptr = path_buffer.offset(subst_off as isize); + let subst_len = (*info).SubstituteNameLength / 2; + let subst = slice::from_raw_parts(subst_ptr, subst_len as usize); + + Ok(PathBuf::from(OsString::from_wide(subst))) + } + } + + pub fn into_handle(self) -> Handle { self.handle } +} + +impl FromInner for File { + fn from_inner(handle: libc::HANDLE) -> File { + File { handle: Handle::new(handle) } + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // FIXME(#24570): add more info here (e.g. path, mode) + f.debug_struct("File") + .field("handle", &self.handle.raw()) + .finish() + } +} + +pub fn to_utf16(s: &Path) -> Vec { + s.as_os_str().encode_wide().chain(Some(0).into_iter()).collect() +} + +impl FileAttr { + pub fn size(&self) -> u64 { + ((self.data.nFileSizeHigh as u64) << 32) | (self.data.nFileSizeLow as u64) + } + + pub fn perm(&self) -> FilePermissions { + FilePermissions { attrs: self.data.dwFileAttributes } + } + + pub fn attrs(&self) -> u32 { self.data.dwFileAttributes as u32 } + + pub fn file_type(&self) -> FileType { + FileType::new(self.data.dwFileAttributes, self.is_symlink) + } + + pub fn created(&self) -> u64 { self.to_u64(&self.data.ftCreationTime) } + pub fn accessed(&self) -> u64 { self.to_u64(&self.data.ftLastAccessTime) } + pub fn modified(&self) -> u64 { self.to_u64(&self.data.ftLastWriteTime) } + + fn to_u64(&self, ft: &libc::FILETIME) -> u64 { + (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32) + } + + fn is_reparse_point(&self) -> bool { + self.data.dwFileAttributes & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + self.attrs & c::FILE_ATTRIBUTE_READONLY != 0 + } + + pub fn set_readonly(&mut self, readonly: bool) { + if readonly { + self.attrs |= c::FILE_ATTRIBUTE_READONLY; + } else { + self.attrs &= !c::FILE_ATTRIBUTE_READONLY; + } + } +} + +impl FileType { + fn new(attrs: libc::DWORD, is_symlink: bool) -> FileType { + if attrs & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + if is_symlink { + FileType::Symlink + } else { + FileType::ReparsePoint + } + } else if attrs & c::FILE_ATTRIBUTE_DIRECTORY != 0 { + FileType::Dir + } else { + FileType::File + } + } + + pub fn is_dir(&self) -> bool { *self == FileType::Dir } + pub fn is_file(&self) -> bool { *self == FileType::File } + pub fn is_symlink(&self) -> bool { *self == FileType::Symlink } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { DirBuilder } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + let p = to_utf16(p); + try!(cvt(unsafe { + libc::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) + })); + Ok(()) + } +} + +pub fn readdir(p: &Path) -> io::Result { + let root = p.to_path_buf(); + let star = p.join("*"); + let path = to_utf16(&star); + + unsafe { + let mut wfd = mem::zeroed(); + let find_handle = libc::FindFirstFileW(path.as_ptr(), &mut wfd); + if find_handle != libc::INVALID_HANDLE_VALUE { + Ok(ReadDir { + handle: FindNextFileHandle(find_handle), + root: Arc::new(root), + first: Some(wfd), + }) + } else { + Err(Error::last_os_error()) + } + } +} + +pub fn unlink(p: &Path) -> io::Result<()> { + let p_utf16 = to_utf16(p); + try!(cvt(unsafe { libc::DeleteFileW(p_utf16.as_ptr()) })); + Ok(()) +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + let old = to_utf16(old); + let new = to_utf16(new); + try!(cvt(unsafe { + libc::MoveFileExW(old.as_ptr(), new.as_ptr(), + libc::MOVEFILE_REPLACE_EXISTING) + })); + Ok(()) +} + +pub fn rmdir(p: &Path) -> io::Result<()> { + let p = to_utf16(p); + try!(cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })); + Ok(()) +} + +pub fn readlink(p: &Path) -> io::Result { + let file = try!(File::open_reparse_point(p)); + file.readlink() +} + +pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { + symlink_inner(src, dst, false) +} + +pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> { + use sys::c::compat::kernel32::CreateSymbolicLinkW; + let src = to_utf16(src); + let dst = to_utf16(dst); + let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 }; + try!(cvt(unsafe { + CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as libc::BOOL + })); + Ok(()) +} + +pub fn link(src: &Path, dst: &Path) -> io::Result<()> { + let src = to_utf16(src); + let dst = to_utf16(dst); + try!(cvt(unsafe { + libc::CreateHardLinkW(dst.as_ptr(), src.as_ptr(), ptr::null_mut()) + })); + Ok(()) +} + +pub fn stat(p: &Path) -> io::Result { + let attr = try!(lstat(p)); + if attr.data.dwFileAttributes & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + let opts = OpenOptions::new(); + let file = try!(File::open(p, &opts)); + file.file_attr() + } else { + Ok(attr) + } +} + +pub fn lstat(p: &Path) -> io::Result { + let utf16 = to_utf16(p); + unsafe { + let mut attr: FileAttr = mem::zeroed(); + try!(cvt(c::GetFileAttributesExW(utf16.as_ptr(), + c::GetFileExInfoStandard, + &mut attr.data as *mut _ as *mut _))); + if attr.is_reparse_point() { + attr.is_symlink = File::open_reparse_point(p).map(|f| { + f.is_symlink() + }).unwrap_or(false); + } + Ok(attr) + } +} + +pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + let p = to_utf16(p); + unsafe { + try!(cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))); + Ok(()) + } +} + +pub fn utimes(p: &Path, atime: u64, mtime: u64) -> io::Result<()> { + let atime = super::ms_to_filetime(atime); + let mtime = super::ms_to_filetime(mtime); + + let mut o = OpenOptions::new(); + o.write(true); + let f = try!(File::open(p, &o)); + try!(cvt(unsafe { + c::SetFileTime(f.handle.raw(), 0 as *const _, &atime, &mtime) + })); + Ok(()) +} + +pub fn canonicalize(p: &Path) -> io::Result { + use sys::c::compat::kernel32::GetFinalPathNameByHandleW; + + let mut opts = OpenOptions::new(); + opts.read(true); + let f = try!(File::open(p, &opts)); + super::fill_utf16_buf(|buf, sz| unsafe { + GetFinalPathNameByHandleW(f.handle.raw(), buf, sz, + libc::VOLUME_NAME_DOS) + }, |buf| { + PathBuf::from(OsString::from_wide(buf)) + }) +} diff --git a/src/libstd/sys/windows/fs2.rs b/src/libstd/sys/windows/fs2.rs deleted file mode 100644 index 03a56e2958a..00000000000 --- a/src/libstd/sys/windows/fs2.rs +++ /dev/null @@ -1,579 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use core::prelude::*; -use io::prelude::*; -use os::windows::prelude::*; - -use ffi::OsString; -use fmt; -use io::{self, Error, SeekFrom}; -use libc::{self, HANDLE}; -use mem; -use path::{Path, PathBuf}; -use ptr; -use slice; -use sync::Arc; -use sys::handle::Handle; -use sys::{c, cvt}; -use sys_common::FromInner; -use vec::Vec; - -pub struct File { handle: Handle } - -pub struct FileAttr { - data: c::WIN32_FILE_ATTRIBUTE_DATA, - is_symlink: bool, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub enum FileType { - Dir, File, Symlink, ReparsePoint -} - -pub struct ReadDir { - handle: FindNextFileHandle, - root: Arc, - first: Option, -} - -struct FindNextFileHandle(libc::HANDLE); - -unsafe impl Send for FindNextFileHandle {} -unsafe impl Sync for FindNextFileHandle {} - -pub struct DirEntry { - root: Arc, - data: libc::WIN32_FIND_DATAW, -} - -#[derive(Clone, Default)] -pub struct OpenOptions { - create: bool, - append: bool, - read: bool, - write: bool, - truncate: bool, - desired_access: Option, - share_mode: Option, - creation_disposition: Option, - flags_and_attributes: Option, - security_attributes: usize, // *mut T doesn't have a Default impl -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct FilePermissions { attrs: libc::DWORD } - -pub struct DirBuilder; - -impl Iterator for ReadDir { - type Item = io::Result; - fn next(&mut self) -> Option> { - if let Some(first) = self.first.take() { - if let Some(e) = DirEntry::new(&self.root, &first) { - return Some(Ok(e)); - } - } - unsafe { - let mut wfd = mem::zeroed(); - loop { - if libc::FindNextFileW(self.handle.0, &mut wfd) == 0 { - if libc::GetLastError() == - c::ERROR_NO_MORE_FILES as libc::DWORD { - return None - } else { - return Some(Err(Error::last_os_error())) - } - } - if let Some(e) = DirEntry::new(&self.root, &wfd) { - return Some(Ok(e)) - } - } - } - } -} - -impl Drop for FindNextFileHandle { - fn drop(&mut self) { - let r = unsafe { libc::FindClose(self.0) }; - debug_assert!(r != 0); - } -} - -impl DirEntry { - fn new(root: &Arc, wfd: &libc::WIN32_FIND_DATAW) -> Option { - match &wfd.cFileName[0..3] { - // check for '.' and '..' - [46, 0, ..] | - [46, 46, 0, ..] => return None, - _ => {} - } - - Some(DirEntry { - root: root.clone(), - data: *wfd, - }) - } - - pub fn path(&self) -> PathBuf { - self.root.join(&self.file_name()) - } - - pub fn file_name(&self) -> OsString { - let filename = super::truncate_utf16_at_nul(&self.data.cFileName); - OsString::from_wide(filename) - } - - pub fn file_type(&self) -> io::Result { - Ok(FileType::new(self.data.dwFileAttributes, - self.data.dwReserved0 == c::IO_REPARSE_TAG_SYMLINK)) - } - - pub fn metadata(&self) -> io::Result { - Ok(FileAttr { - data: c::WIN32_FILE_ATTRIBUTE_DATA { - dwFileAttributes: self.data.dwFileAttributes, - ftCreationTime: self.data.ftCreationTime, - ftLastAccessTime: self.data.ftLastAccessTime, - ftLastWriteTime: self.data.ftLastWriteTime, - nFileSizeHigh: self.data.nFileSizeHigh, - nFileSizeLow: self.data.nFileSizeLow, - }, - is_symlink: self.data.dwReserved0 == c::IO_REPARSE_TAG_SYMLINK, - }) - } -} - -impl OpenOptions { - pub fn new() -> OpenOptions { Default::default() } - pub fn read(&mut self, read: bool) { self.read = read; } - pub fn write(&mut self, write: bool) { self.write = write; } - pub fn append(&mut self, append: bool) { self.append = append; } - pub fn create(&mut self, create: bool) { self.create = create; } - pub fn truncate(&mut self, truncate: bool) { self.truncate = truncate; } - pub fn creation_disposition(&mut self, val: i32) { - self.creation_disposition = Some(val as libc::DWORD); - } - pub fn flags_and_attributes(&mut self, val: i32) { - self.flags_and_attributes = Some(val as libc::DWORD); - } - pub fn desired_access(&mut self, val: i32) { - self.desired_access = Some(val as libc::DWORD); - } - pub fn share_mode(&mut self, val: i32) { - self.share_mode = Some(val as libc::DWORD); - } - pub fn security_attributes(&mut self, attrs: libc::LPSECURITY_ATTRIBUTES) { - self.security_attributes = attrs as usize; - } - - fn get_desired_access(&self) -> libc::DWORD { - self.desired_access.unwrap_or({ - let mut base = if self.read {libc::FILE_GENERIC_READ} else {0} | - if self.write {libc::FILE_GENERIC_WRITE} else {0}; - if self.append { - base &= !libc::FILE_WRITE_DATA; - base |= libc::FILE_APPEND_DATA; - } - base - }) - } - - fn get_share_mode(&self) -> libc::DWORD { - // libuv has a good comment about this, but the basic idea is that - // we try to emulate unix semantics by enabling all sharing by - // allowing things such as deleting a file while it's still open. - self.share_mode.unwrap_or(libc::FILE_SHARE_READ | - libc::FILE_SHARE_WRITE | - libc::FILE_SHARE_DELETE) - } - - fn get_creation_disposition(&self) -> libc::DWORD { - self.creation_disposition.unwrap_or({ - match (self.create, self.truncate) { - (true, true) => libc::CREATE_ALWAYS, - (true, false) => libc::OPEN_ALWAYS, - (false, false) => libc::OPEN_EXISTING, - (false, true) => { - if self.write && !self.append { - libc::CREATE_ALWAYS - } else { - libc::TRUNCATE_EXISTING - } - } - } - }) - } - - fn get_flags_and_attributes(&self) -> libc::DWORD { - self.flags_and_attributes.unwrap_or(libc::FILE_ATTRIBUTE_NORMAL) - } -} - -impl File { - fn open_reparse_point(path: &Path) -> io::Result { - let mut opts = OpenOptions::new(); - opts.read(true); - opts.flags_and_attributes(c::FILE_FLAG_OPEN_REPARSE_POINT as i32); - File::open(path, &opts) - } - - pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { - let path = to_utf16(path); - let handle = unsafe { - libc::CreateFileW(path.as_ptr(), - opts.get_desired_access(), - opts.get_share_mode(), - opts.security_attributes as *mut _, - opts.get_creation_disposition(), - opts.get_flags_and_attributes(), - ptr::null_mut()) - }; - if handle == libc::INVALID_HANDLE_VALUE { - Err(Error::last_os_error()) - } else { - Ok(File { handle: Handle::new(handle) }) - } - } - - pub fn fsync(&self) -> io::Result<()> { - try!(cvt(unsafe { libc::FlushFileBuffers(self.handle.raw()) })); - Ok(()) - } - - pub fn datasync(&self) -> io::Result<()> { self.fsync() } - - pub fn truncate(&self, size: u64) -> io::Result<()> { - let mut info = c::FILE_END_OF_FILE_INFO { - EndOfFile: size as libc::LARGE_INTEGER, - }; - let size = mem::size_of_val(&info); - try!(cvt(unsafe { - c::SetFileInformationByHandle(self.handle.raw(), - c::FileEndOfFileInfo, - &mut info as *mut _ as *mut _, - size as libc::DWORD) - })); - Ok(()) - } - - pub fn file_attr(&self) -> io::Result { - unsafe { - let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed(); - try!(cvt(c::GetFileInformationByHandle(self.handle.raw(), - &mut info))); - let mut attr = FileAttr { - data: c::WIN32_FILE_ATTRIBUTE_DATA { - dwFileAttributes: info.dwFileAttributes, - ftCreationTime: info.ftCreationTime, - ftLastAccessTime: info.ftLastAccessTime, - ftLastWriteTime: info.ftLastWriteTime, - nFileSizeHigh: info.nFileSizeHigh, - nFileSizeLow: info.nFileSizeLow, - }, - is_symlink: false, - }; - if attr.is_reparse_point() { - attr.is_symlink = self.is_symlink(); - } - Ok(attr) - } - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.handle.read(buf) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.handle.write(buf) - } - - pub fn flush(&self) -> io::Result<()> { Ok(()) } - - pub fn seek(&self, pos: SeekFrom) -> io::Result { - let (whence, pos) = match pos { - SeekFrom::Start(n) => (libc::FILE_BEGIN, n as i64), - SeekFrom::End(n) => (libc::FILE_END, n), - SeekFrom::Current(n) => (libc::FILE_CURRENT, n), - }; - let pos = pos as libc::LARGE_INTEGER; - let mut newpos = 0; - try!(cvt(unsafe { - libc::SetFilePointerEx(self.handle.raw(), pos, - &mut newpos, whence) - })); - Ok(newpos as u64) - } - - pub fn handle(&self) -> &Handle { &self.handle } - - fn is_symlink(&self) -> bool { - self.readlink().is_ok() - } - - fn readlink(&self) -> io::Result { - let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; - let mut bytes = 0; - - unsafe { - try!(cvt({ - c::DeviceIoControl(self.handle.raw(), - c::FSCTL_GET_REPARSE_POINT, - 0 as *mut _, - 0, - space.as_mut_ptr() as *mut _, - space.len() as libc::DWORD, - &mut bytes, - 0 as *mut _) - })); - let buf: *const c::REPARSE_DATA_BUFFER = space.as_ptr() as *const _; - if (*buf).ReparseTag != c::IO_REPARSE_TAG_SYMLINK { - return Err(io::Error::new(io::ErrorKind::Other, "not a symlink")) - } - let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER = - &(*buf).rest as *const _ as *const _; - let path_buffer = &(*info).PathBuffer as *const _ as *const u16; - let subst_off = (*info).SubstituteNameOffset / 2; - let subst_ptr = path_buffer.offset(subst_off as isize); - let subst_len = (*info).SubstituteNameLength / 2; - let subst = slice::from_raw_parts(subst_ptr, subst_len as usize); - - Ok(PathBuf::from(OsString::from_wide(subst))) - } - } - - pub fn into_handle(self) -> Handle { self.handle } -} - -impl FromInner for File { - fn from_inner(handle: libc::HANDLE) -> File { - File { handle: Handle::new(handle) } - } -} - -impl fmt::Debug for File { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // FIXME(#24570): add more info here (e.g. path, mode) - f.debug_struct("File") - .field("handle", &self.handle.raw()) - .finish() - } -} - -pub fn to_utf16(s: &Path) -> Vec { - s.as_os_str().encode_wide().chain(Some(0).into_iter()).collect() -} - -impl FileAttr { - pub fn size(&self) -> u64 { - ((self.data.nFileSizeHigh as u64) << 32) | (self.data.nFileSizeLow as u64) - } - - pub fn perm(&self) -> FilePermissions { - FilePermissions { attrs: self.data.dwFileAttributes } - } - - pub fn attrs(&self) -> u32 { self.data.dwFileAttributes as u32 } - - pub fn file_type(&self) -> FileType { - FileType::new(self.data.dwFileAttributes, self.is_symlink) - } - - pub fn created(&self) -> u64 { self.to_u64(&self.data.ftCreationTime) } - pub fn accessed(&self) -> u64 { self.to_u64(&self.data.ftLastAccessTime) } - pub fn modified(&self) -> u64 { self.to_u64(&self.data.ftLastWriteTime) } - - fn to_u64(&self, ft: &libc::FILETIME) -> u64 { - (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32) - } - - fn is_reparse_point(&self) -> bool { - self.data.dwFileAttributes & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 - } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - self.attrs & c::FILE_ATTRIBUTE_READONLY != 0 - } - - pub fn set_readonly(&mut self, readonly: bool) { - if readonly { - self.attrs |= c::FILE_ATTRIBUTE_READONLY; - } else { - self.attrs &= !c::FILE_ATTRIBUTE_READONLY; - } - } -} - -impl FileType { - fn new(attrs: libc::DWORD, is_symlink: bool) -> FileType { - if attrs & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 { - if is_symlink { - FileType::Symlink - } else { - FileType::ReparsePoint - } - } else if attrs & c::FILE_ATTRIBUTE_DIRECTORY != 0 { - FileType::Dir - } else { - FileType::File - } - } - - pub fn is_dir(&self) -> bool { *self == FileType::Dir } - pub fn is_file(&self) -> bool { *self == FileType::File } - pub fn is_symlink(&self) -> bool { *self == FileType::Symlink } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { DirBuilder } - - pub fn mkdir(&self, p: &Path) -> io::Result<()> { - let p = to_utf16(p); - try!(cvt(unsafe { - libc::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) - })); - Ok(()) - } -} - -pub fn readdir(p: &Path) -> io::Result { - let root = p.to_path_buf(); - let star = p.join("*"); - let path = to_utf16(&star); - - unsafe { - let mut wfd = mem::zeroed(); - let find_handle = libc::FindFirstFileW(path.as_ptr(), &mut wfd); - if find_handle != libc::INVALID_HANDLE_VALUE { - Ok(ReadDir { - handle: FindNextFileHandle(find_handle), - root: Arc::new(root), - first: Some(wfd), - }) - } else { - Err(Error::last_os_error()) - } - } -} - -pub fn unlink(p: &Path) -> io::Result<()> { - let p_utf16 = to_utf16(p); - try!(cvt(unsafe { libc::DeleteFileW(p_utf16.as_ptr()) })); - Ok(()) -} - -pub fn rename(old: &Path, new: &Path) -> io::Result<()> { - let old = to_utf16(old); - let new = to_utf16(new); - try!(cvt(unsafe { - libc::MoveFileExW(old.as_ptr(), new.as_ptr(), - libc::MOVEFILE_REPLACE_EXISTING) - })); - Ok(()) -} - -pub fn rmdir(p: &Path) -> io::Result<()> { - let p = to_utf16(p); - try!(cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })); - Ok(()) -} - -pub fn readlink(p: &Path) -> io::Result { - let file = try!(File::open_reparse_point(p)); - file.readlink() -} - -pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { - symlink_inner(src, dst, false) -} - -pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> { - use sys::c::compat::kernel32::CreateSymbolicLinkW; - let src = to_utf16(src); - let dst = to_utf16(dst); - let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 }; - try!(cvt(unsafe { - CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as libc::BOOL - })); - Ok(()) -} - -pub fn link(src: &Path, dst: &Path) -> io::Result<()> { - let src = to_utf16(src); - let dst = to_utf16(dst); - try!(cvt(unsafe { - libc::CreateHardLinkW(dst.as_ptr(), src.as_ptr(), ptr::null_mut()) - })); - Ok(()) -} - -pub fn stat(p: &Path) -> io::Result { - let attr = try!(lstat(p)); - if attr.data.dwFileAttributes & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 { - let opts = OpenOptions::new(); - let file = try!(File::open(p, &opts)); - file.file_attr() - } else { - Ok(attr) - } -} - -pub fn lstat(p: &Path) -> io::Result { - let utf16 = to_utf16(p); - unsafe { - let mut attr: FileAttr = mem::zeroed(); - try!(cvt(c::GetFileAttributesExW(utf16.as_ptr(), - c::GetFileExInfoStandard, - &mut attr.data as *mut _ as *mut _))); - if attr.is_reparse_point() { - attr.is_symlink = File::open_reparse_point(p).map(|f| { - f.is_symlink() - }).unwrap_or(false); - } - Ok(attr) - } -} - -pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { - let p = to_utf16(p); - unsafe { - try!(cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))); - Ok(()) - } -} - -pub fn utimes(p: &Path, atime: u64, mtime: u64) -> io::Result<()> { - let atime = super::ms_to_filetime(atime); - let mtime = super::ms_to_filetime(mtime); - - let mut o = OpenOptions::new(); - o.write(true); - let f = try!(File::open(p, &o)); - try!(cvt(unsafe { - c::SetFileTime(f.handle.raw(), 0 as *const _, &atime, &mtime) - })); - Ok(()) -} - -pub fn canonicalize(p: &Path) -> io::Result { - use sys::c::compat::kernel32::GetFinalPathNameByHandleW; - - let mut opts = OpenOptions::new(); - opts.read(true); - let f = try!(File::open(p, &opts)); - super::fill_utf16_buf(|buf, sz| unsafe { - GetFinalPathNameByHandleW(f.handle.raw(), buf, sz, - libc::VOLUME_NAME_DOS) - }, |buf| { - PathBuf::from(OsString::from_wide(buf)) - }) -} diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 5ae5f6f201b..4c30f0f8660 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -25,14 +25,14 @@ pub mod backtrace; pub mod c; pub mod condvar; pub mod ext; -pub mod fs2; +pub mod fs; pub mod handle; pub mod mutex; pub mod net; pub mod os; pub mod os_str; -pub mod pipe2; -pub mod process2; +pub mod pipe; +pub mod process; pub mod rwlock; pub mod stack_overflow; pub mod sync; diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs new file mode 100644 index 00000000000..b441d8beedb --- /dev/null +++ b/src/libstd/sys/windows/pipe.rs @@ -0,0 +1,48 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use io; +use libc; +use sys::cvt; +use sys::c; +use sys::handle::Handle; + +//////////////////////////////////////////////////////////////////////////////// +// Anonymous pipes +//////////////////////////////////////////////////////////////////////////////// + +pub struct AnonPipe { + inner: Handle, +} + +pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { + let mut reader = libc::INVALID_HANDLE_VALUE; + let mut writer = libc::INVALID_HANDLE_VALUE; + try!(cvt(unsafe { + c::CreatePipe(&mut reader, &mut writer, 0 as *mut _, 0) + })); + let reader = Handle::new(reader); + let writer = Handle::new(writer); + Ok((AnonPipe { inner: reader }, AnonPipe { inner: writer })) +} + +impl AnonPipe { + pub fn handle(&self) -> &Handle { &self.inner } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.inner.write(buf) + } +} diff --git a/src/libstd/sys/windows/pipe2.rs b/src/libstd/sys/windows/pipe2.rs deleted file mode 100644 index b441d8beedb..00000000000 --- a/src/libstd/sys/windows/pipe2.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use prelude::v1::*; - -use io; -use libc; -use sys::cvt; -use sys::c; -use sys::handle::Handle; - -//////////////////////////////////////////////////////////////////////////////// -// Anonymous pipes -//////////////////////////////////////////////////////////////////////////////// - -pub struct AnonPipe { - inner: Handle, -} - -pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { - let mut reader = libc::INVALID_HANDLE_VALUE; - let mut writer = libc::INVALID_HANDLE_VALUE; - try!(cvt(unsafe { - c::CreatePipe(&mut reader, &mut writer, 0 as *mut _, 0) - })); - let reader = Handle::new(reader); - let writer = Handle::new(writer); - Ok((AnonPipe { inner: reader }, AnonPipe { inner: writer })) -} - -impl AnonPipe { - pub fn handle(&self) -> &Handle { &self.inner } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.inner.write(buf) - } -} diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs new file mode 100644 index 00000000000..032a349b00e --- /dev/null +++ b/src/libstd/sys/windows/process.rs @@ -0,0 +1,429 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use ascii::*; +use collections::HashMap; +use collections; +use env::split_paths; +use env; +use ffi::{OsString, OsStr}; +use fmt; +use fs; +use io::{self, Error}; +use libc::{self, c_void}; +use mem; +use os::windows::ffi::OsStrExt; +use path::Path; +use ptr; +use sync::{StaticMutex, MUTEX_INIT}; +use sys::c; +use sys::fs::{OpenOptions, File}; +use sys::handle::Handle; +use sys::pipe::AnonPipe; +use sys::stdio; +use sys::{self, cvt}; +use sys_common::{AsInner, FromInner}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +fn mk_key(s: &OsStr) -> OsString { + FromInner::from_inner(sys::os_str::Buf { + inner: s.as_inner().inner.to_ascii_uppercase() + }) +} + +#[derive(Clone)] +pub struct Command { + pub program: OsString, + pub args: Vec, + pub env: Option>, + pub cwd: Option, + pub detach: bool, // not currently exposed in std::process +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + Command { + program: program.to_os_string(), + args: Vec::new(), + env: None, + cwd: None, + detach: false, + } + } + + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(arg.to_os_string()) + } + pub fn args<'a, I: Iterator>(&mut self, args: I) { + self.args.extend(args.map(OsStr::to_os_string)) + } + fn init_env_map(&mut self){ + if self.env.is_none() { + self.env = Some(env::vars_os().map(|(key, val)| { + (mk_key(&key), val) + }).collect()); + } + } + pub fn env(&mut self, key: &OsStr, val: &OsStr) { + self.init_env_map(); + self.env.as_mut().unwrap().insert(mk_key(key), val.to_os_string()); + } + pub fn env_remove(&mut self, key: &OsStr) { + self.init_env_map(); + self.env.as_mut().unwrap().remove(&mk_key(key)); + } + pub fn env_clear(&mut self) { + self.env = Some(HashMap::new()) + } + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(dir.to_os_string()) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +/// A value representing a child process. +/// +/// The lifetime of this value is linked to the lifetime of the actual +/// process - the Process destructor calls self.finish() which waits +/// for the process to terminate. +pub struct Process { + handle: Handle, +} + +pub enum Stdio { + Inherit, + Piped(AnonPipe), + None, +} + +impl Process { + pub fn spawn(cfg: &Command, + in_handle: Stdio, + out_handle: Stdio, + err_handle: Stdio) -> io::Result + { + use libc::{TRUE, STARTF_USESTDHANDLES}; + use libc::{DWORD, STARTUPINFO, CreateProcessW}; + + // To have the spawning semantics of unix/windows stay the same, we need + // to read the *child's* PATH if one is provided. See #15149 for more + // details. + let program = cfg.env.as_ref().and_then(|env| { + for (key, v) in env { + if OsStr::new("PATH") != &**key { continue } + + // Split the value and test each path to see if the + // program exists. + for path in split_paths(&v) { + let path = path.join(cfg.program.to_str().unwrap()) + .with_extension(env::consts::EXE_EXTENSION); + if fs::metadata(&path).is_ok() { + return Some(path.into_os_string()) + } + } + break + } + None + }); + + let mut si = zeroed_startupinfo(); + si.cb = mem::size_of::() as DWORD; + si.dwFlags = STARTF_USESTDHANDLES; + + let stdin = try!(in_handle.to_handle(c::STD_INPUT_HANDLE)); + let stdout = try!(out_handle.to_handle(c::STD_OUTPUT_HANDLE)); + let stderr = try!(err_handle.to_handle(c::STD_ERROR_HANDLE)); + + si.hStdInput = stdin.raw(); + si.hStdOutput = stdout.raw(); + si.hStdError = stderr.raw(); + + let program = program.as_ref().unwrap_or(&cfg.program); + let mut cmd_str = make_command_line(program, &cfg.args); + cmd_str.push(0); // add null terminator + + // stolen from the libuv code. + let mut flags = libc::CREATE_UNICODE_ENVIRONMENT; + if cfg.detach { + flags |= libc::DETACHED_PROCESS | libc::CREATE_NEW_PROCESS_GROUP; + } + + let (envp, _data) = make_envp(cfg.env.as_ref()); + let (dirp, _data) = make_dirp(cfg.cwd.as_ref()); + let mut pi = zeroed_process_information(); + try!(unsafe { + // `CreateProcess` is racy! + // http://support.microsoft.com/kb/315939 + static CREATE_PROCESS_LOCK: StaticMutex = MUTEX_INIT; + let _lock = CREATE_PROCESS_LOCK.lock(); + + cvt(CreateProcessW(ptr::null(), + cmd_str.as_mut_ptr(), + ptr::null_mut(), + ptr::null_mut(), + TRUE, flags, envp, dirp, + &mut si, &mut pi)) + }); + + // We close the thread handle because we don't care about keeping + // the thread id valid, and we aren't keeping the thread handle + // around to be able to close it later. + drop(Handle::new(pi.hThread)); + + Ok(Process { handle: Handle::new(pi.hProcess) }) + } + + pub unsafe fn kill(&self) -> io::Result<()> { + try!(cvt(libc::TerminateProcess(self.handle.raw(), 1))); + Ok(()) + } + + pub fn wait(&self) -> io::Result { + use libc::{STILL_ACTIVE, INFINITE, WAIT_OBJECT_0}; + use libc::{GetExitCodeProcess, WaitForSingleObject}; + + unsafe { + loop { + let mut status = 0; + try!(cvt(GetExitCodeProcess(self.handle.raw(), &mut status))); + if status != STILL_ACTIVE { + return Ok(ExitStatus(status as i32)); + } + match WaitForSingleObject(self.handle.raw(), INFINITE) { + WAIT_OBJECT_0 => {} + _ => return Err(Error::last_os_error()), + } + } + } + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatus(i32); + +impl ExitStatus { + pub fn success(&self) -> bool { + self.0 == 0 + } + pub fn code(&self) -> Option { + Some(self.0) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "exit code: {}", self.0) + } +} + +fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO { + libc::types::os::arch::extra::STARTUPINFO { + cb: 0, + lpReserved: ptr::null_mut(), + lpDesktop: ptr::null_mut(), + lpTitle: ptr::null_mut(), + dwX: 0, + dwY: 0, + dwXSize: 0, + dwYSize: 0, + dwXCountChars: 0, + dwYCountCharts: 0, + dwFillAttribute: 0, + dwFlags: 0, + wShowWindow: 0, + cbReserved2: 0, + lpReserved2: ptr::null_mut(), + hStdInput: libc::INVALID_HANDLE_VALUE, + hStdOutput: libc::INVALID_HANDLE_VALUE, + hStdError: libc::INVALID_HANDLE_VALUE, + } +} + +fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION { + libc::types::os::arch::extra::PROCESS_INFORMATION { + hProcess: ptr::null_mut(), + hThread: ptr::null_mut(), + dwProcessId: 0, + dwThreadId: 0 + } +} + +// Produces a wide string *without terminating null* +fn make_command_line(prog: &OsStr, args: &[OsString]) -> Vec { + // Encode the command and arguments in a command line string such + // that the spawned process may recover them using CommandLineToArgvW. + let mut cmd: Vec = Vec::new(); + append_arg(&mut cmd, prog); + for arg in args { + cmd.push(' ' as u16); + append_arg(&mut cmd, arg); + } + return cmd; + + fn append_arg(cmd: &mut Vec, arg: &OsStr) { + // If an argument has 0 characters then we need to quote it to ensure + // that it actually gets passed through on the command line or otherwise + // it will be dropped entirely when parsed on the other end. + let arg_bytes = &arg.as_inner().inner.as_inner(); + let quote = arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') + || arg_bytes.is_empty(); + if quote { + cmd.push('"' as u16); + } + + let mut iter = arg.encode_wide(); + let mut backslashes: usize = 0; + while let Some(x) = iter.next() { + if x == '\\' as u16 { + backslashes += 1; + } else { + if x == '"' as u16 { + // Add n+1 backslashes to total 2n+1 before internal '"'. + for _ in 0..(backslashes+1) { + cmd.push('\\' as u16); + } + } + backslashes = 0; + } + cmd.push(x); + } + + if quote { + // Add n backslashes to total 2n before ending '"'. + for _ in 0..backslashes { + cmd.push('\\' as u16); + } + cmd.push('"' as u16); + } + } +} + +fn make_envp(env: Option<&collections::HashMap>) + -> (*mut c_void, Vec) { + // On Windows we pass an "environment block" which is not a char**, but + // rather a concatenation of null-terminated k=v\0 sequences, with a final + // \0 to terminate. + match env { + Some(env) => { + let mut blk = Vec::new(); + + for pair in env { + blk.extend(pair.0.encode_wide()); + blk.push('=' as u16); + blk.extend(pair.1.encode_wide()); + blk.push(0); + } + blk.push(0); + (blk.as_mut_ptr() as *mut c_void, blk) + } + _ => (ptr::null_mut(), Vec::new()) + } +} + +fn make_dirp(d: Option<&OsString>) -> (*const u16, Vec) { + match d { + Some(dir) => { + let mut dir_str: Vec = dir.encode_wide().collect(); + dir_str.push(0); + (dir_str.as_ptr(), dir_str) + }, + None => (ptr::null(), Vec::new()) + } +} + +impl Stdio { + fn to_handle(&self, stdio_id: libc::DWORD) -> io::Result { + use libc::DUPLICATE_SAME_ACCESS; + + match *self { + Stdio::Inherit => { + stdio::get(stdio_id).and_then(|io| { + io.handle().duplicate(0, true, DUPLICATE_SAME_ACCESS) + }) + } + Stdio::Piped(ref pipe) => { + pipe.handle().duplicate(0, true, DUPLICATE_SAME_ACCESS) + } + + // Similarly to unix, we don't actually leave holes for the + // stdio file descriptors, but rather open up /dev/null + // equivalents. These equivalents are drawn from libuv's + // windows process spawning. + Stdio::None => { + let size = mem::size_of::(); + let mut sa = libc::SECURITY_ATTRIBUTES { + nLength: size as libc::DWORD, + lpSecurityDescriptor: ptr::null_mut(), + bInheritHandle: 1, + }; + let mut opts = OpenOptions::new(); + opts.read(stdio_id == c::STD_INPUT_HANDLE); + opts.write(stdio_id != c::STD_INPUT_HANDLE); + opts.security_attributes(&mut sa); + File::open(Path::new("NUL"), &opts).map(|file| { + file.into_handle() + }) + } + } + } +} + +#[cfg(test)] +mod tests { + use prelude::v1::*; + use str; + use ffi::{OsStr, OsString}; + use super::make_command_line; + + #[test] + fn test_make_command_line() { + fn test_wrapper(prog: &str, args: &[&str]) -> String { + String::from_utf16( + &make_command_line(OsStr::new(prog), + &args.iter() + .map(|a| OsString::from(a)) + .collect::>())).unwrap() + } + + assert_eq!( + test_wrapper("prog", &["aaa", "bbb", "ccc"]), + "prog aaa bbb ccc" + ); + + assert_eq!( + test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]), + "\"C:\\Program Files\\blah\\blah.exe\" aaa" + ); + assert_eq!( + test_wrapper("C:\\Program Files\\test", &["aa\"bb"]), + "\"C:\\Program Files\\test\" aa\\\"bb" + ); + assert_eq!( + test_wrapper("echo", &["a b c"]), + "echo \"a b c\"" + ); + assert_eq!( + test_wrapper("echo", &["\" \\\" \\", "\\"]), + "echo \"\\\" \\\\\\\" \\\\\" \\" + ); + assert_eq!( + test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]), + "\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}" + ); + } +} diff --git a/src/libstd/sys/windows/process2.rs b/src/libstd/sys/windows/process2.rs deleted file mode 100644 index 5aad5f668dd..00000000000 --- a/src/libstd/sys/windows/process2.rs +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use prelude::v1::*; - -use ascii::*; -use collections::HashMap; -use collections; -use env::split_paths; -use env; -use ffi::{OsString, OsStr}; -use fmt; -use fs; -use io::{self, Error}; -use libc::{self, c_void}; -use mem; -use os::windows::ffi::OsStrExt; -use path::Path; -use ptr; -use sync::{StaticMutex, MUTEX_INIT}; -use sys::c; -use sys::fs2::{OpenOptions, File}; -use sys::handle::Handle; -use sys::pipe2::AnonPipe; -use sys::stdio; -use sys::{self, cvt}; -use sys_common::{AsInner, FromInner}; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -fn mk_key(s: &OsStr) -> OsString { - FromInner::from_inner(sys::os_str::Buf { - inner: s.as_inner().inner.to_ascii_uppercase() - }) -} - -#[derive(Clone)] -pub struct Command { - pub program: OsString, - pub args: Vec, - pub env: Option>, - pub cwd: Option, - pub detach: bool, // not currently exposed in std::process -} - -impl Command { - pub fn new(program: &OsStr) -> Command { - Command { - program: program.to_os_string(), - args: Vec::new(), - env: None, - cwd: None, - detach: false, - } - } - - pub fn arg(&mut self, arg: &OsStr) { - self.args.push(arg.to_os_string()) - } - pub fn args<'a, I: Iterator>(&mut self, args: I) { - self.args.extend(args.map(OsStr::to_os_string)) - } - fn init_env_map(&mut self){ - if self.env.is_none() { - self.env = Some(env::vars_os().map(|(key, val)| { - (mk_key(&key), val) - }).collect()); - } - } - pub fn env(&mut self, key: &OsStr, val: &OsStr) { - self.init_env_map(); - self.env.as_mut().unwrap().insert(mk_key(key), val.to_os_string()); - } - pub fn env_remove(&mut self, key: &OsStr) { - self.init_env_map(); - self.env.as_mut().unwrap().remove(&mk_key(key)); - } - pub fn env_clear(&mut self) { - self.env = Some(HashMap::new()) - } - pub fn cwd(&mut self, dir: &OsStr) { - self.cwd = Some(dir.to_os_string()) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Processes -//////////////////////////////////////////////////////////////////////////////// - -/// A value representing a child process. -/// -/// The lifetime of this value is linked to the lifetime of the actual -/// process - the Process destructor calls self.finish() which waits -/// for the process to terminate. -pub struct Process { - handle: Handle, -} - -pub enum Stdio { - Inherit, - Piped(AnonPipe), - None, -} - -impl Process { - pub fn spawn(cfg: &Command, - in_handle: Stdio, - out_handle: Stdio, - err_handle: Stdio) -> io::Result - { - use libc::{TRUE, STARTF_USESTDHANDLES}; - use libc::{DWORD, STARTUPINFO, CreateProcessW}; - - // To have the spawning semantics of unix/windows stay the same, we need - // to read the *child's* PATH if one is provided. See #15149 for more - // details. - let program = cfg.env.as_ref().and_then(|env| { - for (key, v) in env { - if OsStr::new("PATH") != &**key { continue } - - // Split the value and test each path to see if the - // program exists. - for path in split_paths(&v) { - let path = path.join(cfg.program.to_str().unwrap()) - .with_extension(env::consts::EXE_EXTENSION); - if fs::metadata(&path).is_ok() { - return Some(path.into_os_string()) - } - } - break - } - None - }); - - let mut si = zeroed_startupinfo(); - si.cb = mem::size_of::() as DWORD; - si.dwFlags = STARTF_USESTDHANDLES; - - let stdin = try!(in_handle.to_handle(c::STD_INPUT_HANDLE)); - let stdout = try!(out_handle.to_handle(c::STD_OUTPUT_HANDLE)); - let stderr = try!(err_handle.to_handle(c::STD_ERROR_HANDLE)); - - si.hStdInput = stdin.raw(); - si.hStdOutput = stdout.raw(); - si.hStdError = stderr.raw(); - - let program = program.as_ref().unwrap_or(&cfg.program); - let mut cmd_str = make_command_line(program, &cfg.args); - cmd_str.push(0); // add null terminator - - // stolen from the libuv code. - let mut flags = libc::CREATE_UNICODE_ENVIRONMENT; - if cfg.detach { - flags |= libc::DETACHED_PROCESS | libc::CREATE_NEW_PROCESS_GROUP; - } - - let (envp, _data) = make_envp(cfg.env.as_ref()); - let (dirp, _data) = make_dirp(cfg.cwd.as_ref()); - let mut pi = zeroed_process_information(); - try!(unsafe { - // `CreateProcess` is racy! - // http://support.microsoft.com/kb/315939 - static CREATE_PROCESS_LOCK: StaticMutex = MUTEX_INIT; - let _lock = CREATE_PROCESS_LOCK.lock(); - - cvt(CreateProcessW(ptr::null(), - cmd_str.as_mut_ptr(), - ptr::null_mut(), - ptr::null_mut(), - TRUE, flags, envp, dirp, - &mut si, &mut pi)) - }); - - // We close the thread handle because we don't care about keeping - // the thread id valid, and we aren't keeping the thread handle - // around to be able to close it later. - drop(Handle::new(pi.hThread)); - - Ok(Process { handle: Handle::new(pi.hProcess) }) - } - - pub unsafe fn kill(&self) -> io::Result<()> { - try!(cvt(libc::TerminateProcess(self.handle.raw(), 1))); - Ok(()) - } - - pub fn wait(&self) -> io::Result { - use libc::{STILL_ACTIVE, INFINITE, WAIT_OBJECT_0}; - use libc::{GetExitCodeProcess, WaitForSingleObject}; - - unsafe { - loop { - let mut status = 0; - try!(cvt(GetExitCodeProcess(self.handle.raw(), &mut status))); - if status != STILL_ACTIVE { - return Ok(ExitStatus(status as i32)); - } - match WaitForSingleObject(self.handle.raw(), INFINITE) { - WAIT_OBJECT_0 => {} - _ => return Err(Error::last_os_error()), - } - } - } - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitStatus(i32); - -impl ExitStatus { - pub fn success(&self) -> bool { - self.0 == 0 - } - pub fn code(&self) -> Option { - Some(self.0) - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "exit code: {}", self.0) - } -} - -fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO { - libc::types::os::arch::extra::STARTUPINFO { - cb: 0, - lpReserved: ptr::null_mut(), - lpDesktop: ptr::null_mut(), - lpTitle: ptr::null_mut(), - dwX: 0, - dwY: 0, - dwXSize: 0, - dwYSize: 0, - dwXCountChars: 0, - dwYCountCharts: 0, - dwFillAttribute: 0, - dwFlags: 0, - wShowWindow: 0, - cbReserved2: 0, - lpReserved2: ptr::null_mut(), - hStdInput: libc::INVALID_HANDLE_VALUE, - hStdOutput: libc::INVALID_HANDLE_VALUE, - hStdError: libc::INVALID_HANDLE_VALUE, - } -} - -fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION { - libc::types::os::arch::extra::PROCESS_INFORMATION { - hProcess: ptr::null_mut(), - hThread: ptr::null_mut(), - dwProcessId: 0, - dwThreadId: 0 - } -} - -// Produces a wide string *without terminating null* -fn make_command_line(prog: &OsStr, args: &[OsString]) -> Vec { - // Encode the command and arguments in a command line string such - // that the spawned process may recover them using CommandLineToArgvW. - let mut cmd: Vec = Vec::new(); - append_arg(&mut cmd, prog); - for arg in args { - cmd.push(' ' as u16); - append_arg(&mut cmd, arg); - } - return cmd; - - fn append_arg(cmd: &mut Vec, arg: &OsStr) { - // If an argument has 0 characters then we need to quote it to ensure - // that it actually gets passed through on the command line or otherwise - // it will be dropped entirely when parsed on the other end. - let arg_bytes = &arg.as_inner().inner.as_inner(); - let quote = arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') - || arg_bytes.is_empty(); - if quote { - cmd.push('"' as u16); - } - - let mut iter = arg.encode_wide(); - let mut backslashes: usize = 0; - while let Some(x) = iter.next() { - if x == '\\' as u16 { - backslashes += 1; - } else { - if x == '"' as u16 { - // Add n+1 backslashes to total 2n+1 before internal '"'. - for _ in 0..(backslashes+1) { - cmd.push('\\' as u16); - } - } - backslashes = 0; - } - cmd.push(x); - } - - if quote { - // Add n backslashes to total 2n before ending '"'. - for _ in 0..backslashes { - cmd.push('\\' as u16); - } - cmd.push('"' as u16); - } - } -} - -fn make_envp(env: Option<&collections::HashMap>) - -> (*mut c_void, Vec) { - // On Windows we pass an "environment block" which is not a char**, but - // rather a concatenation of null-terminated k=v\0 sequences, with a final - // \0 to terminate. - match env { - Some(env) => { - let mut blk = Vec::new(); - - for pair in env { - blk.extend(pair.0.encode_wide()); - blk.push('=' as u16); - blk.extend(pair.1.encode_wide()); - blk.push(0); - } - blk.push(0); - (blk.as_mut_ptr() as *mut c_void, blk) - } - _ => (ptr::null_mut(), Vec::new()) - } -} - -fn make_dirp(d: Option<&OsString>) -> (*const u16, Vec) { - match d { - Some(dir) => { - let mut dir_str: Vec = dir.encode_wide().collect(); - dir_str.push(0); - (dir_str.as_ptr(), dir_str) - }, - None => (ptr::null(), Vec::new()) - } -} - -impl Stdio { - fn to_handle(&self, stdio_id: libc::DWORD) -> io::Result { - use libc::DUPLICATE_SAME_ACCESS; - - match *self { - Stdio::Inherit => { - stdio::get(stdio_id).and_then(|io| { - io.handle().duplicate(0, true, DUPLICATE_SAME_ACCESS) - }) - } - Stdio::Piped(ref pipe) => { - pipe.handle().duplicate(0, true, DUPLICATE_SAME_ACCESS) - } - - // Similarly to unix, we don't actually leave holes for the - // stdio file descriptors, but rather open up /dev/null - // equivalents. These equivalents are drawn from libuv's - // windows process spawning. - Stdio::None => { - let size = mem::size_of::(); - let mut sa = libc::SECURITY_ATTRIBUTES { - nLength: size as libc::DWORD, - lpSecurityDescriptor: ptr::null_mut(), - bInheritHandle: 1, - }; - let mut opts = OpenOptions::new(); - opts.read(stdio_id == c::STD_INPUT_HANDLE); - opts.write(stdio_id != c::STD_INPUT_HANDLE); - opts.security_attributes(&mut sa); - File::open(Path::new("NUL"), &opts).map(|file| { - file.into_handle() - }) - } - } - } -} - -#[cfg(test)] -mod tests { - use prelude::v1::*; - use str; - use ffi::{OsStr, OsString}; - use super::make_command_line; - - #[test] - fn test_make_command_line() { - fn test_wrapper(prog: &str, args: &[&str]) -> String { - String::from_utf16( - &make_command_line(OsStr::new(prog), - &args.iter() - .map(|a| OsString::from(a)) - .collect::>())).unwrap() - } - - assert_eq!( - test_wrapper("prog", &["aaa", "bbb", "ccc"]), - "prog aaa bbb ccc" - ); - - assert_eq!( - test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]), - "\"C:\\Program Files\\blah\\blah.exe\" aaa" - ); - assert_eq!( - test_wrapper("C:\\Program Files\\test", &["aa\"bb"]), - "\"C:\\Program Files\\test\" aa\\\"bb" - ); - assert_eq!( - test_wrapper("echo", &["a b c"]), - "echo \"a b c\"" - ); - assert_eq!( - test_wrapper("echo", &["\" \\\" \\", "\\"]), - "echo \"\\\" \\\\\\\" \\\\\" \\" - ); - assert_eq!( - test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]), - "\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}" - ); - } -} -- cgit 1.4.1-3-g733a5