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/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 --------------------------- 9 files changed, 1066 insertions(+), 1066 deletions(-) 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/sys/windows') 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