diff options
| author | Valentin <[email protected]> | 2018-06-15 18:57:24 +0200 |
|---|---|---|
| committer | FenrirWolf <[email protected]> | 2018-06-15 10:57:24 -0600 |
| commit | f2a90174bb36b9ad528e863ab34c02ebce002b02 (patch) | |
| tree | 959e8d67883d3a89e179b3549b1f30d28e51a87c /ctr-std/src/sys/windows | |
| parent | Merge pull request #68 from linouxis9/master (diff) | |
| download | ctru-rs-f2a90174bb36b9ad528e863ab34c02ebce002b02.tar.xz ctru-rs-f2a90174bb36b9ad528e863ab34c02ebce002b02.zip | |
Update for latest nightly 2018-06-09 (#70)
* Update for latest nightly 2018-06-09
* We now have a proper horizon os and sys modules in libstd
Diffstat (limited to 'ctr-std/src/sys/windows')
37 files changed, 7470 insertions, 0 deletions
diff --git a/ctr-std/src/sys/windows/args.rs b/ctr-std/src/sys/windows/args.rs new file mode 100644 index 0000000..4784633 --- /dev/null +++ b/ctr-std/src/sys/windows/args.rs @@ -0,0 +1,107 @@ +// Copyright 2012-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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] // runtime init functions not used during testing + +use os::windows::prelude::*; +use sys::c; +use slice; +use ops::Range; +use ffi::OsString; +use libc::{c_int, c_void}; +use fmt; + +pub unsafe fn init(_argc: isize, _argv: *const *const u8) { } + +pub unsafe fn cleanup() { } + +pub fn args() -> Args { + unsafe { + let mut nArgs: c_int = 0; + let lpCmdLine = c::GetCommandLineW(); + let szArgList = c::CommandLineToArgvW(lpCmdLine, &mut nArgs); + + // szArcList can be NULL if CommandLinToArgvW failed, + // but in that case nArgs is 0 so we won't actually + // try to read a null pointer + Args { cur: szArgList, range: 0..(nArgs as isize) } + } +} + +pub struct Args { + range: Range<isize>, + cur: *mut *mut u16, +} + +pub struct ArgsInnerDebug<'a> { + args: &'a Args, +} + +impl<'a> fmt::Debug for ArgsInnerDebug<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("[")?; + let mut first = true; + for i in self.args.range.clone() { + if !first { + f.write_str(", ")?; + } + first = false; + + // Here we do allocation which could be avoided. + fmt::Debug::fmt(&unsafe { os_string_from_ptr(*self.args.cur.offset(i)) }, f)?; + } + f.write_str("]")?; + Ok(()) + } +} + +impl Args { + pub fn inner_debug(&self) -> ArgsInnerDebug { + ArgsInnerDebug { + args: self + } + } +} + +unsafe fn os_string_from_ptr(ptr: *mut u16) -> OsString { + let mut len = 0; + while *ptr.offset(len) != 0 { len += 1; } + + // Push it onto the list. + let ptr = ptr as *const u16; + let buf = slice::from_raw_parts(ptr, len as usize); + OsStringExt::from_wide(buf) +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option<OsString> { + self.range.next().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } ) + } + fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option<OsString> { + self.range.next_back().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } ) + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { self.range.len() } +} + +impl Drop for Args { + fn drop(&mut self) { + // self.cur can be null if CommandLineToArgvW previously failed, + // but LocalFree ignores NULL pointers + unsafe { c::LocalFree(self.cur as *mut c_void); } + } +} diff --git a/ctr-std/src/sys/windows/backtrace/backtrace_gnu.rs b/ctr-std/src/sys/windows/backtrace/backtrace_gnu.rs new file mode 100644 index 0000000..f0d29dd --- /dev/null +++ b/ctr-std/src/sys/windows/backtrace/backtrace_gnu.rs @@ -0,0 +1,62 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use sys::c; +use libc::c_char; +use path::PathBuf; +use fs::{OpenOptions, File}; +use sys::ext::fs::OpenOptionsExt; +use sys::handle::Handle; +use super::super::{fill_utf16_buf, os2path, to_u16s, wide_char_to_multi_byte}; + +fn query_full_process_image_name() -> io::Result<PathBuf> { + unsafe { + let process_handle = Handle::new(c::OpenProcess(c::PROCESS_QUERY_INFORMATION, + c::FALSE, + c::GetCurrentProcessId())); + fill_utf16_buf(|buf, mut sz| { + if c::QueryFullProcessImageNameW(process_handle.raw(), 0, buf, &mut sz) == 0 { + 0 + } else { + sz + } + }, os2path) + } +} + +fn lock_and_get_executable_filename() -> io::Result<(PathBuf, File)> { + // We query the current image name, open the file without FILE_SHARE_DELETE so it + // can't be moved and then get the current image name again. If the names are the + // same than we have successfully locked the file + let image_name1 = query_full_process_image_name()?; + let file = OpenOptions::new() + .read(true) + .share_mode(c::FILE_SHARE_READ | c::FILE_SHARE_WRITE) + .open(&image_name1)?; + let image_name2 = query_full_process_image_name()?; + + if image_name1 != image_name2 { + return Err(io::Error::new(io::ErrorKind::Other, + "executable moved while trying to lock it")); + } + + Ok((image_name1, file)) +} + +// Get the executable filename for libbacktrace +// This returns the path in the ANSI code page and a File which should remain open +// for as long as the path should remain valid +pub fn get_executable_filename() -> io::Result<(Vec<c_char>, File)> { + let (executable, file) = lock_and_get_executable_filename()?; + let u16_executable = to_u16s(executable.into_os_string())?; + Ok((wide_char_to_multi_byte(c::CP_ACP, c::WC_NO_BEST_FIT_CHARS, + &u16_executable, true)?, file)) +} diff --git a/ctr-std/src/sys/windows/backtrace/mod.rs b/ctr-std/src/sys/windows/backtrace/mod.rs new file mode 100644 index 0000000..82498ad --- /dev/null +++ b/ctr-std/src/sys/windows/backtrace/mod.rs @@ -0,0 +1,155 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! As always, windows has something very different than unix, we mainly want +//! to avoid having to depend too much on libunwind for windows. +//! +//! If you google around, you'll find a fair bit of references to built-in +//! functions to get backtraces on windows. It turns out that most of these are +//! in an external library called dbghelp. I was unable to find this library +//! via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent +//! of it. +//! +//! You'll also find that there's a function called CaptureStackBackTrace +//! mentioned frequently (which is also easy to use), but sadly I didn't have a +//! copy of that function in my mingw install (maybe it was broken?). Instead, +//! this takes the route of using StackWalk64 in order to walk the stack. + +#![allow(deprecated)] // dynamic_lib + +use io; +use libc::c_void; +use mem; +use ptr; +use sys::c; +use sys::dynamic_lib::DynamicLibrary; +use sys_common::backtrace::Frame; + +macro_rules! sym { + ($lib:expr, $e:expr, $t:ident) => ( + $lib.symbol($e).map(|f| unsafe { + $crate::mem::transmute::<usize, $t>(f) + }) + ) +} + +mod printing; + +#[cfg(target_env = "gnu")] +#[path = "backtrace_gnu.rs"] +pub mod gnu; + +pub use self::printing::{resolve_symname, foreach_symbol_fileline}; + +pub fn unwind_backtrace(frames: &mut [Frame]) + -> io::Result<(usize, BacktraceContext)> +{ + let dbghelp = DynamicLibrary::open("dbghelp.dll")?; + + // Fetch the symbols necessary from dbghelp.dll + let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn)?; + let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn)?; + let StackWalkEx = sym!(dbghelp, "StackWalkEx", StackWalkExFn)?; + + // Allocate necessary structures for doing the stack walk + let process = unsafe { c::GetCurrentProcess() }; + let thread = unsafe { c::GetCurrentThread() }; + let mut context: c::CONTEXT = unsafe { mem::zeroed() }; + unsafe { c::RtlCaptureContext(&mut context) }; + let mut frame: c::STACKFRAME_EX = unsafe { mem::zeroed() }; + frame.StackFrameSize = mem::size_of_val(&frame) as c::DWORD; + let image = init_frame(&mut frame, &context); + + let backtrace_context = BacktraceContext { + handle: process, + SymCleanup, + dbghelp, + }; + + // Initialize this process's symbols + let ret = unsafe { SymInitialize(process, ptr::null_mut(), c::TRUE) }; + if ret != c::TRUE { + return Ok((0, backtrace_context)) + } + + // And now that we're done with all the setup, do the stack walking! + let mut i = 0; + unsafe { + while i < frames.len() && + StackWalkEx(image, process, thread, &mut frame, &mut context, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + 0) == c::TRUE + { + let addr = (frame.AddrPC.Offset - 1) as *const u8; + + frames[i] = Frame { + symbol_addr: addr, + exact_position: addr, + inline_context: frame.InlineFrameContext, + }; + i += 1; + } + } + + Ok((i, backtrace_context)) +} + +type SymInitializeFn = + unsafe extern "system" fn(c::HANDLE, *mut c_void, + c::BOOL) -> c::BOOL; +type SymCleanupFn = + unsafe extern "system" fn(c::HANDLE) -> c::BOOL; + +type StackWalkExFn = + unsafe extern "system" fn(c::DWORD, c::HANDLE, c::HANDLE, + *mut c::STACKFRAME_EX, *mut c::CONTEXT, + *mut c_void, *mut c_void, + *mut c_void, *mut c_void, c::DWORD) -> c::BOOL; + +#[cfg(target_arch = "x86")] +fn init_frame(frame: &mut c::STACKFRAME_EX, + ctx: &c::CONTEXT) -> c::DWORD { + frame.AddrPC.Offset = ctx.Eip as u64; + frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; + frame.AddrStack.Offset = ctx.Esp as u64; + frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat; + frame.AddrFrame.Offset = ctx.Ebp as u64; + frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat; + c::IMAGE_FILE_MACHINE_I386 +} + +#[cfg(target_arch = "x86_64")] +fn init_frame(frame: &mut c::STACKFRAME_EX, + ctx: &c::CONTEXT) -> c::DWORD { + frame.AddrPC.Offset = ctx.Rip as u64; + frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; + frame.AddrStack.Offset = ctx.Rsp as u64; + frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat; + frame.AddrFrame.Offset = ctx.Rbp as u64; + frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat; + c::IMAGE_FILE_MACHINE_AMD64 +} + +pub struct BacktraceContext { + handle: c::HANDLE, + SymCleanup: SymCleanupFn, + // Only used in printing for msvc and not gnu + #[allow(dead_code)] + dbghelp: DynamicLibrary, +} + +impl Drop for BacktraceContext { + fn drop(&mut self) { + unsafe { (self.SymCleanup)(self.handle); } + } +} diff --git a/ctr-std/src/sys/windows/backtrace/printing/mod.rs b/ctr-std/src/sys/windows/backtrace/printing/mod.rs new file mode 100644 index 0000000..3e566f6 --- /dev/null +++ b/ctr-std/src/sys/windows/backtrace/printing/mod.rs @@ -0,0 +1,20 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[cfg(target_env = "msvc")] +#[path = "msvc.rs"] +mod printing; + +#[cfg(target_env = "gnu")] +mod printing { + pub use sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname}; +} + +pub use self::printing::{foreach_symbol_fileline, resolve_symname}; diff --git a/ctr-std/src/sys/windows/backtrace/printing/msvc.rs b/ctr-std/src/sys/windows/backtrace/printing/msvc.rs new file mode 100644 index 0000000..967df1c --- /dev/null +++ b/ctr-std/src/sys/windows/backtrace/printing/msvc.rs @@ -0,0 +1,97 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi::CStr; +use io; +use libc::{c_ulong, c_char}; +use mem; +use sys::c; +use sys::backtrace::BacktraceContext; +use sys_common::backtrace::Frame; + +type SymFromInlineContextFn = + unsafe extern "system" fn(c::HANDLE, u64, c::ULONG, + *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL; +type SymGetLineFromInlineContextFn = + unsafe extern "system" fn(c::HANDLE, u64, c::ULONG, + u64, *mut c::DWORD, *mut c::IMAGEHLP_LINE64) -> c::BOOL; + +/// Converts a pointer to symbol to its string value. +pub fn resolve_symname<F>(frame: Frame, + callback: F, + context: &BacktraceContext) -> io::Result<()> + where F: FnOnce(Option<&str>) -> io::Result<()> +{ + let SymFromInlineContext = sym!(&context.dbghelp, + "SymFromInlineContext", + SymFromInlineContextFn)?; + + unsafe { + let mut info: c::SYMBOL_INFO = mem::zeroed(); + info.MaxNameLen = c::MAX_SYM_NAME as c_ulong; + // the struct size in C. the value is different to + // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81) + // due to struct alignment. + info.SizeOfStruct = 88; + + let mut displacement = 0u64; + let ret = SymFromInlineContext(context.handle, + frame.symbol_addr as u64, + frame.inline_context, + &mut displacement, + &mut info); + let valid_range = if ret == c::TRUE && + frame.symbol_addr as usize >= info.Address as usize { + if info.Size != 0 { + (frame.symbol_addr as usize) < info.Address as usize + info.Size as usize + } else { + true + } + } else { + false + }; + let symname = if valid_range { + let ptr = info.Name.as_ptr() as *const c_char; + CStr::from_ptr(ptr).to_str().ok() + } else { + None + }; + callback(symname) + } +} + +pub fn foreach_symbol_fileline<F>(frame: Frame, + mut f: F, + context: &BacktraceContext) + -> io::Result<bool> + where F: FnMut(&[u8], u32) -> io::Result<()> +{ + let SymGetLineFromInlineContext = sym!(&context.dbghelp, + "SymGetLineFromInlineContext", + SymGetLineFromInlineContextFn)?; + + unsafe { + let mut line: c::IMAGEHLP_LINE64 = mem::zeroed(); + line.SizeOfStruct = ::mem::size_of::<c::IMAGEHLP_LINE64>() as u32; + + let mut displacement = 0u32; + let ret = SymGetLineFromInlineContext(context.handle, + frame.exact_position as u64, + frame.inline_context, + 0, + &mut displacement, + &mut line); + if ret == c::TRUE { + let name = CStr::from_ptr(line.Filename).to_bytes(); + f(name, line.LineNumber as u32)?; + } + Ok(false) + } +} diff --git a/ctr-std/src/sys/windows/c.rs b/ctr-std/src/sys/windows/c.rs new file mode 100644 index 0000000..6d929f2 --- /dev/null +++ b/ctr-std/src/sys/windows/c.rs @@ -0,0 +1,1254 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! C definitions used by libnative that don't belong in liblibc + +#![allow(bad_style)] +#![cfg_attr(test, allow(dead_code))] +#![unstable(issue = "0", feature = "windows_c")] + +use os::raw::{c_int, c_uint, c_ulong, c_long, c_longlong, c_ushort, c_char}; +#[cfg(target_arch = "x86_64")] +use os::raw::c_ulonglong; +use libc::{wchar_t, size_t, c_void}; +use ptr; + +pub use self::FILE_INFO_BY_HANDLE_CLASS::*; +pub use self::EXCEPTION_DISPOSITION::*; + +pub type DWORD = c_ulong; +pub type HANDLE = LPVOID; +pub type HINSTANCE = HANDLE; +pub type HMODULE = HINSTANCE; +pub type HRESULT = LONG; +pub type BOOL = c_int; +pub type BYTE = u8; +pub type BOOLEAN = BYTE; +pub type GROUP = c_uint; +pub type LARGE_INTEGER = c_longlong; +pub type LONG = c_long; +pub type UINT = c_uint; +pub type WCHAR = u16; +pub type USHORT = c_ushort; +pub type SIZE_T = usize; +pub type WORD = u16; +pub type CHAR = c_char; +pub type ULONG_PTR = usize; +pub type ULONG = c_ulong; +#[cfg(target_arch = "x86_64")] +pub type ULONGLONG = u64; +#[cfg(target_arch = "x86_64")] +pub type DWORDLONG = ULONGLONG; + +pub type LPBOOL = *mut BOOL; +pub type LPBYTE = *mut BYTE; +pub type LPBY_HANDLE_FILE_INFORMATION = *mut BY_HANDLE_FILE_INFORMATION; +pub type LPCSTR = *const CHAR; +pub type LPCVOID = *const c_void; +pub type LPCWSTR = *const WCHAR; +pub type LPDWORD = *mut DWORD; +pub type LPHANDLE = *mut HANDLE; +pub type LPOVERLAPPED = *mut OVERLAPPED; +pub type LPPROCESS_INFORMATION = *mut PROCESS_INFORMATION; +pub type LPSECURITY_ATTRIBUTES = *mut SECURITY_ATTRIBUTES; +pub type LPSTARTUPINFO = *mut STARTUPINFO; +pub type LPVOID = *mut c_void; +pub type LPWCH = *mut WCHAR; +pub type LPWIN32_FIND_DATAW = *mut WIN32_FIND_DATAW; +pub type LPWSADATA = *mut WSADATA; +pub type LPWSAPROTOCOL_INFO = *mut WSAPROTOCOL_INFO; +pub type LPSTR = *mut CHAR; +pub type LPWSTR = *mut WCHAR; +pub type LPFILETIME = *mut FILETIME; + +pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE; +pub type PLARGE_INTEGER = *mut c_longlong; +pub type PSRWLOCK = *mut SRWLOCK; + +pub type SOCKET = ::os::windows::raw::SOCKET; +pub type socklen_t = c_int; +pub type ADDRESS_FAMILY = USHORT; + +pub const TRUE: BOOL = 1; +pub const FALSE: BOOL = 0; + +pub const FILE_ATTRIBUTE_READONLY: DWORD = 0x1; +pub const FILE_ATTRIBUTE_DIRECTORY: DWORD = 0x10; +pub const FILE_ATTRIBUTE_REPARSE_POINT: DWORD = 0x400; + +pub const FILE_SHARE_DELETE: DWORD = 0x4; +pub const FILE_SHARE_READ: DWORD = 0x1; +pub const FILE_SHARE_WRITE: DWORD = 0x2; + +pub const CREATE_ALWAYS: DWORD = 2; +pub const CREATE_NEW: DWORD = 1; +pub const OPEN_ALWAYS: DWORD = 4; +pub const OPEN_EXISTING: DWORD = 3; +pub const TRUNCATE_EXISTING: DWORD = 5; + +pub const FILE_WRITE_DATA: DWORD = 0x00000002; +pub const FILE_APPEND_DATA: DWORD = 0x00000004; +pub const FILE_WRITE_EA: DWORD = 0x00000010; +pub const FILE_WRITE_ATTRIBUTES: DWORD = 0x00000100; +pub const READ_CONTROL: DWORD = 0x00020000; +pub const SYNCHRONIZE: DWORD = 0x00100000; +pub const GENERIC_READ: DWORD = 0x80000000; +pub const GENERIC_WRITE: DWORD = 0x40000000; +pub const STANDARD_RIGHTS_WRITE: DWORD = READ_CONTROL; +pub const FILE_GENERIC_WRITE: DWORD = STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | + FILE_WRITE_ATTRIBUTES | + FILE_WRITE_EA | + FILE_APPEND_DATA | + SYNCHRONIZE; + +pub const FILE_FLAG_OPEN_REPARSE_POINT: DWORD = 0x00200000; +pub const FILE_FLAG_BACKUP_SEMANTICS: DWORD = 0x02000000; +pub const SECURITY_SQOS_PRESENT: DWORD = 0x00100000; + +pub const FIONBIO: c_ulong = 0x8004667e; + +#[repr(C)] +#[derive(Copy)] +pub struct WIN32_FIND_DATAW { + pub dwFileAttributes: DWORD, + pub ftCreationTime: FILETIME, + pub ftLastAccessTime: FILETIME, + pub ftLastWriteTime: FILETIME, + pub nFileSizeHigh: DWORD, + pub nFileSizeLow: DWORD, + pub dwReserved0: DWORD, + pub dwReserved1: DWORD, + pub cFileName: [wchar_t; 260], // #define MAX_PATH 260 + pub cAlternateFileName: [wchar_t; 14], +} +impl Clone for WIN32_FIND_DATAW { + fn clone(&self) -> Self { *self } +} + +pub const WSA_FLAG_OVERLAPPED: DWORD = 0x01; + +pub const WSADESCRIPTION_LEN: usize = 256; +pub const WSASYS_STATUS_LEN: usize = 128; +pub const WSAPROTOCOL_LEN: DWORD = 255; +pub const INVALID_SOCKET: SOCKET = !0; + +pub const WSAEACCES: c_int = 10013; +pub const WSAEINVAL: c_int = 10022; +pub const WSAEWOULDBLOCK: c_int = 10035; +pub const WSAEADDRINUSE: c_int = 10048; +pub const WSAEADDRNOTAVAIL: c_int = 10049; +pub const WSAECONNABORTED: c_int = 10053; +pub const WSAECONNRESET: c_int = 10054; +pub const WSAENOTCONN: c_int = 10057; +pub const WSAESHUTDOWN: c_int = 10058; +pub const WSAETIMEDOUT: c_int = 10060; +pub const WSAECONNREFUSED: c_int = 10061; + +pub const MAX_PROTOCOL_CHAIN: DWORD = 7; + +pub const TOKEN_READ: DWORD = 0x20008; +pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: usize = 16 * 1024; +pub const FSCTL_GET_REPARSE_POINT: DWORD = 0x900a8; +pub const IO_REPARSE_TAG_SYMLINK: DWORD = 0xa000000c; +pub const IO_REPARSE_TAG_MOUNT_POINT: DWORD = 0xa0000003; +pub const SYMLINK_FLAG_RELATIVE: DWORD = 0x00000001; +pub const FSCTL_SET_REPARSE_POINT: DWORD = 0x900a4; + +pub const SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 0x1; +pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: DWORD = 0x2; + +// Note that these are not actually HANDLEs, just values to pass to GetStdHandle +pub const STD_INPUT_HANDLE: DWORD = -10i32 as DWORD; +pub const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD; +pub const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD; + +pub const HANDLE_FLAG_INHERIT: DWORD = 0x00000001; + +pub const PROGRESS_CONTINUE: DWORD = 0; + +pub const ERROR_FILE_NOT_FOUND: DWORD = 2; +pub const ERROR_PATH_NOT_FOUND: DWORD = 3; +pub const ERROR_ACCESS_DENIED: DWORD = 5; +pub const ERROR_INVALID_HANDLE: DWORD = 6; +pub const ERROR_NO_MORE_FILES: DWORD = 18; +pub const ERROR_HANDLE_EOF: DWORD = 38; +pub const ERROR_FILE_EXISTS: DWORD = 80; +pub const ERROR_INVALID_PARAMETER: DWORD = 87; +pub const ERROR_BROKEN_PIPE: DWORD = 109; +pub const ERROR_CALL_NOT_IMPLEMENTED: DWORD = 120; +pub const ERROR_INSUFFICIENT_BUFFER: DWORD = 122; +pub const ERROR_ALREADY_EXISTS: DWORD = 183; +pub const ERROR_NO_DATA: DWORD = 232; +pub const ERROR_ENVVAR_NOT_FOUND: DWORD = 203; +pub const ERROR_OPERATION_ABORTED: DWORD = 995; +pub const ERROR_IO_PENDING: DWORD = 997; +pub const ERROR_TIMEOUT: DWORD = 0x5B4; + +pub const E_NOTIMPL: HRESULT = 0x80004001u32 as HRESULT; + +pub const INVALID_HANDLE_VALUE: HANDLE = !0 as HANDLE; + +pub const FACILITY_NT_BIT: DWORD = 0x1000_0000; + +pub const FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000; +pub const FORMAT_MESSAGE_FROM_HMODULE: DWORD = 0x00000800; +pub const FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200; + +pub const TLS_OUT_OF_INDEXES: DWORD = 0xFFFFFFFF; + +pub const DLL_THREAD_DETACH: DWORD = 3; +pub const DLL_PROCESS_DETACH: DWORD = 0; + +pub const INFINITE: DWORD = !0; + +pub const DUPLICATE_SAME_ACCESS: DWORD = 0x00000002; + +pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE { + ptr: ptr::null_mut(), +}; +pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: ptr::null_mut() }; + +pub const DETACHED_PROCESS: DWORD = 0x00000008; +pub const CREATE_NEW_PROCESS_GROUP: DWORD = 0x00000200; +pub const CREATE_UNICODE_ENVIRONMENT: DWORD = 0x00000400; +pub const STARTF_USESTDHANDLES: DWORD = 0x00000100; + +pub const AF_INET: c_int = 2; +pub const AF_INET6: c_int = 23; +pub const SD_BOTH: c_int = 2; +pub const SD_RECEIVE: c_int = 0; +pub const SD_SEND: c_int = 1; +pub const SOCK_DGRAM: c_int = 2; +pub const SOCK_STREAM: c_int = 1; +pub const SOL_SOCKET: c_int = 0xffff; +pub const SO_RCVTIMEO: c_int = 0x1006; +pub const SO_SNDTIMEO: c_int = 0x1005; +pub const SO_REUSEADDR: c_int = 0x0004; +pub const IPPROTO_IP: c_int = 0; +pub const IPPROTO_TCP: c_int = 6; +pub const IPPROTO_IPV6: c_int = 41; +pub const TCP_NODELAY: c_int = 0x0001; +pub const IP_TTL: c_int = 4; +pub const IPV6_V6ONLY: c_int = 27; +pub const SO_ERROR: c_int = 0x1007; +pub const SO_BROADCAST: c_int = 0x0020; +pub const IP_MULTICAST_LOOP: c_int = 11; +pub const IPV6_MULTICAST_LOOP: c_int = 11; +pub const IP_MULTICAST_TTL: c_int = 10; +pub const IP_ADD_MEMBERSHIP: c_int = 12; +pub const IP_DROP_MEMBERSHIP: c_int = 13; +pub const IPV6_ADD_MEMBERSHIP: c_int = 12; +pub const IPV6_DROP_MEMBERSHIP: c_int = 13; +pub const MSG_PEEK: c_int = 0x2; + +#[repr(C)] +pub struct ip_mreq { + pub imr_multiaddr: in_addr, + pub imr_interface: in_addr, +} + +#[repr(C)] +pub struct ipv6_mreq { + pub ipv6mr_multiaddr: in6_addr, + pub ipv6mr_interface: c_uint, +} + +pub const VOLUME_NAME_DOS: DWORD = 0x0; +pub const MOVEFILE_REPLACE_EXISTING: DWORD = 1; + +pub const FILE_BEGIN: DWORD = 0; +pub const FILE_CURRENT: DWORD = 1; +pub const FILE_END: DWORD = 2; + +pub const WAIT_OBJECT_0: DWORD = 0x00000000; +pub const WAIT_TIMEOUT: DWORD = 258; +pub const WAIT_FAILED: DWORD = 0xFFFFFFFF; + +#[cfg(target_env = "msvc")] +#[cfg(feature = "backtrace")] +pub const MAX_SYM_NAME: usize = 2000; +#[cfg(target_arch = "x86")] +#[cfg(feature = "backtrace")] +pub const IMAGE_FILE_MACHINE_I386: DWORD = 0x014c; +#[cfg(target_arch = "x86_64")] +#[cfg(feature = "backtrace")] +pub const IMAGE_FILE_MACHINE_AMD64: DWORD = 0x8664; + +pub const EXCEPTION_CONTINUE_SEARCH: LONG = 0; +pub const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd; +pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; + +pub const PIPE_ACCESS_INBOUND: DWORD = 0x00000001; +pub const PIPE_ACCESS_OUTBOUND: DWORD = 0x00000002; +pub const FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD = 0x00080000; +pub const FILE_FLAG_OVERLAPPED: DWORD = 0x40000000; +pub const PIPE_WAIT: DWORD = 0x00000000; +pub const PIPE_TYPE_BYTE: DWORD = 0x00000000; +pub const PIPE_REJECT_REMOTE_CLIENTS: DWORD = 0x00000008; +pub const PIPE_READMODE_BYTE: DWORD = 0x00000000; + +pub const FD_SETSIZE: usize = 64; + +#[repr(C)] +#[cfg(not(target_pointer_width = "64"))] +pub struct WSADATA { + pub wVersion: WORD, + pub wHighVersion: WORD, + pub szDescription: [u8; WSADESCRIPTION_LEN + 1], + pub szSystemStatus: [u8; WSASYS_STATUS_LEN + 1], + pub iMaxSockets: u16, + pub iMaxUdpDg: u16, + pub lpVendorInfo: *mut u8, +} +#[repr(C)] +#[cfg(target_pointer_width = "64")] +pub struct WSADATA { + pub wVersion: WORD, + pub wHighVersion: WORD, + pub iMaxSockets: u16, + pub iMaxUdpDg: u16, + pub lpVendorInfo: *mut u8, + pub szDescription: [u8; WSADESCRIPTION_LEN + 1], + pub szSystemStatus: [u8; WSASYS_STATUS_LEN + 1], +} + +#[repr(C)] +pub struct WSAPROTOCOL_INFO { + pub dwServiceFlags1: DWORD, + pub dwServiceFlags2: DWORD, + pub dwServiceFlags3: DWORD, + pub dwServiceFlags4: DWORD, + pub dwProviderFlags: DWORD, + pub ProviderId: GUID, + pub dwCatalogEntryId: DWORD, + pub ProtocolChain: WSAPROTOCOLCHAIN, + pub iVersion: c_int, + pub iAddressFamily: c_int, + pub iMaxSockAddr: c_int, + pub iMinSockAddr: c_int, + pub iSocketType: c_int, + pub iProtocol: c_int, + pub iProtocolMaxOffset: c_int, + pub iNetworkByteOrder: c_int, + pub iSecurityScheme: c_int, + pub dwMessageSize: DWORD, + pub dwProviderReserved: DWORD, + pub szProtocol: [u16; (WSAPROTOCOL_LEN as usize) + 1], +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct WIN32_FILE_ATTRIBUTE_DATA { + pub dwFileAttributes: DWORD, + pub ftCreationTime: FILETIME, + pub ftLastAccessTime: FILETIME, + pub ftLastWriteTime: FILETIME, + pub nFileSizeHigh: DWORD, + pub nFileSizeLow: DWORD, +} + +#[repr(C)] +pub struct BY_HANDLE_FILE_INFORMATION { + pub dwFileAttributes: DWORD, + pub ftCreationTime: FILETIME, + pub ftLastAccessTime: FILETIME, + pub ftLastWriteTime: FILETIME, + pub dwVolumeSerialNumber: DWORD, + pub nFileSizeHigh: DWORD, + pub nFileSizeLow: DWORD, + pub nNumberOfLinks: DWORD, + pub nFileIndexHigh: DWORD, + pub nFileIndexLow: DWORD, +} + +#[repr(C)] +#[allow(dead_code)] // we only use some variants +pub enum FILE_INFO_BY_HANDLE_CLASS { + FileBasicInfo = 0, + FileStandardInfo = 1, + FileNameInfo = 2, + FileRenameInfo = 3, + FileDispositionInfo = 4, + FileAllocationInfo = 5, + FileEndOfFileInfo = 6, + FileStreamInfo = 7, + FileCompressionInfo = 8, + FileAttributeTagInfo = 9, + FileIdBothDirectoryInfo = 10, // 0xA + FileIdBothDirectoryRestartInfo = 11, // 0xB + FileIoPriorityHintInfo = 12, // 0xC + FileRemoteProtocolInfo = 13, // 0xD + FileFullDirectoryInfo = 14, // 0xE + FileFullDirectoryRestartInfo = 15, // 0xF + FileStorageInfo = 16, // 0x10 + FileAlignmentInfo = 17, // 0x11 + FileIdInfo = 18, // 0x12 + FileIdExtdDirectoryInfo = 19, // 0x13 + FileIdExtdDirectoryRestartInfo = 20, // 0x14 + MaximumFileInfoByHandlesClass +} + +#[repr(C)] +pub struct FILE_BASIC_INFO { + pub CreationTime: LARGE_INTEGER, + pub LastAccessTime: LARGE_INTEGER, + pub LastWriteTime: LARGE_INTEGER, + pub ChangeTime: LARGE_INTEGER, + pub FileAttributes: DWORD, +} + +#[repr(C)] +pub struct FILE_END_OF_FILE_INFO { + pub EndOfFile: LARGE_INTEGER, +} + +#[repr(C)] +pub struct REPARSE_DATA_BUFFER { + pub ReparseTag: c_uint, + pub ReparseDataLength: c_ushort, + pub Reserved: c_ushort, + pub rest: (), +} + +#[repr(C)] +pub struct SYMBOLIC_LINK_REPARSE_BUFFER { + pub SubstituteNameOffset: c_ushort, + pub SubstituteNameLength: c_ushort, + pub PrintNameOffset: c_ushort, + pub PrintNameLength: c_ushort, + pub Flags: c_ulong, + pub PathBuffer: WCHAR, +} + +#[repr(C)] +pub struct MOUNT_POINT_REPARSE_BUFFER { + pub SubstituteNameOffset: c_ushort, + pub SubstituteNameLength: c_ushort, + pub PrintNameOffset: c_ushort, + pub PrintNameLength: c_ushort, + pub PathBuffer: WCHAR, +} + +pub type LPPROGRESS_ROUTINE = ::option::Option<unsafe extern "system" fn( + TotalFileSize: LARGE_INTEGER, + TotalBytesTransferred: LARGE_INTEGER, + StreamSize: LARGE_INTEGER, + StreamBytesTransferred: LARGE_INTEGER, + dwStreamNumber: DWORD, + dwCallbackReason: DWORD, + hSourceFile: HANDLE, + hDestinationFile: HANDLE, + lpData: LPVOID, +) -> DWORD>; + +#[repr(C)] +pub struct CONDITION_VARIABLE { pub ptr: LPVOID } +#[repr(C)] +pub struct SRWLOCK { pub ptr: LPVOID } +#[repr(C)] +pub struct CRITICAL_SECTION { + CriticalSectionDebug: LPVOID, + LockCount: LONG, + RecursionCount: LONG, + OwningThread: HANDLE, + LockSemaphore: HANDLE, + SpinCount: ULONG_PTR +} + +#[repr(C)] +pub struct REPARSE_MOUNTPOINT_DATA_BUFFER { + pub ReparseTag: DWORD, + pub ReparseDataLength: DWORD, + pub Reserved: WORD, + pub ReparseTargetLength: WORD, + pub ReparseTargetMaximumLength: WORD, + pub Reserved1: WORD, + pub ReparseTarget: WCHAR, +} + +#[repr(C)] +pub struct EXCEPTION_RECORD { + pub ExceptionCode: DWORD, + pub ExceptionFlags: DWORD, + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ExceptionAddress: LPVOID, + pub NumberParameters: DWORD, + pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS] +} + +#[repr(C)] +pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ContextRecord: *mut CONTEXT, +} + +pub type PVECTORED_EXCEPTION_HANDLER = extern "system" + fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG; + +#[repr(C)] +pub struct GUID { + pub Data1: DWORD, + pub Data2: WORD, + pub Data3: WORD, + pub Data4: [BYTE; 8], +} + +#[repr(C)] +pub struct WSAPROTOCOLCHAIN { + pub ChainLen: c_int, + pub ChainEntries: [DWORD; MAX_PROTOCOL_CHAIN as usize], +} + +#[repr(C)] +pub struct SECURITY_ATTRIBUTES { + pub nLength: DWORD, + pub lpSecurityDescriptor: LPVOID, + pub bInheritHandle: BOOL, +} + +#[repr(C)] +pub struct PROCESS_INFORMATION { + pub hProcess: HANDLE, + pub hThread: HANDLE, + pub dwProcessId: DWORD, + pub dwThreadId: DWORD, +} + +#[repr(C)] +pub struct STARTUPINFO { + pub cb: DWORD, + pub lpReserved: LPWSTR, + pub lpDesktop: LPWSTR, + pub lpTitle: LPWSTR, + pub dwX: DWORD, + pub dwY: DWORD, + pub dwXSize: DWORD, + pub dwYSize: DWORD, + pub dwXCountChars: DWORD, + pub dwYCountCharts: DWORD, + pub dwFillAttribute: DWORD, + pub dwFlags: DWORD, + pub wShowWindow: WORD, + pub cbReserved2: WORD, + pub lpReserved2: LPBYTE, + pub hStdInput: HANDLE, + pub hStdOutput: HANDLE, + pub hStdError: HANDLE, +} + +#[repr(C)] +pub struct SOCKADDR { + pub sa_family: ADDRESS_FAMILY, + pub sa_data: [CHAR; 14], +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct FILETIME { + pub dwLowDateTime: DWORD, + pub dwHighDateTime: DWORD, +} + +#[repr(C)] +pub struct OVERLAPPED { + pub Internal: *mut c_ulong, + pub InternalHigh: *mut c_ulong, + pub Offset: DWORD, + pub OffsetHigh: DWORD, + pub hEvent: HANDLE, +} + +#[repr(C)] +#[cfg(target_env = "msvc")] +#[cfg(feature = "backtrace")] +pub struct SYMBOL_INFO { + pub SizeOfStruct: c_ulong, + pub TypeIndex: c_ulong, + pub Reserved: [u64; 2], + pub Index: c_ulong, + pub Size: c_ulong, + pub ModBase: u64, + pub Flags: c_ulong, + pub Value: u64, + pub Address: u64, + pub Register: c_ulong, + pub Scope: c_ulong, + pub Tag: c_ulong, + pub NameLen: c_ulong, + pub MaxNameLen: c_ulong, + // note that windows has this as 1, but it basically just means that + // the name is inline at the end of the struct. For us, we just bump + // the struct size up to MAX_SYM_NAME. + pub Name: [c_char; MAX_SYM_NAME], +} + +#[repr(C)] +#[cfg(target_env = "msvc")] +#[cfg(feature = "backtrace")] +pub struct IMAGEHLP_LINE64 { + pub SizeOfStruct: u32, + pub Key: *const c_void, + pub LineNumber: u32, + pub Filename: *const c_char, + pub Address: u64, +} + +#[repr(C)] +#[allow(dead_code)] // we only use some variants +pub enum ADDRESS_MODE { + AddrMode1616, + AddrMode1632, + AddrModeReal, + AddrModeFlat, +} + +#[repr(C)] +#[cfg(feature = "backtrace")] +pub struct ADDRESS64 { + pub Offset: u64, + pub Segment: u16, + pub Mode: ADDRESS_MODE, +} + +#[repr(C)] +#[cfg(feature = "backtrace")] +pub struct STACKFRAME_EX { + pub AddrPC: ADDRESS64, + pub AddrReturn: ADDRESS64, + pub AddrFrame: ADDRESS64, + pub AddrStack: ADDRESS64, + pub AddrBStore: ADDRESS64, + pub FuncTableEntry: *mut c_void, + pub Params: [u64; 4], + pub Far: BOOL, + pub Virtual: BOOL, + pub Reserved: [u64; 3], + pub KdHelp: KDHELP64, + pub StackFrameSize: DWORD, + pub InlineFrameContext: DWORD, +} + +#[repr(C)] +#[cfg(feature = "backtrace")] +pub struct KDHELP64 { + pub Thread: u64, + pub ThCallbackStack: DWORD, + pub ThCallbackBStore: DWORD, + pub NextCallback: DWORD, + pub FramePointer: DWORD, + pub KiCallUserMode: u64, + pub KeUserCallbackDispatcher: u64, + pub SystemRangeStart: u64, + pub KiUserExceptionDispatcher: u64, + pub StackBase: u64, + pub StackLimit: u64, + pub Reserved: [u64; 5], +} + +#[cfg(target_arch = "x86")] +#[repr(C)] +pub struct CONTEXT { + pub ContextFlags: DWORD, + pub Dr0: DWORD, + pub Dr1: DWORD, + pub Dr2: DWORD, + pub Dr3: DWORD, + pub Dr6: DWORD, + pub Dr7: DWORD, + pub FloatSave: FLOATING_SAVE_AREA, + pub SegGs: DWORD, + pub SegFs: DWORD, + pub SegEs: DWORD, + pub SegDs: DWORD, + pub Edi: DWORD, + pub Esi: DWORD, + pub Ebx: DWORD, + pub Edx: DWORD, + pub Ecx: DWORD, + pub Eax: DWORD, + pub Ebp: DWORD, + pub Eip: DWORD, + pub SegCs: DWORD, + pub EFlags: DWORD, + pub Esp: DWORD, + pub SegSs: DWORD, + pub ExtendedRegisters: [u8; 512], +} + +#[cfg(target_arch = "x86")] +#[repr(C)] +pub struct FLOATING_SAVE_AREA { + pub ControlWord: DWORD, + pub StatusWord: DWORD, + pub TagWord: DWORD, + pub ErrorOffset: DWORD, + pub ErrorSelector: DWORD, + pub DataOffset: DWORD, + pub DataSelector: DWORD, + pub RegisterArea: [u8; 80], + pub Cr0NpxState: DWORD, +} + +#[cfg(target_arch = "x86_64")] +#[repr(C, align(16))] +pub struct CONTEXT { + pub P1Home: DWORDLONG, + pub P2Home: DWORDLONG, + pub P3Home: DWORDLONG, + pub P4Home: DWORDLONG, + pub P5Home: DWORDLONG, + pub P6Home: DWORDLONG, + + pub ContextFlags: DWORD, + pub MxCsr: DWORD, + + pub SegCs: WORD, + pub SegDs: WORD, + pub SegEs: WORD, + pub SegFs: WORD, + pub SegGs: WORD, + pub SegSs: WORD, + pub EFlags: DWORD, + + pub Dr0: DWORDLONG, + pub Dr1: DWORDLONG, + pub Dr2: DWORDLONG, + pub Dr3: DWORDLONG, + pub Dr6: DWORDLONG, + pub Dr7: DWORDLONG, + + pub Rax: DWORDLONG, + pub Rcx: DWORDLONG, + pub Rdx: DWORDLONG, + pub Rbx: DWORDLONG, + pub Rsp: DWORDLONG, + pub Rbp: DWORDLONG, + pub Rsi: DWORDLONG, + pub Rdi: DWORDLONG, + pub R8: DWORDLONG, + pub R9: DWORDLONG, + pub R10: DWORDLONG, + pub R11: DWORDLONG, + pub R12: DWORDLONG, + pub R13: DWORDLONG, + pub R14: DWORDLONG, + pub R15: DWORDLONG, + + pub Rip: DWORDLONG, + + pub FltSave: FLOATING_SAVE_AREA, + + pub VectorRegister: [M128A; 26], + pub VectorControl: DWORDLONG, + + pub DebugControl: DWORDLONG, + pub LastBranchToRip: DWORDLONG, + pub LastBranchFromRip: DWORDLONG, + pub LastExceptionToRip: DWORDLONG, + pub LastExceptionFromRip: DWORDLONG, +} + +#[cfg(target_arch = "x86_64")] +#[repr(C, align(16))] +pub struct M128A { + pub Low: c_ulonglong, + pub High: c_longlong +} + +#[cfg(target_arch = "x86_64")] +#[repr(C, align(16))] +pub struct FLOATING_SAVE_AREA { + _Dummy: [u8; 512] // FIXME: Fill this out +} + +// FIXME(#43348): This structure is used for backtrace only, and a fake +// definition is provided here only to allow rustdoc to pass type-check. This +// will not appear in the final documentation. This should be also defined for +// other architectures supported by Windows such as ARM, and for historical +// interest, maybe MIPS and PowerPC as well. +#[cfg(all(dox, not(any(target_arch = "x86_64", target_arch = "x86"))))] +pub enum CONTEXT {} + +#[repr(C)] +pub struct SOCKADDR_STORAGE_LH { + pub ss_family: ADDRESS_FAMILY, + pub __ss_pad1: [CHAR; 6], + pub __ss_align: i64, + pub __ss_pad2: [CHAR; 112], +} + +#[repr(C)] +pub struct ADDRINFOA { + pub ai_flags: c_int, + pub ai_family: c_int, + pub ai_socktype: c_int, + pub ai_protocol: c_int, + pub ai_addrlen: size_t, + pub ai_canonname: *mut c_char, + pub ai_addr: *mut SOCKADDR, + pub ai_next: *mut ADDRINFOA, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct sockaddr_in { + pub sin_family: ADDRESS_FAMILY, + pub sin_port: USHORT, + pub sin_addr: in_addr, + pub sin_zero: [CHAR; 8], +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct sockaddr_in6 { + pub sin6_family: ADDRESS_FAMILY, + pub sin6_port: USHORT, + pub sin6_flowinfo: c_ulong, + pub sin6_addr: in6_addr, + pub sin6_scope_id: c_ulong, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct in_addr { + pub s_addr: u32, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct in6_addr { + pub s6_addr: [u8; 16], +} + +#[repr(C)] +#[derive(Copy, Clone)] +#[allow(dead_code)] // we only use some variants +pub enum EXCEPTION_DISPOSITION { + ExceptionContinueExecution, + ExceptionContinueSearch, + ExceptionNestedException, + ExceptionCollidedUnwind +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct CONSOLE_READCONSOLE_CONTROL { + pub nLength: ULONG, + pub nInitialChars: ULONG, + pub dwCtrlWakeupMask: ULONG, + pub dwControlKeyState: ULONG, +} +pub type PCONSOLE_READCONSOLE_CONTROL = *mut CONSOLE_READCONSOLE_CONTROL; + +#[repr(C)] +#[derive(Copy)] +pub struct fd_set { + pub fd_count: c_uint, + pub fd_array: [SOCKET; FD_SETSIZE], +} + +impl Clone for fd_set { + fn clone(&self) -> fd_set { + *self + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct timeval { + pub tv_sec: c_long, + pub tv_usec: c_long, +} + +extern "system" { + pub fn WSAStartup(wVersionRequested: WORD, + lpWSAData: LPWSADATA) -> c_int; + pub fn WSACleanup() -> c_int; + pub fn WSAGetLastError() -> c_int; + pub fn WSADuplicateSocketW(s: SOCKET, + dwProcessId: DWORD, + lpProtocolInfo: LPWSAPROTOCOL_INFO) + -> c_int; + pub fn GetCurrentProcessId() -> DWORD; + pub fn WSASocketW(af: c_int, + kind: c_int, + protocol: c_int, + lpProtocolInfo: LPWSAPROTOCOL_INFO, + g: GROUP, + dwFlags: DWORD) -> SOCKET; + pub fn ioctlsocket(s: SOCKET, cmd: c_long, argp: *mut c_ulong) -> c_int; + pub fn InitializeCriticalSection(CriticalSection: *mut CRITICAL_SECTION); + pub fn EnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION); + pub fn TryEnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION) -> BOOLEAN; + pub fn LeaveCriticalSection(CriticalSection: *mut CRITICAL_SECTION); + pub fn DeleteCriticalSection(CriticalSection: *mut CRITICAL_SECTION); + + pub fn ReadConsoleW(hConsoleInput: HANDLE, + lpBuffer: LPVOID, + nNumberOfCharsToRead: DWORD, + lpNumberOfCharsRead: LPDWORD, + pInputControl: PCONSOLE_READCONSOLE_CONTROL) -> BOOL; + + pub fn WriteConsoleW(hConsoleOutput: HANDLE, + lpBuffer: LPCVOID, + nNumberOfCharsToWrite: DWORD, + lpNumberOfCharsWritten: LPDWORD, + lpReserved: LPVOID) -> BOOL; + + pub fn GetConsoleMode(hConsoleHandle: HANDLE, + lpMode: LPDWORD) -> BOOL; + pub fn RemoveDirectoryW(lpPathName: LPCWSTR) -> BOOL; + pub fn SetFileAttributesW(lpFileName: LPCWSTR, + dwFileAttributes: DWORD) -> BOOL; + pub fn GetFileInformationByHandle(hFile: HANDLE, + lpFileInformation: LPBY_HANDLE_FILE_INFORMATION) + -> BOOL; + + pub fn SetLastError(dwErrCode: DWORD); + pub fn GetCommandLineW() -> *mut LPCWSTR; + pub fn LocalFree(ptr: *mut c_void); + pub fn CommandLineToArgvW(lpCmdLine: *mut LPCWSTR, + pNumArgs: *mut c_int) -> *mut *mut u16; + pub fn GetTempPathW(nBufferLength: DWORD, + lpBuffer: LPCWSTR) -> DWORD; + pub fn OpenProcessToken(ProcessHandle: HANDLE, + DesiredAccess: DWORD, + TokenHandle: *mut HANDLE) -> BOOL; + pub fn GetCurrentProcess() -> HANDLE; + pub fn GetCurrentThread() -> HANDLE; + pub fn GetStdHandle(which: DWORD) -> HANDLE; + pub fn ExitProcess(uExitCode: c_uint) -> !; + pub fn DeviceIoControl(hDevice: HANDLE, + dwIoControlCode: DWORD, + lpInBuffer: LPVOID, + nInBufferSize: DWORD, + lpOutBuffer: LPVOID, + nOutBufferSize: DWORD, + lpBytesReturned: LPDWORD, + lpOverlapped: LPOVERLAPPED) -> BOOL; + pub fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, + dwStackSize: SIZE_T, + lpStartAddress: extern "system" fn(*mut c_void) + -> DWORD, + lpParameter: LPVOID, + dwCreationFlags: DWORD, + lpThreadId: LPDWORD) -> HANDLE; + pub fn WaitForSingleObject(hHandle: HANDLE, + dwMilliseconds: DWORD) -> DWORD; + pub fn SwitchToThread() -> BOOL; + pub fn Sleep(dwMilliseconds: DWORD); + pub fn GetProcessId(handle: HANDLE) -> DWORD; + pub fn GetUserProfileDirectoryW(hToken: HANDLE, + lpProfileDir: LPWSTR, + lpcchSize: *mut DWORD) -> BOOL; + pub fn SetHandleInformation(hObject: HANDLE, + dwMask: DWORD, + dwFlags: DWORD) -> BOOL; + pub fn CopyFileExW(lpExistingFileName: LPCWSTR, + lpNewFileName: LPCWSTR, + lpProgressRoutine: LPPROGRESS_ROUTINE, + lpData: LPVOID, + pbCancel: LPBOOL, + dwCopyFlags: DWORD) -> BOOL; + pub fn AddVectoredExceptionHandler(FirstHandler: ULONG, + VectoredHandler: PVECTORED_EXCEPTION_HANDLER) + -> LPVOID; + pub fn FormatMessageW(flags: DWORD, + lpSrc: LPVOID, + msgId: DWORD, + langId: DWORD, + buf: LPWSTR, + nsize: DWORD, + args: *const c_void) + -> DWORD; + pub fn TlsAlloc() -> DWORD; + pub fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID; + pub fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL; + pub fn GetLastError() -> DWORD; + pub fn QueryPerformanceFrequency(lpFrequency: *mut LARGE_INTEGER) -> BOOL; + pub fn QueryPerformanceCounter(lpPerformanceCount: *mut LARGE_INTEGER) + -> BOOL; + pub fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: LPDWORD) -> BOOL; + pub fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) -> BOOL; + pub fn CreateProcessW(lpApplicationName: LPCWSTR, + lpCommandLine: LPWSTR, + lpProcessAttributes: LPSECURITY_ATTRIBUTES, + lpThreadAttributes: LPSECURITY_ATTRIBUTES, + bInheritHandles: BOOL, + dwCreationFlags: DWORD, + lpEnvironment: LPVOID, + lpCurrentDirectory: LPCWSTR, + lpStartupInfo: LPSTARTUPINFO, + lpProcessInformation: LPPROCESS_INFORMATION) + -> BOOL; + pub fn GetEnvironmentVariableW(n: LPCWSTR, v: LPWSTR, nsize: DWORD) -> DWORD; + pub fn SetEnvironmentVariableW(n: LPCWSTR, v: LPCWSTR) -> BOOL; + pub fn GetEnvironmentStringsW() -> LPWCH; + pub fn FreeEnvironmentStringsW(env_ptr: LPWCH) -> BOOL; + pub fn GetModuleFileNameW(hModule: HMODULE, + lpFilename: LPWSTR, + nSize: DWORD) + -> DWORD; + pub fn CreateDirectoryW(lpPathName: LPCWSTR, + lpSecurityAttributes: LPSECURITY_ATTRIBUTES) + -> BOOL; + pub fn DeleteFileW(lpPathName: LPCWSTR) -> BOOL; + pub fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: LPWSTR) -> DWORD; + pub fn SetCurrentDirectoryW(lpPathName: LPCWSTR) -> BOOL; + pub fn WideCharToMultiByte(CodePage: UINT, + dwFlags: DWORD, + lpWideCharStr: LPCWSTR, + cchWideChar: c_int, + lpMultiByteStr: LPSTR, + cbMultiByte: c_int, + lpDefaultChar: LPCSTR, + lpUsedDefaultChar: LPBOOL) -> c_int; + + pub fn closesocket(socket: SOCKET) -> c_int; + pub fn recv(socket: SOCKET, buf: *mut c_void, len: c_int, + flags: c_int) -> c_int; + pub fn send(socket: SOCKET, buf: *const c_void, len: c_int, + flags: c_int) -> c_int; + pub fn recvfrom(socket: SOCKET, + buf: *mut c_void, + len: c_int, + flags: c_int, + addr: *mut SOCKADDR, + addrlen: *mut c_int) + -> c_int; + pub fn sendto(socket: SOCKET, + buf: *const c_void, + len: c_int, + flags: c_int, + addr: *const SOCKADDR, + addrlen: c_int) + -> c_int; + pub fn shutdown(socket: SOCKET, how: c_int) -> c_int; + pub fn accept(socket: SOCKET, + address: *mut SOCKADDR, + address_len: *mut c_int) + -> SOCKET; + pub fn DuplicateHandle(hSourceProcessHandle: HANDLE, + hSourceHandle: HANDLE, + hTargetProcessHandle: HANDLE, + lpTargetHandle: LPHANDLE, + dwDesiredAccess: DWORD, + bInheritHandle: BOOL, + dwOptions: DWORD) + -> BOOL; + pub fn ReadFile(hFile: HANDLE, + lpBuffer: LPVOID, + nNumberOfBytesToRead: DWORD, + lpNumberOfBytesRead: LPDWORD, + lpOverlapped: LPOVERLAPPED) + -> BOOL; + pub fn WriteFile(hFile: HANDLE, + lpBuffer: LPVOID, + nNumberOfBytesToWrite: DWORD, + lpNumberOfBytesWritten: LPDWORD, + lpOverlapped: LPOVERLAPPED) + -> BOOL; + pub fn CloseHandle(hObject: HANDLE) -> BOOL; + pub fn CreateHardLinkW(lpSymlinkFileName: LPCWSTR, + lpTargetFileName: LPCWSTR, + lpSecurityAttributes: LPSECURITY_ATTRIBUTES) + -> BOOL; + pub fn MoveFileExW(lpExistingFileName: LPCWSTR, + lpNewFileName: LPCWSTR, + dwFlags: DWORD) + -> BOOL; + pub fn SetFilePointerEx(hFile: HANDLE, + liDistanceToMove: LARGE_INTEGER, + lpNewFilePointer: PLARGE_INTEGER, + dwMoveMethod: DWORD) + -> BOOL; + pub fn FlushFileBuffers(hFile: HANDLE) -> BOOL; + pub fn CreateFileW(lpFileName: LPCWSTR, + dwDesiredAccess: DWORD, + dwShareMode: DWORD, + lpSecurityAttributes: LPSECURITY_ATTRIBUTES, + dwCreationDisposition: DWORD, + dwFlagsAndAttributes: DWORD, + hTemplateFile: HANDLE) + -> HANDLE; + + pub fn FindFirstFileW(fileName: LPCWSTR, + findFileData: LPWIN32_FIND_DATAW) + -> HANDLE; + pub fn FindNextFileW(findFile: HANDLE, findFileData: LPWIN32_FIND_DATAW) + -> BOOL; + pub fn FindClose(findFile: HANDLE) -> BOOL; + #[cfg(feature = "backtrace")] + pub fn RtlCaptureContext(ctx: *mut CONTEXT); + pub fn getsockopt(s: SOCKET, + level: c_int, + optname: c_int, + optval: *mut c_char, + optlen: *mut c_int) + -> c_int; + pub fn setsockopt(s: SOCKET, + level: c_int, + optname: c_int, + optval: *const c_void, + optlen: c_int) + -> c_int; + pub fn getsockname(socket: SOCKET, + address: *mut SOCKADDR, + address_len: *mut c_int) + -> c_int; + pub fn getpeername(socket: SOCKET, + address: *mut SOCKADDR, + address_len: *mut c_int) + -> c_int; + pub fn bind(socket: SOCKET, address: *const SOCKADDR, + address_len: socklen_t) -> c_int; + pub fn listen(socket: SOCKET, backlog: c_int) -> c_int; + pub fn connect(socket: SOCKET, address: *const SOCKADDR, len: c_int) + -> c_int; + pub fn getaddrinfo(node: *const c_char, service: *const c_char, + hints: *const ADDRINFOA, + res: *mut *mut ADDRINFOA) -> c_int; + pub fn freeaddrinfo(res: *mut ADDRINFOA); + + #[cfg(feature = "backtrace")] + pub fn LoadLibraryW(name: LPCWSTR) -> HMODULE; + #[cfg(feature = "backtrace")] + pub fn FreeLibrary(handle: HMODULE) -> BOOL; + pub fn GetProcAddress(handle: HMODULE, + name: LPCSTR) -> *mut c_void; + pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; + + pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME); + + pub fn CreateEventW(lpEventAttributes: LPSECURITY_ATTRIBUTES, + bManualReset: BOOL, + bInitialState: BOOL, + lpName: LPCWSTR) -> HANDLE; + pub fn WaitForMultipleObjects(nCount: DWORD, + lpHandles: *const HANDLE, + bWaitAll: BOOL, + dwMilliseconds: DWORD) -> DWORD; + pub fn CreateNamedPipeW(lpName: LPCWSTR, + dwOpenMode: DWORD, + dwPipeMode: DWORD, + nMaxInstances: DWORD, + nOutBufferSize: DWORD, + nInBufferSize: DWORD, + nDefaultTimeOut: DWORD, + lpSecurityAttributes: LPSECURITY_ATTRIBUTES) + -> HANDLE; + pub fn CancelIo(handle: HANDLE) -> BOOL; + pub fn GetOverlappedResult(hFile: HANDLE, + lpOverlapped: LPOVERLAPPED, + lpNumberOfBytesTransferred: LPDWORD, + bWait: BOOL) -> BOOL; + pub fn select(nfds: c_int, + readfds: *mut fd_set, + writefds: *mut fd_set, + exceptfds: *mut fd_set, + timeout: *const timeval) -> c_int; + + #[link_name = "SystemFunction036"] + pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: ULONG) -> BOOLEAN; +} + +// Functions that aren't available on every version of Windows that we support, +// but we still use them and just provide some form of a fallback implementation. +compat_fn! { + kernel32: + + pub fn CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR, + _lpTargetFileName: LPCWSTR, + _dwFlags: DWORD) -> BOOLEAN { + SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 + } + pub fn GetFinalPathNameByHandleW(_hFile: HANDLE, + _lpszFilePath: LPCWSTR, + _cchFilePath: DWORD, + _dwFlags: DWORD) -> DWORD { + SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 + } + pub fn SetThreadStackGuarantee(_size: *mut c_ulong) -> BOOL { + SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 + } + pub fn SetThreadDescription(hThread: HANDLE, + lpThreadDescription: LPCWSTR) -> HRESULT { + SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); E_NOTIMPL + } + pub fn SetFileInformationByHandle(_hFile: HANDLE, + _FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, + _lpFileInformation: LPVOID, + _dwBufferSize: DWORD) -> BOOL { + SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 + } + pub fn SleepConditionVariableSRW(ConditionVariable: PCONDITION_VARIABLE, + SRWLock: PSRWLOCK, + dwMilliseconds: DWORD, + Flags: ULONG) -> BOOL { + panic!("condition variables not available") + } + pub fn WakeConditionVariable(ConditionVariable: PCONDITION_VARIABLE) + -> () { + panic!("condition variables not available") + } + pub fn WakeAllConditionVariable(ConditionVariable: PCONDITION_VARIABLE) + -> () { + panic!("condition variables not available") + } + pub fn AcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> () { + panic!("rwlocks not available") + } + pub fn AcquireSRWLockShared(SRWLock: PSRWLOCK) -> () { + panic!("rwlocks not available") + } + pub fn ReleaseSRWLockExclusive(SRWLock: PSRWLOCK) -> () { + panic!("rwlocks not available") + } + pub fn ReleaseSRWLockShared(SRWLock: PSRWLOCK) -> () { + panic!("rwlocks not available") + } + pub fn TryAcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> BOOLEAN { + panic!("rwlocks not available") + } + pub fn TryAcquireSRWLockShared(SRWLock: PSRWLOCK) -> BOOLEAN { + panic!("rwlocks not available") + } +} + +#[cfg(all(target_env = "gnu", feature = "backtrace"))] +mod gnu { + use super::*; + + pub const PROCESS_QUERY_INFORMATION: DWORD = 0x0400; + + pub const CP_ACP: UINT = 0; + + pub const WC_NO_BEST_FIT_CHARS: DWORD = 0x00000400; + + extern "system" { + pub fn OpenProcess(dwDesiredAccess: DWORD, + bInheritHandle: BOOL, + dwProcessId: DWORD) -> HANDLE; + } + + compat_fn! { + kernel32: + + pub fn QueryFullProcessImageNameW(_hProcess: HANDLE, + _dwFlags: DWORD, + _lpExeName: LPWSTR, + _lpdwSize: LPDWORD) -> BOOL { + SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 + } + } +} + +#[cfg(all(target_env = "gnu", feature = "backtrace"))] +pub use self::gnu::*; diff --git a/ctr-std/src/sys/windows/cmath.rs b/ctr-std/src/sys/windows/cmath.rs new file mode 100644 index 0000000..b665a2c --- /dev/null +++ b/ctr-std/src/sys/windows/cmath.rs @@ -0,0 +1,103 @@ +// Copyright 2017 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg(not(test))] + +use libc::{c_float, c_double}; + +#[link_name = "m"] +extern { + pub fn acos(n: c_double) -> c_double; + pub fn asin(n: c_double) -> c_double; + pub fn atan(n: c_double) -> c_double; + pub fn atan2(a: c_double, b: c_double) -> c_double; + pub fn cbrt(n: c_double) -> c_double; + pub fn cbrtf(n: c_float) -> c_float; + pub fn cosh(n: c_double) -> c_double; + pub fn expm1(n: c_double) -> c_double; + pub fn expm1f(n: c_float) -> c_float; + pub fn fdim(a: c_double, b: c_double) -> c_double; + pub fn fdimf(a: c_float, b: c_float) -> c_float; + #[cfg_attr(target_env = "msvc", link_name = "_hypot")] + pub fn hypot(x: c_double, y: c_double) -> c_double; + #[cfg_attr(target_env = "msvc", link_name = "_hypotf")] + pub fn hypotf(x: c_float, y: c_float) -> c_float; + pub fn log1p(n: c_double) -> c_double; + pub fn log1pf(n: c_float) -> c_float; + pub fn sinh(n: c_double) -> c_double; + pub fn tan(n: c_double) -> c_double; + pub fn tanh(n: c_double) -> c_double; +} + +pub use self::shims::*; + +#[cfg(not(target_env = "msvc"))] +mod shims { + use libc::c_float; + + extern { + pub fn acosf(n: c_float) -> c_float; + pub fn asinf(n: c_float) -> c_float; + pub fn atan2f(a: c_float, b: c_float) -> c_float; + pub fn atanf(n: c_float) -> c_float; + pub fn coshf(n: c_float) -> c_float; + pub fn sinhf(n: c_float) -> c_float; + pub fn tanf(n: c_float) -> c_float; + pub fn tanhf(n: c_float) -> c_float; + } +} + +// On MSVC these functions aren't defined, so we just define shims which promote +// everything fo f64, perform the calculation, and then demote back to f32. +// While not precisely correct should be "correct enough" for now. +#[cfg(target_env = "msvc")] +mod shims { + use libc::c_float; + + #[inline] + pub unsafe fn acosf(n: c_float) -> c_float { + f64::acos(n as f64) as c_float + } + + #[inline] + pub unsafe fn asinf(n: c_float) -> c_float { + f64::asin(n as f64) as c_float + } + + #[inline] + pub unsafe fn atan2f(n: c_float, b: c_float) -> c_float { + f64::atan2(n as f64, b as f64) as c_float + } + + #[inline] + pub unsafe fn atanf(n: c_float) -> c_float { + f64::atan(n as f64) as c_float + } + + #[inline] + pub unsafe fn coshf(n: c_float) -> c_float { + f64::cosh(n as f64) as c_float + } + + #[inline] + pub unsafe fn sinhf(n: c_float) -> c_float { + f64::sinh(n as f64) as c_float + } + + #[inline] + pub unsafe fn tanf(n: c_float) -> c_float { + f64::tan(n as f64) as c_float + } + + #[inline] + pub unsafe fn tanhf(n: c_float) -> c_float { + f64::tanh(n as f64) as c_float + } +} diff --git a/ctr-std/src/sys/windows/compat.rs b/ctr-std/src/sys/windows/compat.rs new file mode 100644 index 0000000..cd42b7d --- /dev/null +++ b/ctr-std/src/sys/windows/compat.rs @@ -0,0 +1,81 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A "compatibility layer" for spanning XP and Windows 7 +//! +//! The standard library currently binds many functions that are not available +//! on Windows XP, but we would also like to support building executables that +//! run on XP. To do this we specify all non-XP APIs as having a fallback +//! implementation to do something reasonable. +//! +//! This dynamic runtime detection of whether a function is available is +//! implemented with `GetModuleHandle` and `GetProcAddress` paired with a +//! static-per-function which caches the result of the first check. In this +//! manner we pay a semi-large one-time cost up front for detecting whether a +//! function is available but afterwards it's just a load and a jump. + +use ffi::CString; +use sync::atomic::{AtomicUsize, Ordering}; +use sys::c; + +pub fn lookup(module: &str, symbol: &str) -> Option<usize> { + let mut module: Vec<u16> = module.encode_utf16().collect(); + module.push(0); + let symbol = CString::new(symbol).unwrap(); + unsafe { + let handle = c::GetModuleHandleW(module.as_ptr()); + match c::GetProcAddress(handle, symbol.as_ptr()) as usize { + 0 => None, + n => Some(n), + } + } +} + +pub fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str, + fallback: usize) -> usize { + let value = lookup(module, symbol).unwrap_or(fallback); + ptr.store(value, Ordering::SeqCst); + value +} + +macro_rules! compat_fn { + ($module:ident: $( + pub fn $symbol:ident($($argname:ident: $argtype:ty),*) + -> $rettype:ty { + $($body:expr);* + } + )*) => ($( + #[allow(unused_variables)] + pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype { + use sync::atomic::{AtomicUsize, Ordering}; + use mem; + type F = unsafe extern "system" fn($($argtype),*) -> $rettype; + + static PTR: AtomicUsize = AtomicUsize::new(0); + + fn load() -> usize { + ::sys::compat::store_func(&PTR, + stringify!($module), + stringify!($symbol), + fallback as usize) + } + unsafe extern "system" fn fallback($($argname: $argtype),*) + -> $rettype { + $($body);* + } + + let addr = match PTR.load(Ordering::SeqCst) { + 0 => load(), + n => n, + }; + mem::transmute::<usize, F>(addr)($($argname),*) + } + )*) +} diff --git a/ctr-std/src/sys/windows/condvar.rs b/ctr-std/src/sys/windows/condvar.rs new file mode 100644 index 0000000..d708b32 --- /dev/null +++ b/ctr-std/src/sys/windows/condvar.rs @@ -0,0 +1,65 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cell::UnsafeCell; +use sys::c; +use sys::mutex::{self, Mutex}; +use sys::os; +use time::Duration; + +pub struct Condvar { inner: UnsafeCell<c::CONDITION_VARIABLE> } + +unsafe impl Send for Condvar {} +unsafe impl Sync for Condvar {} + +impl Condvar { + pub const fn new() -> Condvar { + Condvar { inner: UnsafeCell::new(c::CONDITION_VARIABLE_INIT) } + } + + #[inline] + pub unsafe fn init(&mut self) {} + + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { + let r = c::SleepConditionVariableSRW(self.inner.get(), + mutex::raw(mutex), + c::INFINITE, + 0); + debug_assert!(r != 0); + } + + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + let r = c::SleepConditionVariableSRW(self.inner.get(), + mutex::raw(mutex), + super::dur2timeout(dur), + 0); + if r == 0 { + debug_assert_eq!(os::errno() as usize, c::ERROR_TIMEOUT as usize); + false + } else { + true + } + } + + #[inline] + pub unsafe fn notify_one(&self) { + c::WakeConditionVariable(self.inner.get()) + } + + #[inline] + pub unsafe fn notify_all(&self) { + c::WakeAllConditionVariable(self.inner.get()) + } + + pub unsafe fn destroy(&self) { + // ... + } +} diff --git a/ctr-std/src/sys/windows/dynamic_lib.rs b/ctr-std/src/sys/windows/dynamic_lib.rs new file mode 100644 index 0000000..5227280 --- /dev/null +++ b/ctr-std/src/sys/windows/dynamic_lib.rs @@ -0,0 +1,54 @@ +// Copyright 2016 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use os::windows::prelude::*; + +use ffi::{CString, OsStr}; +use io; +use sys::c; + +pub struct DynamicLibrary { + handle: c::HMODULE, +} + +impl DynamicLibrary { + pub fn open(filename: &str) -> io::Result<DynamicLibrary> { + let filename = OsStr::new(filename) + .encode_wide() + .chain(Some(0)) + .collect::<Vec<_>>(); + let result = unsafe { + c::LoadLibraryW(filename.as_ptr()) + }; + if result.is_null() { + Err(io::Error::last_os_error()) + } else { + Ok(DynamicLibrary { handle: result }) + } + } + + pub fn symbol(&self, symbol: &str) -> io::Result<usize> { + let symbol = CString::new(symbol)?; + unsafe { + match c::GetProcAddress(self.handle, symbol.as_ptr()) as usize { + 0 => Err(io::Error::last_os_error()), + n => Ok(n), + } + } + } +} + +impl Drop for DynamicLibrary { + fn drop(&mut self) { + unsafe { + c::FreeLibrary(self.handle); + } + } +} diff --git a/ctr-std/src/sys/windows/env.rs b/ctr-std/src/sys/windows/env.rs new file mode 100644 index 0000000..e6d7489 --- /dev/null +++ b/ctr-std/src/sys/windows/env.rs @@ -0,0 +1,19 @@ +// Copyright 2012-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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub mod os { + pub const FAMILY: &'static str = "windows"; + pub const OS: &'static str = "windows"; + pub const DLL_PREFIX: &'static str = ""; + pub const DLL_SUFFIX: &'static str = ".dll"; + pub const DLL_EXTENSION: &'static str = "dll"; + pub const EXE_SUFFIX: &'static str = ".exe"; + pub const EXE_EXTENSION: &'static str = "exe"; +} diff --git a/ctr-std/src/sys/windows/ext/ffi.rs b/ctr-std/src/sys/windows/ext/ffi.rs new file mode 100644 index 0000000..98d4355 --- /dev/null +++ b/ctr-std/src/sys/windows/ext/ffi.rs @@ -0,0 +1,151 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Windows-specific extensions to the primitives in the `std::ffi` module. +//! +//! # Overview +//! +//! For historical reasons, the Windows API uses a form of potentially +//! ill-formed UTF-16 encoding for strings. Specifically, the 16-bit +//! code units in Windows strings may contain [isolated surrogate code +//! points which are not paired together][ill-formed-utf-16]. The +//! Unicode standard requires that surrogate code points (those in the +//! range U+D800 to U+DFFF) always be *paired*, because in the UTF-16 +//! encoding a *surrogate code unit pair* is used to encode a single +//! character. For compatibility with code that does not enforce +//! these pairings, Windows does not enforce them, either. +//! +//! While it is not always possible to convert such a string losslessly into +//! a valid UTF-16 string (or even UTF-8), it is often desirable to be +//! able to round-trip such a string from and to Windows APIs +//! losslessly. For example, some Rust code may be "bridging" some +//! Windows APIs together, just passing `WCHAR` strings among those +//! APIs without ever really looking into the strings. +//! +//! If Rust code *does* need to look into those strings, it can +//! convert them to valid UTF-8, possibly lossily, by substituting +//! invalid sequences with U+FFFD REPLACEMENT CHARACTER, as is +//! conventionally done in other Rust APIs that deal with string +//! encodings. +//! +//! # `OsStringExt` and `OsStrExt` +//! +//! [`OsString`] is the Rust wrapper for owned strings in the +//! preferred representation of the operating system. On Windows, +//! this struct gets augmented with an implementation of the +//! [`OsStringExt`] trait, which has a [`from_wide`] method. This +//! lets you create an [`OsString`] from a `&[u16]` slice; presumably +//! you get such a slice out of a `WCHAR` Windows API. +//! +//! Similarly, [`OsStr`] is the Rust wrapper for borrowed strings from +//! preferred representation of the operating system. On Windows, the +//! [`OsStrExt`] trait provides the [`encode_wide`] method, which +//! outputs an [`EncodeWide`] iterator. You can [`collect`] this +//! iterator, for example, to obtain a `Vec<u16>`; you can later get a +//! pointer to this vector's contents and feed it to Windows APIs. +//! +//! These traits, along with [`OsString`] and [`OsStr`], work in +//! conjunction so that it is possible to **round-trip** strings from +//! Windows and back, with no loss of data, even if the strings are +//! ill-formed UTF-16. +//! +//! [ill-formed-utf-16]: https://simonsapin.github.io/wtf-8/#ill-formed-utf-16 +//! [`OsString`]: ../../../ffi/struct.OsString.html +//! [`OsStr`]: ../../../ffi/struct.OsStr.html +//! [`OsStringExt`]: trait.OsStringExt.html +//! [`OsStrExt`]: trait.OsStrExt.html +//! [`EncodeWide`]: struct.EncodeWide.html +//! [`from_wide`]: trait.OsStringExt.html#tymethod.from_wide +//! [`encode_wide`]: trait.OsStrExt.html#tymethod.encode_wide +//! [`collect`]: ../../../iter/trait.Iterator.html#method.collect + +#![stable(feature = "rust1", since = "1.0.0")] + +use ffi::{OsString, OsStr}; +use sys::os_str::Buf; +use sys_common::wtf8::Wtf8Buf; +use sys_common::{FromInner, AsInner}; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use sys_common::wtf8::EncodeWide; + +/// Windows-specific extensions to [`OsString`]. +/// +/// [`OsString`]: ../../../../std/ffi/struct.OsString.html +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStringExt { + /// Creates an `OsString` from a potentially ill-formed UTF-16 slice of + /// 16-bit code units. + /// + /// This is lossless: calling [`encode_wide`] on the resulting string + /// will always return the original code units. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsString; + /// use std::os::windows::prelude::*; + /// + /// // UTF-16 encoding for "Unicode". + /// let source = [0x0055, 0x006E, 0x0069, 0x0063, 0x006F, 0x0064, 0x0065]; + /// + /// let string = OsString::from_wide(&source[..]); + /// ``` + /// + /// [`encode_wide`]: ./trait.OsStrExt.html#tymethod.encode_wide + #[stable(feature = "rust1", since = "1.0.0")] + fn from_wide(wide: &[u16]) -> Self; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStringExt for OsString { + fn from_wide(wide: &[u16]) -> OsString { + FromInner::from_inner(Buf { inner: Wtf8Buf::from_wide(wide) }) + } +} + +/// Windows-specific extensions to [`OsStr`]. +/// +/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStrExt { + /// Re-encodes an `OsStr` as a wide character sequence, i.e. potentially + /// ill-formed UTF-16. + /// + /// This is lossless: calling [`OsString::from_wide`] and then + /// `encode_wide` on the result will yield the original code units. + /// Note that the encoding does not add a final null terminator. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsString; + /// use std::os::windows::prelude::*; + /// + /// // UTF-16 encoding for "Unicode". + /// let source = [0x0055, 0x006E, 0x0069, 0x0063, 0x006F, 0x0064, 0x0065]; + /// + /// let string = OsString::from_wide(&source[..]); + /// + /// let result: Vec<u16> = string.encode_wide().collect(); + /// assert_eq!(&source[..], &result[..]); + /// ``` + /// + /// [`OsString::from_wide`]: ./trait.OsStringExt.html#tymethod.from_wide + #[stable(feature = "rust1", since = "1.0.0")] + fn encode_wide(&self) -> EncodeWide; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStrExt for OsStr { + fn encode_wide(&self) -> EncodeWide { + self.as_inner().inner.encode_wide() + } +} diff --git a/ctr-std/src/sys/windows/ext/fs.rs b/ctr-std/src/sys/windows/ext/fs.rs new file mode 100644 index 0000000..78c9e95 --- /dev/null +++ b/ctr-std/src/sys/windows/ext/fs.rs @@ -0,0 +1,508 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Windows-specific extensions for the primitives in the `std::fs` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use fs::{self, OpenOptions, Metadata}; +use io; +use path::Path; +use sys; +use sys_common::{AsInnerMut, AsInner}; + +/// Windows-specific extensions to [`File`]. +/// +/// [`File`]: ../../../fs/struct.File.html +#[stable(feature = "file_offset", since = "1.15.0")] +pub trait FileExt { + /// Seeks to a given position and reads a number of bytes. + /// + /// Returns the number of bytes read. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. The current cursor **is** affected by this + /// function, it is set to the end of the read. + /// + /// Reading beyond the end of the file will always return with a length of + /// 0\. + /// + /// Note that similar to `File::read`, it is not an error to return with a + /// short read. When returning from such a short read, the file pointer is + /// still updated. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs::File; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let mut file = File::open("foo.txt")?; + /// let mut buffer = [0; 10]; + /// + /// // Read 10 bytes, starting 72 bytes from the + /// // start of the file. + /// file.seek_read(&mut buffer[..], 72)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_offset", since = "1.15.0")] + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>; + + /// Seeks to a given position and writes a number of bytes. + /// + /// Returns the number of bytes written. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. The current cursor **is** affected by this + /// function, it is set to the end of the write. + /// + /// When writing beyond the end of the file, the file is appropriately + /// extended and the intermediate bytes are left uninitialized. + /// + /// Note that similar to `File::write`, it is not an error to return a + /// short write. When returning from such a short write, the file pointer + /// is still updated. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // Write a byte string starting 72 bytes from + /// // the start of the file. + /// buffer.seek_write(b"some bytes", 72)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_offset", since = "1.15.0")] + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize>; +} + +#[stable(feature = "file_offset", since = "1.15.0")] +impl FileExt for fs::File { + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { + self.as_inner().read_at(buf, offset) + } + + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + self.as_inner().write_at(buf, offset) + } +} + +/// Windows-specific extensions to [`fs::OpenOptions`]. +/// +/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html +#[stable(feature = "open_options_ext", since = "1.10.0")] +pub trait OpenOptionsExt { + /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`] + /// with the specified value. + /// + /// This will override the `read`, `write`, and `append` flags on the + /// `OpenOptions` structure. This method provides fine-grained control over + /// the permissions to read, write and append data, attributes (like hidden + /// and system), and extended attributes. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// use std::os::windows::prelude::*; + /// + /// // Open without read and write permission, for example if you only need + /// // to call `stat` on the file + /// let file = OpenOptions::new().access_mode(0).open("foo.txt"); + /// ``` + /// + /// [`CreateFile`]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858.aspx + #[stable(feature = "open_options_ext", since = "1.10.0")] + fn access_mode(&mut self, access: u32) -> &mut Self; + + /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with + /// the specified value. + /// + /// By default `share_mode` is set to + /// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows + /// other processes to to read, write, and delete/rename the same file + /// while it is open. Removing any of the flags will prevent other + /// processes from performing the corresponding operation until the file + /// handle is closed. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// use std::os::windows::prelude::*; + /// + /// // Do not allow others to read or modify this file while we have it open + /// // for writing. + /// let file = OpenOptions::new() + /// .write(true) + /// .share_mode(0) + /// .open("foo.txt"); + /// ``` + /// + /// [`CreateFile`]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858.aspx + #[stable(feature = "open_options_ext", since = "1.10.0")] + fn share_mode(&mut self, val: u32) -> &mut Self; + + /// Sets extra flags for the `dwFileFlags` argument to the call to + /// [`CreateFile2`] to the specified value (or combines it with + /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes` + /// for [`CreateFile`]). + /// + /// Custom flags can only set flags, not remove flags set by Rust's options. + /// This option overwrites any previously set custom flags. + /// + /// # Examples + /// + /// ```no_run + /// # #[cfg(for_demonstration_only)] + /// extern crate winapi; + /// # mod winapi { pub const FILE_FLAG_DELETE_ON_CLOSE: u32 = 0x04000000; } + /// + /// use std::fs::OpenOptions; + /// use std::os::windows::prelude::*; + /// + /// let file = OpenOptions::new() + /// .create(true) + /// .write(true) + /// .custom_flags(winapi::FILE_FLAG_DELETE_ON_CLOSE) + /// .open("foo.txt"); + /// ``` + /// + /// [`CreateFile`]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858.aspx + /// [`CreateFile2`]: https://msdn.microsoft.com/en-us/library/windows/desktop/hh449422.aspx + #[stable(feature = "open_options_ext", since = "1.10.0")] + fn custom_flags(&mut self, flags: u32) -> &mut Self; + + /// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to + /// the specified value (or combines it with `custom_flags` and + /// `security_qos_flags` to set the `dwFlagsAndAttributes` for + /// [`CreateFile`]). + /// + /// If a _new_ file is created because it does not yet exist and + /// `.create(true)` or `.create_new(true)` are specified, the new file is + /// given the attributes declared with `.attributes()`. + /// + /// If an _existing_ file is opened with `.create(true).truncate(true)`, its + /// existing attributes are preserved and combined with the ones declared + /// with `.attributes()`. + /// + /// In all other cases the attributes get ignored. + /// + /// # Examples + /// + /// ```no_run + /// # #[cfg(for_demonstration_only)] + /// extern crate winapi; + /// # mod winapi { pub const FILE_ATTRIBUTE_HIDDEN: u32 = 2; } + /// + /// use std::fs::OpenOptions; + /// use std::os::windows::prelude::*; + /// + /// let file = OpenOptions::new() + /// .write(true) + /// .create(true) + /// .attributes(winapi::FILE_ATTRIBUTE_HIDDEN) + /// .open("foo.txt"); + /// ``` + /// + /// [`CreateFile`]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858.aspx + /// [`CreateFile2`]: https://msdn.microsoft.com/en-us/library/windows/desktop/hh449422.aspx + #[stable(feature = "open_options_ext", since = "1.10.0")] + fn attributes(&mut self, val: u32) -> &mut Self; + + /// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to + /// the specified value (or combines it with `custom_flags` and `attributes` + /// to set the `dwFlagsAndAttributes` for [`CreateFile`]). + /// + /// By default, `security_qos_flags` is set to `SECURITY_ANONYMOUS`. For + /// information about possible values, see [Impersonation Levels] on the + /// Windows Dev Center site. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// use std::os::windows::prelude::*; + /// + /// let file = OpenOptions::new() + /// .write(true) + /// .create(true) + /// + /// // Sets the flag value to `SecurityIdentification`. + /// .security_qos_flags(1) + /// + /// .open("foo.txt"); + /// ``` + /// + /// [`CreateFile`]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858.aspx + /// [`CreateFile2`]: https://msdn.microsoft.com/en-us/library/windows/desktop/hh449422.aspx + /// [Impersonation Levels]: + /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa379572.aspx + #[stable(feature = "open_options_ext", since = "1.10.0")] + fn security_qos_flags(&mut self, flags: u32) -> &mut OpenOptions; +} + +#[stable(feature = "open_options_ext", since = "1.10.0")] +impl OpenOptionsExt for OpenOptions { + fn access_mode(&mut self, access: u32) -> &mut OpenOptions { + self.as_inner_mut().access_mode(access); self + } + + fn share_mode(&mut self, share: u32) -> &mut OpenOptions { + self.as_inner_mut().share_mode(share); self + } + + fn custom_flags(&mut self, flags: u32) -> &mut OpenOptions { + self.as_inner_mut().custom_flags(flags); self + } + + fn attributes(&mut self, attributes: u32) -> &mut OpenOptions { + self.as_inner_mut().attributes(attributes); self + } + + fn security_qos_flags(&mut self, flags: u32) -> &mut OpenOptions { + self.as_inner_mut().security_qos_flags(flags); self + } +} + +/// Windows-specific extensions to [`fs::Metadata`]. +/// +/// The data members that this trait exposes correspond to the members +/// of the [`BY_HANDLE_FILE_INFORMATION`] structure. +/// +/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html +/// [`BY_HANDLE_FILE_INFORMATION`]: +/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363788.aspx +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Returns the value of the `dwFileAttributes` field of this metadata. + /// + /// This field contains the file system attribute information for a file + /// or directory. For possible values and their descriptions, see + /// [File Attribute Constants] in the Windows Dev Center. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// let attributes = metadata.file_attributes(); + /// Ok(()) + /// } + /// ``` + /// + /// [File Attribute Constants]: + /// https://msdn.microsoft.com/en-us/library/windows/desktop/gg258117.aspx + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn file_attributes(&self) -> u32; + + /// Returns the value of the `ftCreationTime` field of this metadata. + /// + /// The returned 64-bit value is equivalent to a [`FILETIME`] struct, + /// which represents the number of 100-nanosecond intervals since + /// January 1, 1601 (UTC). The struct is automatically + /// converted to a `u64` value, as that is the recommended way + /// to use it. + /// + /// If the underlying filesystem does not support creation time, the + /// returned value is 0. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// let creation_time = metadata.creation_time(); + /// Ok(()) + /// } + /// ``` + /// + /// [`FILETIME`]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284.aspx + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn creation_time(&self) -> u64; + + /// Returns the value of the `ftLastAccessTime` field of this metadata. + /// + /// The returned 64-bit value is equivalent to a [`FILETIME`] struct, + /// which represents the number of 100-nanosecond intervals since + /// January 1, 1601 (UTC). The struct is automatically + /// converted to a `u64` value, as that is the recommended way + /// to use it. + /// + /// For a file, the value specifies the last time that a file was read + /// from or written to. For a directory, the value specifies when + /// the directory was created. For both files and directories, the + /// specified date is correct, but the time of day is always set to + /// midnight. + /// + /// If the underlying filesystem does not support last access time, the + /// returned value is 0. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// let last_access_time = metadata.last_access_time(); + /// Ok(()) + /// } + /// ``` + /// + /// [`FILETIME`]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284.aspx + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn last_access_time(&self) -> u64; + + /// Returns the value of the `ftLastWriteTime` field of this metadata. + /// + /// The returned 64-bit value is equivalent to a [`FILETIME`] struct, + /// which represents the number of 100-nanosecond intervals since + /// January 1, 1601 (UTC). The struct is automatically + /// converted to a `u64` value, as that is the recommended way + /// to use it. + /// + /// For a file, the value specifies the last time that a file was written + /// to. For a directory, the structure specifies when the directory was + /// created. + /// + /// If the underlying filesystem does not support the last write time, + /// the returned value is 0. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// let last_write_time = metadata.last_write_time(); + /// Ok(()) + /// } + /// ``` + /// + /// [`FILETIME`]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284.aspx + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn last_write_time(&self) -> u64; + + /// Returns the value of the `nFileSize{High,Low}` fields of this + /// metadata. + /// + /// The returned value does not have meaning for directories. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// let file_size = metadata.file_size(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn file_size(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + fn file_attributes(&self) -> u32 { self.as_inner().attrs() } + fn creation_time(&self) -> u64 { self.as_inner().created_u64() } + fn last_access_time(&self) -> u64 { self.as_inner().accessed_u64() } + fn last_write_time(&self) -> u64 { self.as_inner().modified_u64() } + fn file_size(&self) -> u64 { self.as_inner().size() } +} + +/// Windows-specific extensions to [`FileType`]. +/// +/// On Windows, a symbolic link knows whether it is a file or directory. +/// +/// [`FileType`]: ../../../../std/fs/struct.FileType.html +#[unstable(feature = "windows_file_type_ext", issue = "0")] +pub trait FileTypeExt { + /// Returns whether this file type is a symbolic link that is also a directory. + #[unstable(feature = "windows_file_type_ext", issue = "0")] + fn is_symlink_dir(&self) -> bool; + /// Returns whether this file type is a symbolic link that is also a file. + #[unstable(feature = "windows_file_type_ext", issue = "0")] + fn is_symlink_file(&self) -> bool; +} + +#[unstable(feature = "windows_file_type_ext", issue = "0")] +impl FileTypeExt for fs::FileType { + fn is_symlink_dir(&self) -> bool { self.as_inner().is_symlink_dir() } + fn is_symlink_file(&self) -> bool { self.as_inner().is_symlink_file() } +} + +/// Creates a new file symbolic link on the filesystem. +/// +/// The `dst` path will be a file symbolic link pointing to the `src` +/// path. +/// +/// # Examples +/// +/// ```no_run +/// use std::os::windows::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::symlink_file("a.txt", "b.txt")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "symlink", since = "1.1.0")] +pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) + -> io::Result<()> { + sys::fs::symlink_inner(src.as_ref(), dst.as_ref(), false) +} + +/// Creates a new directory symlink on the filesystem. +/// +/// The `dst` path will be a directory symbolic link pointing to the `src` +/// path. +/// +/// # Examples +/// +/// ```no_run +/// use std::os::windows::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::symlink_dir("a", "b")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "symlink", since = "1.1.0")] +pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) + -> io::Result<()> { + sys::fs::symlink_inner(src.as_ref(), dst.as_ref(), true) +} diff --git a/ctr-std/src/sys/windows/ext/io.rs b/ctr-std/src/sys/windows/ext/io.rs new file mode 100644 index 0000000..90128dd --- /dev/null +++ b/ctr-std/src/sys/windows/ext/io.rs @@ -0,0 +1,209 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![stable(feature = "rust1", since = "1.0.0")] + +use fs; +use os::windows::raw; +use net; +use sys_common::{self, AsInner, FromInner, IntoInner}; +use sys; +use io; +use sys::c; + +/// Raw HANDLEs. +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawHandle = raw::HANDLE; + +/// Raw SOCKETs. +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawSocket = raw::SOCKET; + +/// Extract raw handles. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRawHandle { + /// Extracts the raw handle, without taking any ownership. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_raw_handle(&self) -> RawHandle; +} + +/// Construct I/O objects from raw handles. +#[stable(feature = "from_raw_os", since = "1.1.0")] +pub trait FromRawHandle { + /// Constructs a new I/O object from the specified raw handle. + /// + /// This function will **consume ownership** of the handle given, + /// passing responsibility for closing the handle to the returned + /// object. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + #[stable(feature = "from_raw_os", since = "1.1.0")] + unsafe fn from_raw_handle(handle: RawHandle) -> Self; +} + +/// A trait to express the ability to consume an object and acquire ownership of +/// its raw `HANDLE`. +#[stable(feature = "into_raw_os", since = "1.4.0")] +pub trait IntoRawHandle { + /// Consumes this object, returning the raw underlying handle. + /// + /// This function **transfers ownership** of the underlying handle to the + /// caller. Callers are then the unique owners of the handle and must close + /// it once it's no longer needed. + #[stable(feature = "into_raw_os", since = "1.4.0")] + fn into_raw_handle(self) -> RawHandle; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawHandle for fs::File { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().handle().raw() as RawHandle + } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawHandle for io::Stdin { + fn as_raw_handle(&self) -> RawHandle { + unsafe { c::GetStdHandle(c::STD_INPUT_HANDLE) as RawHandle } + } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawHandle for io::Stdout { + fn as_raw_handle(&self) -> RawHandle { + unsafe { c::GetStdHandle(c::STD_OUTPUT_HANDLE) as RawHandle } + } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawHandle for io::Stderr { + fn as_raw_handle(&self) -> RawHandle { + unsafe { c::GetStdHandle(c::STD_ERROR_HANDLE) as RawHandle } + } +} + +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawHandle for fs::File { + unsafe fn from_raw_handle(handle: RawHandle) -> fs::File { + let handle = handle as c::HANDLE; + fs::File::from_inner(sys::fs::File::from_inner(handle)) + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawHandle for fs::File { + fn into_raw_handle(self) -> RawHandle { + self.into_inner().into_handle().into_raw() as *mut _ + } +} + +/// Extract raw sockets. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRawSocket { + /// Extracts the underlying raw socket from this object. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_raw_socket(&self) -> RawSocket; +} + +/// Create I/O objects from raw sockets. +#[stable(feature = "from_raw_os", since = "1.1.0")] +pub trait FromRawSocket { + /// Creates a new I/O object from the given raw socket. + /// + /// This function will **consume ownership** of the socket provided and + /// it will be closed when the returned object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + #[stable(feature = "from_raw_os", since = "1.1.0")] + unsafe fn from_raw_socket(sock: RawSocket) -> Self; +} + +/// A trait to express the ability to consume an object and acquire ownership of +/// its raw `SOCKET`. +#[stable(feature = "into_raw_os", since = "1.4.0")] +pub trait IntoRawSocket { + /// Consumes this object, returning the raw underlying socket. + /// + /// This function **transfers ownership** of the underlying socket to the + /// caller. Callers are then the unique owners of the socket and must close + /// it once it's no longer needed. + #[stable(feature = "into_raw_os", since = "1.4.0")] + fn into_raw_socket(self) -> RawSocket; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawSocket for net::TcpStream { + fn as_raw_socket(&self) -> RawSocket { + *self.as_inner().socket().as_inner() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawSocket for net::TcpListener { + fn as_raw_socket(&self) -> RawSocket { + *self.as_inner().socket().as_inner() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawSocket for net::UdpSocket { + fn as_raw_socket(&self) -> RawSocket { + *self.as_inner().socket().as_inner() + } +} + +#[stable(feature = "from_raw_os", since = "1.1.0")] +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(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(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(sys_common::net::UdpSocket::from_inner(sock)) + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawSocket for net::TcpStream { + fn into_raw_socket(self) -> RawSocket { + self.into_inner().into_socket().into_inner() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawSocket for net::TcpListener { + fn into_raw_socket(self) -> RawSocket { + self.into_inner().into_socket().into_inner() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawSocket for net::UdpSocket { + fn into_raw_socket(self) -> RawSocket { + self.into_inner().into_socket().into_inner() + } +} diff --git a/ctr-std/src/sys/windows/ext/mod.rs b/ctr-std/src/sys/windows/ext/mod.rs new file mode 100644 index 0000000..4b458d2 --- /dev/null +++ b/ctr-std/src/sys/windows/ext/mod.rs @@ -0,0 +1,44 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Platform-specific extensions to `std` for Windows. +//! +//! Provides access to platform-level information for Windows, and exposes +//! Windows-specific idioms that would otherwise be inappropriate as part +//! the core `std` library. These extensions allow developers to use +//! `std` types and idioms with Windows in a way that the normal +//! platform-agnostic idioms would not normally support. + +#![stable(feature = "rust1", since = "1.0.0")] +#![doc(cfg(windows))] + +pub mod ffi; +pub mod fs; +pub mod io; +pub mod raw; +pub mod process; +pub mod thread; + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod prelude { + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::io::{RawSocket, RawHandle, AsRawSocket, AsRawHandle}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::io::{FromRawSocket, FromRawHandle, IntoRawSocket, IntoRawHandle}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::ffi::{OsStrExt, OsStringExt}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::fs::{OpenOptionsExt, MetadataExt}; + #[doc(no_inline)] #[stable(feature = "file_offset", since = "1.15.0")] + pub use super::fs::FileExt; +} diff --git a/ctr-std/src/sys/windows/ext/process.rs b/ctr-std/src/sys/windows/ext/process.rs new file mode 100644 index 0000000..a02bcbe --- /dev/null +++ b/ctr-std/src/sys/windows/ext/process.rs @@ -0,0 +1,123 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Extensions to `std::process` for Windows. + +#![stable(feature = "process_extensions", since = "1.2.0")] + +use os::windows::io::{FromRawHandle, RawHandle, AsRawHandle, IntoRawHandle}; +use process; +use sys; +use sys_common::{AsInnerMut, AsInner, FromInner, IntoInner}; + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl FromRawHandle for process::Stdio { + unsafe fn from_raw_handle(handle: RawHandle) -> process::Stdio { + let handle = sys::handle::Handle::new(handle as *mut _); + let io = sys::process::Stdio::Handle(handle); + process::Stdio::from_inner(io) + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawHandle for process::Child { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().handle().raw() as *mut _ + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawHandle for process::Child { + fn into_raw_handle(self) -> RawHandle { + self.into_inner().into_handle().into_raw() as *mut _ + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawHandle for process::ChildStdin { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().handle().raw() as *mut _ + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawHandle for process::ChildStdout { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().handle().raw() as *mut _ + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawHandle for process::ChildStderr { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().handle().raw() as *mut _ + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawHandle for process::ChildStdin { + fn into_raw_handle(self) -> RawHandle { + self.into_inner().into_handle().into_raw() as *mut _ + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawHandle for process::ChildStdout { + fn into_raw_handle(self) -> RawHandle { + self.into_inner().into_handle().into_raw() as *mut _ + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawHandle for process::ChildStderr { + fn into_raw_handle(self) -> RawHandle { + self.into_inner().into_handle().into_raw() as *mut _ + } +} + +/// Windows-specific extensions to [`process::ExitStatus`]. +/// +/// [`process::ExitStatus`]: ../../../../std/process/struct.ExitStatus.html +#[stable(feature = "exit_status_from", since = "1.12.0")] +pub trait ExitStatusExt { + /// Creates a new `ExitStatus` from the raw underlying `u32` return value of + /// a process. + #[stable(feature = "exit_status_from", since = "1.12.0")] + fn from_raw(raw: u32) -> Self; +} + +#[stable(feature = "exit_status_from", since = "1.12.0")] +impl ExitStatusExt for process::ExitStatus { + fn from_raw(raw: u32) -> Self { + process::ExitStatus::from_inner(From::from(raw)) + } +} + +/// Windows-specific extensions to the [`process::Command`] builder. +/// +/// [`process::Command`]: ../../../../std/process/struct.Command.html +#[stable(feature = "windows_process_extensions", since = "1.16.0")] +pub trait CommandExt { + /// Sets the [process creation flags][1] to be passed to `CreateProcess`. + /// + /// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`. + /// + /// [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx + #[stable(feature = "windows_process_extensions", since = "1.16.0")] + fn creation_flags(&mut self, flags: u32) -> &mut process::Command; +} + +#[stable(feature = "windows_process_extensions", since = "1.16.0")] +impl CommandExt for process::Command { + fn creation_flags(&mut self, flags: u32) -> &mut process::Command { + self.as_inner_mut().creation_flags(flags); + self + } +} diff --git a/ctr-std/src/sys/windows/ext/raw.rs b/ctr-std/src/sys/windows/ext/raw.rs new file mode 100644 index 0000000..92d53e2 --- /dev/null +++ b/ctr-std/src/sys/windows/ext/raw.rs @@ -0,0 +1,21 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Windows-specific primitives + +#![stable(feature = "raw_ext", since = "1.1.0")] + +use os::raw::c_void; + +#[stable(feature = "raw_ext", since = "1.1.0")] pub type HANDLE = *mut c_void; +#[cfg(target_pointer_width = "32")] +#[stable(feature = "raw_ext", since = "1.1.0")] pub type SOCKET = u32; +#[cfg(target_pointer_width = "64")] +#[stable(feature = "raw_ext", since = "1.1.0")] pub type SOCKET = u64; diff --git a/ctr-std/src/sys/windows/ext/thread.rs b/ctr-std/src/sys/windows/ext/thread.rs new file mode 100644 index 0000000..36b3a3d --- /dev/null +++ b/ctr-std/src/sys/windows/ext/thread.rs @@ -0,0 +1,31 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Extensions to `std::thread` for Windows. + +#![stable(feature = "thread_extensions", since = "1.9.0")] + +use os::windows::io::{RawHandle, AsRawHandle, IntoRawHandle}; +use thread; +use sys_common::{AsInner, IntoInner}; + +#[stable(feature = "thread_extensions", since = "1.9.0")] +impl<T> AsRawHandle for thread::JoinHandle<T> { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().handle().raw() as *mut _ + } +} + +#[stable(feature = "thread_extensions", since = "1.9.0")] +impl<T> IntoRawHandle for thread::JoinHandle<T> { + fn into_raw_handle(self) -> RawHandle { + self.into_inner().into_handle().into_raw() as *mut _ + } +} diff --git a/ctr-std/src/sys/windows/fast_thread_local.rs b/ctr-std/src/sys/windows/fast_thread_local.rs new file mode 100644 index 0000000..9fee9bd --- /dev/null +++ b/ctr-std/src/sys/windows/fast_thread_local.rs @@ -0,0 +1,18 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "thread_local_internals", issue = "0")] +#![cfg(target_thread_local)] + +pub use sys_common::thread_local::register_dtor_fallback as register_dtor; + +pub fn requires_move_before_drop() -> bool { + false +} diff --git a/ctr-std/src/sys/windows/fs.rs b/ctr-std/src/sys/windows/fs.rs new file mode 100644 index 0000000..082d468 --- /dev/null +++ b/ctr-std/src/sys/windows/fs.rs @@ -0,0 +1,804 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use os::windows::prelude::*; + +use ffi::OsString; +use fmt; +use io::{self, Error, SeekFrom}; +use mem; +use path::{Path, PathBuf}; +use ptr; +use slice; +use sync::Arc; +use sys::handle::Handle; +use sys::time::SystemTime; +use sys::{c, cvt}; +use sys_common::FromInner; + +use super::to_u16s; + +pub struct File { handle: Handle } + +#[derive(Clone)] +pub struct FileAttr { + attributes: c::DWORD, + creation_time: c::FILETIME, + last_access_time: c::FILETIME, + last_write_time: c::FILETIME, + file_size: u64, + reparse_tag: c::DWORD, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct FileType { + attributes: c::DWORD, + reparse_tag: c::DWORD, +} + +pub struct ReadDir { + handle: FindNextFileHandle, + root: Arc<PathBuf>, + first: Option<c::WIN32_FIND_DATAW>, +} + +struct FindNextFileHandle(c::HANDLE); + +unsafe impl Send for FindNextFileHandle {} +unsafe impl Sync for FindNextFileHandle {} + +pub struct DirEntry { + root: Arc<PathBuf>, + data: c::WIN32_FIND_DATAW, +} + +#[derive(Clone, Debug)] +pub struct OpenOptions { + // generic + read: bool, + write: bool, + append: bool, + truncate: bool, + create: bool, + create_new: bool, + // system-specific + custom_flags: u32, + access_mode: Option<c::DWORD>, + attributes: c::DWORD, + share_mode: c::DWORD, + security_qos_flags: c::DWORD, + security_attributes: usize, // FIXME: should be a reference +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { attrs: c::DWORD } + +#[derive(Debug)] +pub struct DirBuilder; + +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. + // Thus the result will be e g 'ReadDir("C:\")' + fmt::Debug::fmt(&*self.root, f) + } +} + +impl Iterator for ReadDir { + type Item = io::Result<DirEntry>; + fn next(&mut self) -> Option<io::Result<DirEntry>> { + 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 c::FindNextFileW(self.handle.0, &mut wfd) == 0 { + if c::GetLastError() == c::ERROR_NO_MORE_FILES { + 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 { c::FindClose(self.0) }; + debug_assert!(r != 0); + } +} + +impl DirEntry { + fn new(root: &Arc<PathBuf>, wfd: &c::WIN32_FIND_DATAW) -> Option<DirEntry> { + 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<FileType> { + Ok(FileType::new(self.data.dwFileAttributes, + /* reparse_tag = */ self.data.dwReserved0)) + } + + pub fn metadata(&self) -> io::Result<FileAttr> { + Ok(FileAttr { + attributes: self.data.dwFileAttributes, + creation_time: self.data.ftCreationTime, + last_access_time: self.data.ftLastAccessTime, + last_write_time: self.data.ftLastWriteTime, + file_size: ((self.data.nFileSizeHigh as u64) << 32) | (self.data.nFileSizeLow as u64), + reparse_tag: if self.data.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + // reserved unless this is a reparse point + self.data.dwReserved0 + } else { + 0 + }, + }) + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { + // generic + read: false, + write: false, + append: false, + truncate: false, + create: false, + create_new: false, + // system-specific + custom_flags: 0, + access_mode: None, + share_mode: c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE, + attributes: 0, + security_qos_flags: 0, + security_attributes: 0, + } + } + + 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 truncate(&mut self, truncate: bool) { self.truncate = truncate; } + pub fn create(&mut self, create: bool) { self.create = create; } + pub fn create_new(&mut self, create_new: bool) { self.create_new = create_new; } + + pub fn custom_flags(&mut self, flags: u32) { self.custom_flags = flags; } + pub fn access_mode(&mut self, access_mode: u32) { self.access_mode = Some(access_mode); } + pub fn share_mode(&mut self, share_mode: u32) { self.share_mode = share_mode; } + pub fn attributes(&mut self, attrs: u32) { self.attributes = attrs; } + pub fn security_qos_flags(&mut self, flags: u32) { self.security_qos_flags = flags; } + pub fn security_attributes(&mut self, attrs: c::LPSECURITY_ATTRIBUTES) { + self.security_attributes = attrs as usize; + } + + fn get_access_mode(&self) -> io::Result<c::DWORD> { + const ERROR_INVALID_PARAMETER: i32 = 87; + + match (self.read, self.write, self.append, self.access_mode) { + (.., Some(mode)) => Ok(mode), + (true, false, false, None) => Ok(c::GENERIC_READ), + (false, true, false, None) => Ok(c::GENERIC_WRITE), + (true, true, false, None) => Ok(c::GENERIC_READ | c::GENERIC_WRITE), + (false, _, true, None) => Ok(c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA), + (true, _, true, None) => Ok(c::GENERIC_READ | + (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA)), + (false, false, false, None) => Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)), + } + } + + fn get_creation_mode(&self) -> io::Result<c::DWORD> { + const ERROR_INVALID_PARAMETER: i32 = 87; + + match (self.write, self.append) { + (true, false) => {} + (false, false) => + if self.truncate || self.create || self.create_new { + return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)); + }, + (_, true) => + if self.truncate && !self.create_new { + return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)); + }, + } + + Ok(match (self.create, self.truncate, self.create_new) { + (false, false, false) => c::OPEN_EXISTING, + (true, false, false) => c::OPEN_ALWAYS, + (false, true, false) => c::TRUNCATE_EXISTING, + (true, true, false) => c::CREATE_ALWAYS, + (_, _, true) => c::CREATE_NEW, + }) + } + + fn get_flags_and_attributes(&self) -> c::DWORD { + self.custom_flags | + self.attributes | + self.security_qos_flags | + if self.security_qos_flags != 0 { c::SECURITY_SQOS_PRESENT } else { 0 } | + if self.create_new { c::FILE_FLAG_OPEN_REPARSE_POINT } else { 0 } + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { + let path = to_u16s(path)?; + let handle = unsafe { + c::CreateFileW(path.as_ptr(), + opts.get_access_mode()?, + opts.share_mode, + opts.security_attributes as *mut _, + opts.get_creation_mode()?, + opts.get_flags_and_attributes(), + ptr::null_mut()) + }; + if handle == c::INVALID_HANDLE_VALUE { + Err(Error::last_os_error()) + } else { + Ok(File { handle: Handle::new(handle) }) + } + } + + pub fn fsync(&self) -> io::Result<()> { + cvt(unsafe { c::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 c::LARGE_INTEGER, + }; + let size = mem::size_of_val(&info); + cvt(unsafe { + c::SetFileInformationByHandle(self.handle.raw(), + c::FileEndOfFileInfo, + &mut info as *mut _ as *mut _, + size as c::DWORD) + })?; + Ok(()) + } + + pub fn file_attr(&self) -> io::Result<FileAttr> { + unsafe { + let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed(); + cvt(c::GetFileInformationByHandle(self.handle.raw(), + &mut info))?; + let mut attr = FileAttr { + attributes: info.dwFileAttributes, + creation_time: info.ftCreationTime, + last_access_time: info.ftLastAccessTime, + last_write_time: info.ftLastWriteTime, + file_size: ((info.nFileSizeHigh as u64) << 32) | (info.nFileSizeLow as u64), + reparse_tag: 0, + }; + if attr.is_reparse_point() { + let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + if let Ok((_, buf)) = self.reparse_point(&mut b) { + attr.reparse_tag = buf.ReparseTag; + } + } + Ok(attr) + } + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.handle.read(buf) + } + + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { + self.handle.read_at(buf, offset) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.handle.write(buf) + } + + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + self.handle.write_at(buf, offset) + } + + pub fn flush(&self) -> io::Result<()> { Ok(()) } + + pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { + let (whence, pos) = match pos { + // Casting to `i64` is fine, `SetFilePointerEx` reinterprets this + // integer as `u64`. + SeekFrom::Start(n) => (c::FILE_BEGIN, n as i64), + SeekFrom::End(n) => (c::FILE_END, n), + SeekFrom::Current(n) => (c::FILE_CURRENT, n), + }; + let pos = pos as c::LARGE_INTEGER; + let mut newpos = 0; + cvt(unsafe { + c::SetFilePointerEx(self.handle.raw(), pos, + &mut newpos, whence) + })?; + Ok(newpos as u64) + } + + pub fn duplicate(&self) -> io::Result<File> { + Ok(File { + handle: self.handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS)?, + }) + } + + pub fn handle(&self) -> &Handle { &self.handle } + + pub fn into_handle(self) -> Handle { self.handle } + + fn reparse_point<'a>(&self, + space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]) + -> io::Result<(c::DWORD, &'a c::REPARSE_DATA_BUFFER)> { + unsafe { + let mut bytes = 0; + cvt({ + c::DeviceIoControl(self.handle.raw(), + c::FSCTL_GET_REPARSE_POINT, + ptr::null_mut(), + 0, + space.as_mut_ptr() as *mut _, + space.len() as c::DWORD, + &mut bytes, + ptr::null_mut()) + })?; + Ok((bytes, &*(space.as_ptr() as *const c::REPARSE_DATA_BUFFER))) + } + } + + fn readlink(&self) -> io::Result<PathBuf> { + let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + let (_bytes, buf) = self.reparse_point(&mut space)?; + unsafe { + let (path_buffer, subst_off, subst_len, relative) = match buf.ReparseTag { + c::IO_REPARSE_TAG_SYMLINK => { + let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER = + &buf.rest as *const _ as *const _; + (&(*info).PathBuffer as *const _ as *const u16, + (*info).SubstituteNameOffset / 2, + (*info).SubstituteNameLength / 2, + (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0) + }, + c::IO_REPARSE_TAG_MOUNT_POINT => { + let info: *const c::MOUNT_POINT_REPARSE_BUFFER = + &buf.rest as *const _ as *const _; + (&(*info).PathBuffer as *const _ as *const u16, + (*info).SubstituteNameOffset / 2, + (*info).SubstituteNameLength / 2, + false) + }, + _ => return Err(io::Error::new(io::ErrorKind::Other, + "Unsupported reparse point type")) + }; + let subst_ptr = path_buffer.offset(subst_off as isize); + let mut subst = slice::from_raw_parts(subst_ptr, subst_len as usize); + // Absolute paths start with an NT internal namespace prefix `\??\` + // We should not let it leak through. + if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) { + subst = &subst[4..]; + } + Ok(PathBuf::from(OsString::from_wide(subst))) + } + } + + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + let mut info = c::FILE_BASIC_INFO { + CreationTime: 0, + LastAccessTime: 0, + LastWriteTime: 0, + ChangeTime: 0, + FileAttributes: perm.attrs, + }; + let size = mem::size_of_val(&info); + cvt(unsafe { + c::SetFileInformationByHandle(self.handle.raw(), + c::FileBasicInfo, + &mut info as *mut _ as *mut _, + size as c::DWORD) + })?; + Ok(()) + } +} + +impl FromInner<c::HANDLE> for File { + fn from_inner(handle: c::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. mode) + let mut b = f.debug_struct("File"); + b.field("handle", &self.handle.raw()); + if let Ok(path) = get_path(&self) { + b.field("path", &path); + } + b.finish() + } +} + +impl FileAttr { + pub fn size(&self) -> u64 { + self.file_size + } + + pub fn perm(&self) -> FilePermissions { + FilePermissions { attrs: self.attributes } + } + + pub fn attrs(&self) -> u32 { self.attributes as u32 } + + pub fn file_type(&self) -> FileType { + FileType::new(self.attributes, self.reparse_tag) + } + + pub fn modified(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from(self.last_write_time)) + } + + pub fn accessed(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from(self.last_access_time)) + } + + pub fn created(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from(self.creation_time)) + } + + pub fn modified_u64(&self) -> u64 { + to_u64(&self.last_write_time) + } + + pub fn accessed_u64(&self) -> u64 { + to_u64(&self.last_access_time) + } + + pub fn created_u64(&self) -> u64 { + to_u64(&self.creation_time) + } + + fn is_reparse_point(&self) -> bool { + self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 + } +} + +fn to_u64(ft: &c::FILETIME) -> u64 { + (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32) +} + +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: c::DWORD, reparse_tag: c::DWORD) -> FileType { + FileType { + attributes: attrs, + reparse_tag: reparse_tag, + } + } + pub fn is_dir(&self) -> bool { + !self.is_symlink() && self.is_directory() + } + pub fn is_file(&self) -> bool { + !self.is_symlink() && !self.is_directory() + } + pub fn is_symlink(&self) -> bool { + self.is_reparse_point() && self.is_reparse_tag_name_surrogate() + } + pub fn is_symlink_dir(&self) -> bool { + self.is_symlink() && self.is_directory() + } + pub fn is_symlink_file(&self) -> bool { + self.is_symlink() && !self.is_directory() + } + fn is_directory(&self) -> bool { + self.attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0 + } + fn is_reparse_point(&self) -> bool { + self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 + } + fn is_reparse_tag_name_surrogate(&self) -> bool { + self.reparse_tag & 0x20000000 != 0 + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { DirBuilder } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + let p = to_u16s(p)?; + cvt(unsafe { + c::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) + })?; + Ok(()) + } +} + +pub fn readdir(p: &Path) -> io::Result<ReadDir> { + let root = p.to_path_buf(); + let star = p.join("*"); + let path = to_u16s(&star)?; + + unsafe { + let mut wfd = mem::zeroed(); + let find_handle = c::FindFirstFileW(path.as_ptr(), &mut wfd); + if find_handle != c::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_u16s = to_u16s(p)?; + cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?; + Ok(()) +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + let old = to_u16s(old)?; + let new = to_u16s(new)?; + cvt(unsafe { + c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) + })?; + Ok(()) +} + +pub fn rmdir(p: &Path) -> io::Result<()> { + let p = to_u16s(p)?; + cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?; + Ok(()) +} + +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + let filetype = lstat(path)?.file_type(); + if filetype.is_symlink() { + // On Windows symlinks to files and directories are removed differently. + // rmdir only deletes dir symlinks and junctions, not file symlinks. + rmdir(path) + } else { + remove_dir_all_recursive(path) + } +} + +fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { + for child in readdir(path)? { + let child = child?; + let child_type = child.file_type()?; + if child_type.is_dir() { + remove_dir_all_recursive(&child.path())?; + } else if child_type.is_symlink_dir() { + rmdir(&child.path())?; + } else { + unlink(&child.path())?; + } + } + rmdir(path) +} + +pub fn readlink(path: &Path) -> io::Result<PathBuf> { + // Open the link with no access mode, instead of generic read. + // By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so + // this is needed for a common case. + let mut opts = OpenOptions::new(); + opts.access_mode(0); + opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | + c::FILE_FLAG_BACKUP_SEMANTICS); + let file = File::open(&path, &opts)?; + 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<()> { + let src = to_u16s(src)?; + let dst = to_u16s(dst)?; + let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 }; + // Formerly, symlink creation required the SeCreateSymbolicLink privilege. For the Windows 10 + // Creators Update, Microsoft loosened this to allow unprivileged symlink creation if the + // computer is in Developer Mode, but SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE must be + // added to dwFlags to opt into this behaviour. + let result = cvt(unsafe { + c::CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), + flags | c::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) as c::BOOL + }); + if let Err(err) = result { + if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as i32) { + // Older Windows objects to SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, + // so if we encounter ERROR_INVALID_PARAMETER, retry without that flag. + cvt(unsafe { + c::CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as c::BOOL + })?; + } else { + return Err(err); + } + } + Ok(()) +} + +pub fn link(src: &Path, dst: &Path) -> io::Result<()> { + let src = to_u16s(src)?; + let dst = to_u16s(dst)?; + cvt(unsafe { + c::CreateHardLinkW(dst.as_ptr(), src.as_ptr(), ptr::null_mut()) + })?; + Ok(()) +} + +pub fn stat(path: &Path) -> io::Result<FileAttr> { + let mut opts = OpenOptions::new(); + // No read or write permissions are necessary + opts.access_mode(0); + // This flag is so we can open directories too + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); + let file = File::open(path, &opts)?; + file.file_attr() +} + +pub fn lstat(path: &Path) -> io::Result<FileAttr> { + let mut opts = OpenOptions::new(); + // No read or write permissions are necessary + opts.access_mode(0); + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); + let file = File::open(path, &opts)?; + file.file_attr() +} + +pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + let p = to_u16s(p)?; + unsafe { + cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?; + Ok(()) + } +} + +fn get_path(f: &File) -> io::Result<PathBuf> { + super::fill_utf16_buf(|buf, sz| unsafe { + c::GetFinalPathNameByHandleW(f.handle.raw(), buf, sz, + c::VOLUME_NAME_DOS) + }, |buf| { + PathBuf::from(OsString::from_wide(buf)) + }) +} + +pub fn canonicalize(p: &Path) -> io::Result<PathBuf> { + let mut opts = OpenOptions::new(); + // No read or write permissions are necessary + opts.access_mode(0); + // This flag is so we can open directories too + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); + let f = File::open(p, &opts)?; + get_path(&f) +} + +pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { + unsafe extern "system" fn callback( + _TotalFileSize: c::LARGE_INTEGER, + _TotalBytesTransferred: c::LARGE_INTEGER, + _StreamSize: c::LARGE_INTEGER, + StreamBytesTransferred: c::LARGE_INTEGER, + dwStreamNumber: c::DWORD, + _dwCallbackReason: c::DWORD, + _hSourceFile: c::HANDLE, + _hDestinationFile: c::HANDLE, + lpData: c::LPVOID, + ) -> c::DWORD { + if dwStreamNumber == 1 {*(lpData as *mut i64) = StreamBytesTransferred;} + c::PROGRESS_CONTINUE + } + let pfrom = to_u16s(from)?; + let pto = to_u16s(to)?; + let mut size = 0i64; + cvt(unsafe { + c::CopyFileExW(pfrom.as_ptr(), pto.as_ptr(), Some(callback), + &mut size as *mut _ as *mut _, ptr::null_mut(), 0) + })?; + Ok(size as u64) +} + +#[allow(dead_code)] +pub fn symlink_junction<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> { + symlink_junction_inner(src.as_ref(), dst.as_ref()) +} + +// Creating a directory junction on windows involves dealing with reparse +// points and the DeviceIoControl function, and this code is a skeleton of +// what can be found here: +// +// http://www.flexhex.com/docs/articles/hard-links.phtml +#[allow(dead_code)] +fn symlink_junction_inner(target: &Path, junction: &Path) -> io::Result<()> { + let d = DirBuilder::new(); + d.mkdir(&junction)?; + + let mut opts = OpenOptions::new(); + opts.write(true); + opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | + c::FILE_FLAG_BACKUP_SEMANTICS); + let f = File::open(junction, &opts)?; + let h = f.handle().raw(); + + unsafe { + let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + let db = data.as_mut_ptr() + as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER; + let buf = &mut (*db).ReparseTarget as *mut c::WCHAR; + let mut i = 0; + // FIXME: this conversion is very hacky + let v = br"\??\"; + let v = v.iter().map(|x| *x as u16); + for c in v.chain(target.as_os_str().encode_wide()) { + *buf.offset(i) = c; + i += 1; + } + *buf.offset(i) = 0; + i += 1; + (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT; + (*db).ReparseTargetMaximumLength = (i * 2) as c::WORD; + (*db).ReparseTargetLength = ((i - 1) * 2) as c::WORD; + (*db).ReparseDataLength = + (*db).ReparseTargetLength as c::DWORD + 12; + + let mut ret = 0; + cvt(c::DeviceIoControl(h as *mut _, + c::FSCTL_SET_REPARSE_POINT, + data.as_ptr() as *mut _, + (*db).ReparseDataLength + 8, + ptr::null_mut(), 0, + &mut ret, + ptr::null_mut())).map(|_| ()) + } +} diff --git a/ctr-std/src/sys/windows/handle.rs b/ctr-std/src/sys/windows/handle.rs new file mode 100644 index 0000000..3729d6d --- /dev/null +++ b/ctr-std/src/sys/windows/handle.rs @@ -0,0 +1,218 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(issue = "0", feature = "windows_handle")] + +use cmp; +use io::{ErrorKind, Read}; +use io; +use mem; +use ops::Deref; +use ptr; +use sys::c; +use sys::cvt; + +/// An owned container for `HANDLE` object, closing them on Drop. +/// +/// All methods are inherited through a `Deref` impl to `RawHandle` +pub struct Handle(RawHandle); + +/// A wrapper type for `HANDLE` objects to give them proper Send/Sync inference +/// as well as Rust-y methods. +/// +/// This does **not** drop the handle when it goes out of scope, use `Handle` +/// instead for that. +#[derive(Copy, Clone)] +pub struct RawHandle(c::HANDLE); + +unsafe impl Send for RawHandle {} +unsafe impl Sync for RawHandle {} + +impl Handle { + pub fn new(handle: c::HANDLE) -> Handle { + Handle(RawHandle::new(handle)) + } + + pub fn new_event(manual: bool, init: bool) -> io::Result<Handle> { + unsafe { + let event = c::CreateEventW(ptr::null_mut(), + manual as c::BOOL, + init as c::BOOL, + ptr::null()); + if event.is_null() { + Err(io::Error::last_os_error()) + } else { + Ok(Handle::new(event)) + } + } + } + + pub fn into_raw(self) -> c::HANDLE { + let ret = self.raw(); + mem::forget(self); + return ret; + } +} + +impl Deref for Handle { + type Target = RawHandle; + fn deref(&self) -> &RawHandle { &self.0 } +} + +impl Drop for Handle { + fn drop(&mut self) { + unsafe { let _ = c::CloseHandle(self.raw()); } + } +} + +impl RawHandle { + pub fn new(handle: c::HANDLE) -> RawHandle { + RawHandle(handle) + } + + pub fn raw(&self) -> c::HANDLE { self.0 } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + let mut read = 0; + let len = cmp::min(buf.len(), <c::DWORD>::max_value() as usize) as c::DWORD; + let res = cvt(unsafe { + c::ReadFile(self.0, buf.as_mut_ptr() as c::LPVOID, + len, &mut read, ptr::null_mut()) + }); + + match res { + Ok(_) => Ok(read as usize), + + // The special treatment of BrokenPipe is to deal with Windows + // pipe semantics, which yields this error when *reading* from + // a pipe after the other end has closed; we interpret that as + // EOF on the pipe. + Err(ref e) if e.kind() == ErrorKind::BrokenPipe => Ok(0), + + Err(e) => Err(e) + } + } + + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { + let mut read = 0; + let len = cmp::min(buf.len(), <c::DWORD>::max_value() as usize) as c::DWORD; + let res = unsafe { + let mut overlapped: c::OVERLAPPED = mem::zeroed(); + overlapped.Offset = offset as u32; + overlapped.OffsetHigh = (offset >> 32) as u32; + cvt(c::ReadFile(self.0, buf.as_mut_ptr() as c::LPVOID, + len, &mut read, &mut overlapped)) + }; + match res { + Ok(_) => Ok(read as usize), + Err(ref e) if e.raw_os_error() == Some(c::ERROR_HANDLE_EOF as i32) => Ok(0), + Err(e) => Err(e), + } + } + + pub unsafe fn read_overlapped(&self, + buf: &mut [u8], + overlapped: *mut c::OVERLAPPED) + -> io::Result<Option<usize>> { + let len = cmp::min(buf.len(), <c::DWORD>::max_value() as usize) as c::DWORD; + let mut amt = 0; + let res = cvt({ + c::ReadFile(self.0, buf.as_ptr() as c::LPVOID, + len, &mut amt, overlapped) + }); + match res { + Ok(_) => Ok(Some(amt as usize)), + Err(e) => { + if e.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) { + Ok(None) + } else if e.raw_os_error() == Some(c::ERROR_BROKEN_PIPE as i32) { + Ok(Some(0)) + } else { + Err(e) + } + } + } + } + + pub fn overlapped_result(&self, + overlapped: *mut c::OVERLAPPED, + wait: bool) -> io::Result<usize> { + unsafe { + let mut bytes = 0; + let wait = if wait {c::TRUE} else {c::FALSE}; + let res = cvt({ + c::GetOverlappedResult(self.raw(), overlapped, &mut bytes, wait) + }); + match res { + Ok(_) => Ok(bytes as usize), + Err(e) => { + if e.raw_os_error() == Some(c::ERROR_HANDLE_EOF as i32) || + e.raw_os_error() == Some(c::ERROR_BROKEN_PIPE as i32) { + Ok(0) + } else { + Err(e) + } + } + } + } + } + + pub fn cancel_io(&self) -> io::Result<()> { + unsafe { + cvt(c::CancelIo(self.raw())).map(|_| ()) + } + } + + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { + let mut me = self; + (&mut me).read_to_end(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + let mut amt = 0; + let len = cmp::min(buf.len(), <c::DWORD>::max_value() as usize) as c::DWORD; + cvt(unsafe { + c::WriteFile(self.0, buf.as_ptr() as c::LPVOID, + len, &mut amt, ptr::null_mut()) + })?; + Ok(amt as usize) + } + + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + let mut written = 0; + let len = cmp::min(buf.len(), <c::DWORD>::max_value() as usize) as c::DWORD; + unsafe { + let mut overlapped: c::OVERLAPPED = mem::zeroed(); + overlapped.Offset = offset as u32; + overlapped.OffsetHigh = (offset >> 32) as u32; + cvt(c::WriteFile(self.0, buf.as_ptr() as c::LPVOID, + len, &mut written, &mut overlapped))?; + } + Ok(written as usize) + } + + pub fn duplicate(&self, access: c::DWORD, inherit: bool, + options: c::DWORD) -> io::Result<Handle> { + let mut ret = 0 as c::HANDLE; + cvt(unsafe { + let cur_proc = c::GetCurrentProcess(); + c::DuplicateHandle(cur_proc, self.0, cur_proc, &mut ret, + access, inherit as c::BOOL, + options) + })?; + Ok(Handle::new(ret)) + } +} + +impl<'a> Read for &'a RawHandle { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + (**self).read(buf) + } +} diff --git a/ctr-std/src/sys/windows/memchr.rs b/ctr-std/src/sys/windows/memchr.rs new file mode 100644 index 0000000..fa7c816 --- /dev/null +++ b/ctr-std/src/sys/windows/memchr.rs @@ -0,0 +1,15 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// Original implementation taken from rust-memchr +// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch + +// Fallback memchr is fastest on windows +pub use core::slice::memchr::{memchr, memrchr}; diff --git a/ctr-std/src/sys/windows/mod.rs b/ctr-std/src/sys/windows/mod.rs new file mode 100644 index 0000000..0d12ecf --- /dev/null +++ b/ctr-std/src/sys/windows/mod.rs @@ -0,0 +1,273 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(missing_docs, bad_style)] + +use ptr; +use ffi::{OsStr, OsString}; +use io::{self, ErrorKind}; +use os::windows::ffi::{OsStrExt, OsStringExt}; +use path::PathBuf; +use time::Duration; + +pub use libc::strlen; +pub use self::rand::hashmap_random_keys; + +#[macro_use] pub mod compat; + +pub mod args; +#[cfg(feature = "backtrace")] +pub mod backtrace; +pub mod c; +pub mod cmath; +pub mod condvar; +#[cfg(feature = "backtrace")] +pub mod dynamic_lib; +pub mod env; +pub mod ext; +pub mod fast_thread_local; +pub mod fs; +pub mod handle; +pub mod memchr; +pub mod mutex; +pub mod net; +pub mod os; +pub mod os_str; +pub mod path; +pub mod pipe; +pub mod process; +pub mod rand; +pub mod rwlock; +pub mod stack_overflow; +pub mod thread; +pub mod thread_local; +pub mod time; +pub mod stdio; + +#[cfg(not(test))] +pub fn init() { +} + +pub fn decode_error_kind(errno: i32) -> ErrorKind { + match errno as c::DWORD { + c::ERROR_ACCESS_DENIED => return ErrorKind::PermissionDenied, + c::ERROR_ALREADY_EXISTS => return ErrorKind::AlreadyExists, + c::ERROR_FILE_EXISTS => return ErrorKind::AlreadyExists, + c::ERROR_BROKEN_PIPE => return ErrorKind::BrokenPipe, + c::ERROR_FILE_NOT_FOUND => return ErrorKind::NotFound, + c::ERROR_PATH_NOT_FOUND => return ErrorKind::NotFound, + c::ERROR_NO_DATA => return ErrorKind::BrokenPipe, + c::ERROR_OPERATION_ABORTED => return ErrorKind::TimedOut, + _ => {} + } + + match errno { + c::WSAEACCES => ErrorKind::PermissionDenied, + c::WSAEADDRINUSE => ErrorKind::AddrInUse, + c::WSAEADDRNOTAVAIL => ErrorKind::AddrNotAvailable, + c::WSAECONNABORTED => ErrorKind::ConnectionAborted, + c::WSAECONNREFUSED => ErrorKind::ConnectionRefused, + c::WSAECONNRESET => ErrorKind::ConnectionReset, + c::WSAEINVAL => ErrorKind::InvalidInput, + c::WSAENOTCONN => ErrorKind::NotConnected, + c::WSAEWOULDBLOCK => ErrorKind::WouldBlock, + c::WSAETIMEDOUT => ErrorKind::TimedOut, + + _ => ErrorKind::Other, + } +} + +pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> io::Result<Vec<u16>> { + fn inner(s: &OsStr) -> io::Result<Vec<u16>> { + let mut maybe_result: Vec<u16> = s.encode_wide().collect(); + if maybe_result.iter().any(|&u| u == 0) { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "strings passed to WinAPI cannot contain NULs")); + } + maybe_result.push(0); + Ok(maybe_result) + } + inner(s.as_ref()) +} + +// Many Windows APIs follow a pattern of where we hand a buffer and then they +// will report back to us how large the buffer should be or how many bytes +// currently reside in the buffer. This function is an abstraction over these +// functions by making them easier to call. +// +// The first callback, `f1`, is yielded a (pointer, len) pair which can be +// passed to a syscall. The `ptr` is valid for `len` items (u16 in this case). +// The closure is expected to return what the syscall returns which will be +// interpreted by this function to determine if the syscall needs to be invoked +// again (with more buffer space). +// +// Once the syscall has completed (errors bail out early) the second closure is +// yielded the data which has been read from the syscall. The return value +// from this closure is then the return value of the function. +fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> io::Result<T> + where F1: FnMut(*mut u16, c::DWORD) -> c::DWORD, + F2: FnOnce(&[u16]) -> T +{ + // Start off with a stack buf but then spill over to the heap if we end up + // needing more space. + let mut stack_buf = [0u16; 512]; + let mut heap_buf = Vec::new(); + unsafe { + let mut n = stack_buf.len(); + loop { + let buf = if n <= stack_buf.len() { + &mut stack_buf[..] + } else { + let extra = n - heap_buf.len(); + heap_buf.reserve(extra); + heap_buf.set_len(n); + &mut heap_buf[..] + }; + + // This function is typically called on windows API functions which + // will return the correct length of the string, but these functions + // also return the `0` on error. In some cases, however, the + // returned "correct length" may actually be 0! + // + // To handle this case we call `SetLastError` to reset it to 0 and + // then check it again if we get the "0 error value". If the "last + // error" is still 0 then we interpret it as a 0 length buffer and + // not an actual error. + c::SetLastError(0); + let k = match f1(buf.as_mut_ptr(), n as c::DWORD) { + 0 if c::GetLastError() == 0 => 0, + 0 => return Err(io::Error::last_os_error()), + n => n, + } as usize; + if k == n && c::GetLastError() == c::ERROR_INSUFFICIENT_BUFFER { + n *= 2; + } else if k >= n { + n = k; + } else { + return Ok(f2(&buf[..k])) + } + } + } +} + +fn os2path(s: &[u16]) -> PathBuf { + PathBuf::from(OsString::from_wide(s)) +} + +#[allow(dead_code)] // Only used in backtrace::gnu::get_executable_filename() +fn wide_char_to_multi_byte(code_page: u32, + flags: u32, + s: &[u16], + no_default_char: bool) + -> io::Result<Vec<i8>> { + unsafe { + let mut size = c::WideCharToMultiByte(code_page, + flags, + s.as_ptr(), + s.len() as i32, + ptr::null_mut(), + 0, + ptr::null(), + ptr::null_mut()); + if size == 0 { + return Err(io::Error::last_os_error()); + } + + let mut buf = Vec::with_capacity(size as usize); + buf.set_len(size as usize); + + let mut used_default_char = c::FALSE; + size = c::WideCharToMultiByte(code_page, + flags, + s.as_ptr(), + s.len() as i32, + buf.as_mut_ptr(), + buf.len() as i32, + ptr::null(), + if no_default_char { &mut used_default_char } + else { ptr::null_mut() }); + if size == 0 { + return Err(io::Error::last_os_error()); + } + if no_default_char && used_default_char == c::TRUE { + return Err(io::Error::new(io::ErrorKind::InvalidData, + "string cannot be converted to requested code page")); + } + + buf.set_len(size as usize); + + Ok(buf) + } +} + +pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] { + match v.iter().position(|c| *c == 0) { + // don't include the 0 + Some(i) => &v[..i], + None => v + } +} + +pub trait IsZero { + fn is_zero(&self) -> bool; +} + +macro_rules! impl_is_zero { + ($($t:ident)*) => ($(impl IsZero for $t { + fn is_zero(&self) -> bool { + *self == 0 + } + })*) +} + +impl_is_zero! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize } + +pub fn cvt<I: IsZero>(i: I) -> io::Result<I> { + if i.is_zero() { + Err(io::Error::last_os_error()) + } else { + Ok(i) + } +} + +pub fn dur2timeout(dur: Duration) -> c::DWORD { + // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the + // timeouts in windows APIs are typically u32 milliseconds. To translate, we + // have two pieces to take care of: + // + // * Nanosecond precision is rounded up + // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE + // (never time out). + dur.as_secs().checked_mul(1000).and_then(|ms| { + ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000) + }).and_then(|ms| { + ms.checked_add(if dur.subsec_nanos() % 1_000_000 > 0 {1} else {0}) + }).map(|ms| { + if ms > <c::DWORD>::max_value() as u64 { + c::INFINITE + } else { + ms as c::DWORD + } + }).unwrap_or(c::INFINITE) +} + +// On Windows, use the processor-specific __fastfail mechanism. In Windows 8 +// and later, this will terminate the process immediately without running any +// in-process exception handlers. In earlier versions of Windows, this +// sequence of instructions will be treated as an access violation, +// terminating the process but without necessarily bypassing all exception +// handlers. +// +// https://msdn.microsoft.com/en-us/library/dn774154.aspx +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub unsafe fn abort_internal() -> ! { + asm!("int $$0x29" :: "{ecx}"(7) ::: volatile); // 7 is FAST_FAIL_FATAL_APP_EXIT + ::intrinsics::unreachable(); +} diff --git a/ctr-std/src/sys/windows/mutex.rs b/ctr-std/src/sys/windows/mutex.rs new file mode 100644 index 0000000..9bf9f74 --- /dev/null +++ b/ctr-std/src/sys/windows/mutex.rs @@ -0,0 +1,188 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! System Mutexes +//! +//! The Windows implementation of mutexes is a little odd and it may not be +//! immediately obvious what's going on. The primary oddness is that SRWLock is +//! used instead of CriticalSection, and this is done because: +//! +//! 1. SRWLock is several times faster than CriticalSection according to +//! benchmarks performed on both Windows 8 and Windows 7. +//! +//! 2. CriticalSection allows recursive locking while SRWLock deadlocks. The +//! Unix implementation deadlocks so consistency is preferred. See #19962 for +//! more details. +//! +//! 3. While CriticalSection is fair and SRWLock is not, the current Rust policy +//! is that there are no guarantees of fairness. +//! +//! The downside of this approach, however, is that SRWLock is not available on +//! Windows XP, so we continue to have a fallback implementation where +//! CriticalSection is used and we keep track of who's holding the mutex to +//! detect recursive locks. + +use cell::UnsafeCell; +use mem; +use sync::atomic::{AtomicUsize, Ordering}; +use sys::c; +use sys::compat; + +pub struct Mutex { + lock: AtomicUsize, + held: UnsafeCell<bool>, +} + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +#[derive(Clone, Copy)] +enum Kind { + SRWLock = 1, + CriticalSection = 2, +} + +#[inline] +pub unsafe fn raw(m: &Mutex) -> c::PSRWLOCK { + debug_assert!(mem::size_of::<c::SRWLOCK>() <= mem::size_of_val(&m.lock)); + &m.lock as *const _ as *mut _ +} + +impl Mutex { + pub const fn new() -> Mutex { + Mutex { + lock: AtomicUsize::new(0), + held: UnsafeCell::new(false), + } + } + #[inline] + pub unsafe fn init(&mut self) {} + pub unsafe fn lock(&self) { + match kind() { + Kind::SRWLock => c::AcquireSRWLockExclusive(raw(self)), + Kind::CriticalSection => { + let re = self.remutex(); + (*re).lock(); + if !self.flag_locked() { + (*re).unlock(); + panic!("cannot recursively lock a mutex"); + } + } + } + } + pub unsafe fn try_lock(&self) -> bool { + match kind() { + Kind::SRWLock => c::TryAcquireSRWLockExclusive(raw(self)) != 0, + Kind::CriticalSection => { + let re = self.remutex(); + if !(*re).try_lock() { + false + } else if self.flag_locked() { + true + } else { + (*re).unlock(); + false + } + } + } + } + pub unsafe fn unlock(&self) { + *self.held.get() = false; + match kind() { + Kind::SRWLock => c::ReleaseSRWLockExclusive(raw(self)), + Kind::CriticalSection => (*self.remutex()).unlock(), + } + } + pub unsafe fn destroy(&self) { + match kind() { + Kind::SRWLock => {} + Kind::CriticalSection => { + match self.lock.load(Ordering::SeqCst) { + 0 => {} + n => { Box::from_raw(n as *mut ReentrantMutex).destroy(); } + } + } + } + } + + unsafe fn remutex(&self) -> *mut ReentrantMutex { + match self.lock.load(Ordering::SeqCst) { + 0 => {} + n => return n as *mut _, + } + let mut re = box ReentrantMutex::uninitialized(); + re.init(); + let re = Box::into_raw(re); + match self.lock.compare_and_swap(0, re as usize, Ordering::SeqCst) { + 0 => re, + n => { Box::from_raw(re).destroy(); n as *mut _ } + } + } + + unsafe fn flag_locked(&self) -> bool { + if *self.held.get() { + false + } else { + *self.held.get() = true; + true + } + + } +} + +fn kind() -> Kind { + static KIND: AtomicUsize = AtomicUsize::new(0); + + let val = KIND.load(Ordering::SeqCst); + if val == Kind::SRWLock as usize { + return Kind::SRWLock + } else if val == Kind::CriticalSection as usize { + return Kind::CriticalSection + } + + let ret = match compat::lookup("kernel32", "AcquireSRWLockExclusive") { + None => Kind::CriticalSection, + Some(..) => Kind::SRWLock, + }; + KIND.store(ret as usize, Ordering::SeqCst); + return ret; +} + +pub struct ReentrantMutex { inner: UnsafeCell<c::CRITICAL_SECTION> } + +unsafe impl Send for ReentrantMutex {} +unsafe impl Sync for ReentrantMutex {} + +impl ReentrantMutex { + pub unsafe fn uninitialized() -> ReentrantMutex { + mem::uninitialized() + } + + pub unsafe fn init(&mut self) { + c::InitializeCriticalSection(self.inner.get()); + } + + pub unsafe fn lock(&self) { + c::EnterCriticalSection(self.inner.get()); + } + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + c::TryEnterCriticalSection(self.inner.get()) != 0 + } + + pub unsafe fn unlock(&self) { + c::LeaveCriticalSection(self.inner.get()); + } + + pub unsafe fn destroy(&self) { + c::DeleteCriticalSection(self.inner.get()); + } +} diff --git a/ctr-std/src/sys/windows/net.rs b/ctr-std/src/sys/windows/net.rs new file mode 100644 index 0000000..cd8acff --- /dev/null +++ b/ctr-std/src/sys/windows/net.rs @@ -0,0 +1,356 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(issue = "0", feature = "windows_net")] + +use cmp; +use io::{self, Read}; +use libc::{c_int, c_void, c_ulong, c_long}; +use mem; +use net::{SocketAddr, Shutdown}; +use ptr; +use sync::Once; +use sys::c; +use sys; +use sys_common::{self, AsInner, FromInner, IntoInner}; +use sys_common::net; +use time::Duration; + +pub type wrlen_t = i32; + +pub mod netc { + pub use sys::c::*; + pub use sys::c::SOCKADDR as sockaddr; + pub use sys::c::SOCKADDR_STORAGE_LH as sockaddr_storage; + pub use sys::c::ADDRINFOA as addrinfo; + pub use sys::c::ADDRESS_FAMILY as sa_family_t; +} + +pub struct Socket(c::SOCKET); + +/// Checks whether the Windows socket interface has been started already, and +/// if not, starts it. +pub fn init() { + static START: Once = Once::new(); + + START.call_once(|| unsafe { + let mut data: c::WSADATA = mem::zeroed(); + let ret = c::WSAStartup(0x202, // version 2.2 + &mut data); + assert_eq!(ret, 0); + + let _ = sys_common::at_exit(|| { c::WSACleanup(); }); + }); +} + +/// Returns the last error from the Windows socket interface. +fn last_error() -> io::Error { + io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() }) +} + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +/// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1) +/// and if so, returns the last error from the Windows socket interface. This +/// function must be called before another call to the socket API is made. +pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> { + if t.is_minus_one() { + Err(last_error()) + } else { + Ok(t) + } +} + +/// A variant of `cvt` for `getaddrinfo` which return 0 for a success. +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { + Ok(()) + } else { + Err(last_error()) + } +} + +/// Just to provide the same interface as sys/unix/net.rs +pub fn cvt_r<T, F>(mut f: F) -> io::Result<T> + where T: IsMinusOne, + F: FnMut() -> T +{ + cvt(f()) +} + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> { + let fam = match *addr { + SocketAddr::V4(..) => c::AF_INET, + SocketAddr::V6(..) => c::AF_INET6, + }; + let socket = unsafe { + match c::WSASocketW(fam, ty, 0, ptr::null_mut(), 0, + c::WSA_FLAG_OVERLAPPED) { + c::INVALID_SOCKET => Err(last_error()), + n => Ok(Socket(n)), + } + }?; + socket.set_no_inherit()?; + Ok(socket) + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = unsafe { + let (addrp, len) = addr.into_inner(); + cvt(c::connect(self.0, addrp, len)) + }; + self.set_nonblocking(false)?; + + match r { + Ok(_) => return Ok(()), + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {} + Err(e) => return Err(e), + } + + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } + + let mut timeout = c::timeval { + tv_sec: timeout.as_secs() as c_long, + tv_usec: (timeout.subsec_nanos() / 1000) as c_long, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + + let fds = unsafe { + let mut fds = mem::zeroed::<c::fd_set>(); + fds.fd_count = 1; + fds.fd_array[0] = self.0; + fds + }; + + let mut writefds = fds; + let mut errorfds = fds; + + let n = unsafe { + cvt(c::select(1, ptr::null_mut(), &mut writefds, &mut errorfds, &timeout))? + }; + + match n { + 0 => Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")), + _ => { + if writefds.fd_count != 1 { + if let Some(e) = self.take_error()? { + return Err(e); + } + } + Ok(()) + } + } + } + + pub fn accept(&self, storage: *mut c::SOCKADDR, + len: *mut c_int) -> io::Result<Socket> { + let socket = unsafe { + match c::accept(self.0, storage, len) { + c::INVALID_SOCKET => Err(last_error()), + n => Ok(Socket(n)), + } + }?; + socket.set_no_inherit()?; + Ok(socket) + } + + pub fn duplicate(&self) -> io::Result<Socket> { + let socket = unsafe { + let mut info: c::WSAPROTOCOL_INFO = mem::zeroed(); + cvt(c::WSADuplicateSocketW(self.0, + c::GetCurrentProcessId(), + &mut info))?; + match c::WSASocketW(info.iAddressFamily, + info.iSocketType, + info.iProtocol, + &mut info, 0, + c::WSA_FLAG_OVERLAPPED) { + c::INVALID_SOCKET => Err(last_error()), + n => Ok(Socket(n)), + } + }?; + socket.set_no_inherit()?; + Ok(socket) + } + + fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> { + // On unix when a socket is shut down all further reads return 0, so we + // do the same on windows to map a shut down socket to returning EOF. + let len = cmp::min(buf.len(), i32::max_value() as usize) as i32; + unsafe { + match c::recv(self.0, buf.as_mut_ptr() as *mut c_void, len, flags) { + -1 if c::WSAGetLastError() == c::WSAESHUTDOWN => Ok(0), + -1 => Err(last_error()), + n => Ok(n as usize) + } + } + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.recv_with_flags(buf, 0) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { + self.recv_with_flags(buf, c::MSG_PEEK) + } + + fn recv_from_with_flags(&self, buf: &mut [u8], flags: c_int) + -> io::Result<(usize, SocketAddr)> { + let mut storage: c::SOCKADDR_STORAGE_LH = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as c::socklen_t; + let len = cmp::min(buf.len(), <wrlen_t>::max_value() as usize) as wrlen_t; + + // On unix when a socket is shut down all further reads return 0, so we + // do the same on windows to map a shut down socket to returning EOF. + unsafe { + match c::recvfrom(self.0, + buf.as_mut_ptr() as *mut c_void, + len, + flags, + &mut storage as *mut _ as *mut _, + &mut addrlen) { + -1 if c::WSAGetLastError() == c::WSAESHUTDOWN => { + Ok((0, net::sockaddr_to_addr(&storage, addrlen as usize)?)) + }, + -1 => Err(last_error()), + n => Ok((n as usize, net::sockaddr_to_addr(&storage, addrlen as usize)?)), + } + } + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, c::MSG_PEEK) + } + + pub fn set_timeout(&self, dur: Option<Duration>, + kind: c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + let timeout = sys::dur2timeout(dur); + if timeout == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } + timeout + } + None => 0 + }; + net::setsockopt(self, c::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: c_int) -> io::Result<Option<Duration>> { + let raw: c::DWORD = net::getsockopt(self, c::SOL_SOCKET, kind)?; + if raw == 0 { + Ok(None) + } else { + let secs = raw / 1000; + let nsec = (raw % 1000) * 1000000; + Ok(Some(Duration::new(secs as u64, nsec as u32))) + } + } + + fn set_no_inherit(&self) -> io::Result<()> { + sys::cvt(unsafe { + c::SetHandleInformation(self.0 as c::HANDLE, + c::HANDLE_FLAG_INHERIT, 0) + }).map(|_| ()) + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => c::SD_SEND, + Shutdown::Read => c::SD_RECEIVE, + Shutdown::Both => c::SD_BOTH, + }; + cvt(unsafe { c::shutdown(self.0, how) })?; + Ok(()) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as c_ulong; + let r = unsafe { c::ioctlsocket(self.0, c::FIONBIO as c_int, &mut nonblocking) }; + if r == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BYTE) + } + + pub fn nodelay(&self) -> io::Result<bool> { + let raw: c::BYTE = net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; + Ok(raw != 0) + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + let raw: c_int = net::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; + if raw == 0 { + Ok(None) + } else { + Ok(Some(io::Error::from_raw_os_error(raw as i32))) + } + } +} + +#[unstable(reason = "not public", issue = "0", feature = "fd_read")] +impl<'a> Read for &'a Socket { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + (**self).read(buf) + } +} + +impl Drop for Socket { + fn drop(&mut self) { + let _ = unsafe { c::closesocket(self.0) }; + } +} + +impl AsInner<c::SOCKET> for Socket { + fn as_inner(&self) -> &c::SOCKET { &self.0 } +} + +impl FromInner<c::SOCKET> for Socket { + fn from_inner(sock: c::SOCKET) -> Socket { Socket(sock) } +} + +impl IntoInner<c::SOCKET> for Socket { + fn into_inner(self) -> c::SOCKET { + let ret = self.0; + mem::forget(self); + ret + } +} diff --git a/ctr-std/src/sys/windows/os.rs b/ctr-std/src/sys/windows/os.rs new file mode 100644 index 0000000..b944824 --- /dev/null +++ b/ctr-std/src/sys/windows/os.rs @@ -0,0 +1,337 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation of `std::os` functionality for Windows + +#![allow(bad_style)] + +use os::windows::prelude::*; + +use error::Error as StdError; +use ffi::{OsString, OsStr}; +use fmt; +use io; +use os::windows::ffi::EncodeWide; +use path::{self, PathBuf}; +use ptr; +use slice; +use sys::{c, cvt}; +use sys::handle::Handle; + +use super::to_u16s; + +pub fn errno() -> i32 { + unsafe { c::GetLastError() as i32 } +} + +/// Gets a detailed string description for the given error number. +pub fn error_string(mut errnum: i32) -> String { + // This value is calculated from the macro + // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT) + let langId = 0x0800 as c::DWORD; + + let mut buf = [0 as c::WCHAR; 2048]; + + unsafe { + let mut module = ptr::null_mut(); + let mut flags = 0; + + // NTSTATUS errors may be encoded as HRESULT, which may returned from + // GetLastError. For more information about Windows error codes, see + // `[MS-ERREF]`: https://msdn.microsoft.com/en-us/library/cc231198.aspx + if (errnum & c::FACILITY_NT_BIT as i32) != 0 { + // format according to https://support.microsoft.com/en-us/help/259693 + const NTDLL_DLL: &'static [u16] = &['N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, + '.' as _, 'D' as _, 'L' as _, 'L' as _, 0]; + module = c::GetModuleHandleW(NTDLL_DLL.as_ptr()); + + if module != ptr::null_mut() { + errnum ^= c::FACILITY_NT_BIT as i32; + flags = c::FORMAT_MESSAGE_FROM_HMODULE; + } + } + + let res = c::FormatMessageW(flags | c::FORMAT_MESSAGE_FROM_SYSTEM | + c::FORMAT_MESSAGE_IGNORE_INSERTS, + module, + errnum as c::DWORD, + langId, + buf.as_mut_ptr(), + buf.len() as c::DWORD, + ptr::null()) as usize; + if res == 0 { + // Sometimes FormatMessageW can fail e.g. system doesn't like langId, + let fm_err = errno(); + return format!("OS Error {} (FormatMessageW() returned error {})", + errnum, fm_err); + } + + match String::from_utf16(&buf[..res]) { + Ok(mut msg) => { + // Trim trailing CRLF inserted by FormatMessageW + let len = msg.trim_right().len(); + msg.truncate(len); + msg + }, + Err(..) => format!("OS Error {} (FormatMessageW() returned \ + invalid UTF-16)", errnum), + } + } +} + +pub struct Env { + base: c::LPWCH, + cur: c::LPWCH, +} + +impl Iterator for Env { + type Item = (OsString, OsString); + + fn next(&mut self) -> Option<(OsString, OsString)> { + loop { + unsafe { + if *self.cur == 0 { return None } + let p = &*self.cur as *const u16; + let mut len = 0; + while *p.offset(len) != 0 { + len += 1; + } + let s = slice::from_raw_parts(p, len as usize); + self.cur = self.cur.offset(len + 1); + + // Windows allows environment variables to start with an equals + // symbol (in any other position, this is the separator between + // variable name and value). Since`s` has at least length 1 at + // this point (because the empty string terminates the array of + // environment variables), we can safely slice. + let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) { + Some(p) => p, + None => continue, + }; + return Some(( + OsStringExt::from_wide(&s[..pos]), + OsStringExt::from_wide(&s[pos+1..]), + )) + } + } + } +} + +impl Drop for Env { + fn drop(&mut self) { + unsafe { c::FreeEnvironmentStringsW(self.base); } + } +} + +pub fn env() -> Env { + unsafe { + let ch = c::GetEnvironmentStringsW(); + if ch as usize == 0 { + panic!("failure getting env string from OS: {}", + io::Error::last_os_error()); + } + Env { base: ch, cur: ch } + } +} + +pub struct SplitPaths<'a> { + data: EncodeWide<'a>, + must_yield: bool, +} + +pub fn split_paths(unparsed: &OsStr) -> SplitPaths { + SplitPaths { + data: unparsed.encode_wide(), + must_yield: true, + } +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option<PathBuf> { + // On Windows, the PATH environment variable is semicolon separated. + // Double quotes are used as a way of introducing literal semicolons + // (since c:\some;dir is a valid Windows path). Double quotes are not + // themselves permitted in path names, so there is no way to escape a + // double quote. Quoted regions can appear in arbitrary locations, so + // + // c:\foo;c:\som"e;di"r;c:\bar + // + // Should parse as [c:\foo, c:\some;dir, c:\bar]. + // + // (The above is based on testing; there is no clear reference available + // for the grammar.) + + + let must_yield = self.must_yield; + self.must_yield = false; + + let mut in_progress = Vec::new(); + let mut in_quote = false; + for b in self.data.by_ref() { + if b == '"' as u16 { + in_quote = !in_quote; + } else if b == ';' as u16 && !in_quote { + self.must_yield = true; + break + } else { + in_progress.push(b) + } + } + + if !must_yield && in_progress.is_empty() { + None + } else { + Some(super::os2path(&in_progress)) + } + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError> + where I: Iterator<Item=T>, T: AsRef<OsStr> +{ + let mut joined = Vec::new(); + let sep = b';' as u16; + + for (i, path) in paths.enumerate() { + let path = path.as_ref(); + if i > 0 { joined.push(sep) } + let v = path.encode_wide().collect::<Vec<u16>>(); + if v.contains(&(b'"' as u16)) { + return Err(JoinPathsError) + } else if v.contains(&sep) { + joined.push(b'"' as u16); + joined.extend_from_slice(&v[..]); + joined.push(b'"' as u16); + } else { + joined.extend_from_slice(&v[..]); + } + } + + Ok(OsStringExt::from_wide(&joined[..])) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + "path segment contains `\"`".fmt(f) + } +} + +impl StdError for JoinPathsError { + fn description(&self) -> &str { "failed to join paths" } +} + +pub fn current_exe() -> io::Result<PathBuf> { + super::fill_utf16_buf(|buf, sz| unsafe { + c::GetModuleFileNameW(ptr::null_mut(), buf, sz) + }, super::os2path) +} + +pub fn getcwd() -> io::Result<PathBuf> { + super::fill_utf16_buf(|buf, sz| unsafe { + c::GetCurrentDirectoryW(sz, buf) + }, super::os2path) +} + +pub fn chdir(p: &path::Path) -> io::Result<()> { + let p: &OsStr = p.as_ref(); + let mut p = p.encode_wide().collect::<Vec<_>>(); + p.push(0); + + cvt(unsafe { + c::SetCurrentDirectoryW(p.as_ptr()) + }).map(|_| ()) +} + +pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> { + let k = to_u16s(k)?; + let res = super::fill_utf16_buf(|buf, sz| unsafe { + c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) + }, |buf| { + OsStringExt::from_wide(buf) + }); + match res { + Ok(value) => Ok(Some(value)), + Err(e) => { + if e.raw_os_error() == Some(c::ERROR_ENVVAR_NOT_FOUND as i32) { + Ok(None) + } else { + Err(e) + } + } + } +} + +pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let k = to_u16s(k)?; + let v = to_u16s(v)?; + + cvt(unsafe { + c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) + }).map(|_| ()) +} + +pub fn unsetenv(n: &OsStr) -> io::Result<()> { + let v = to_u16s(n)?; + cvt(unsafe { + c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) + }).map(|_| ()) +} + +pub fn temp_dir() -> PathBuf { + super::fill_utf16_buf(|buf, sz| unsafe { + c::GetTempPathW(sz, buf) + }, super::os2path).unwrap() +} + +pub fn home_dir() -> Option<PathBuf> { + ::env::var_os("HOME").or_else(|| { + ::env::var_os("USERPROFILE") + }).map(PathBuf::from).or_else(|| unsafe { + let me = c::GetCurrentProcess(); + let mut token = ptr::null_mut(); + if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 { + return None + } + let _handle = Handle::new(token); + super::fill_utf16_buf(|buf, mut sz| { + match c::GetUserProfileDirectoryW(token, buf, &mut sz) { + 0 if c::GetLastError() != c::ERROR_INSUFFICIENT_BUFFER => 0, + 0 => sz, + _ => sz - 1, // sz includes the null terminator + } + }, super::os2path).ok() + }) +} + +pub fn exit(code: i32) -> ! { + unsafe { c::ExitProcess(code as c::UINT) } +} + +pub fn getpid() -> u32 { + unsafe { c::GetCurrentProcessId() as u32 } +} + +#[cfg(test)] +mod tests { + use io::Error; + use sys::c; + + // tests `error_string` above + #[test] + fn ntstatus_error() { + const STATUS_UNSUCCESSFUL: u32 = 0xc000_0001; + assert!(!Error::from_raw_os_error((STATUS_UNSUCCESSFUL | c::FACILITY_NT_BIT) as _) + .to_string().contains("FormatMessageW() returned error")); + } +} diff --git a/ctr-std/src/sys/windows/os_str.rs b/ctr-std/src/sys/windows/os_str.rs new file mode 100644 index 0000000..bcc66b9 --- /dev/null +++ b/ctr-std/src/sys/windows/os_str.rs @@ -0,0 +1,182 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// The underlying OsString/OsStr implementation on Windows is a +/// wrapper around the "WTF-8" encoding; see the `wtf8` module for more. + +use borrow::Cow; +use fmt; +use sys_common::wtf8::{Wtf8, Wtf8Buf}; +use mem; +use rc::Rc; +use sync::Arc; +use sys_common::{AsInner, IntoInner, FromInner}; + +#[derive(Clone, Hash)] +pub struct Buf { + pub inner: Wtf8Buf +} + +impl IntoInner<Wtf8Buf> for Buf { + fn into_inner(self) -> Wtf8Buf { + self.inner + } +} + +impl FromInner<Wtf8Buf> for Buf { + fn from_inner(inner: Wtf8Buf) -> Self { + Buf { inner } + } +} + +impl AsInner<Wtf8> for Buf { + fn as_inner(&self) -> &Wtf8 { + &self.inner + } +} + +impl fmt::Debug for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self.as_slice(), formatter) + } +} + +impl fmt::Display for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self.as_slice(), formatter) + } +} + +pub struct Slice { + pub inner: Wtf8 +} + +impl fmt::Debug for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.inner, formatter) + } +} + +impl fmt::Display for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.inner, formatter) + } +} + +impl Buf { + pub fn with_capacity(capacity: usize) -> Buf { + Buf { + inner: Wtf8Buf::with_capacity(capacity) + } + } + + pub fn clear(&mut self) { + self.inner.clear() + } + + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + pub fn from_string(s: String) -> Buf { + Buf { inner: Wtf8Buf::from_string(s) } + } + + pub fn as_slice(&self) -> &Slice { + unsafe { mem::transmute(self.inner.as_slice()) } + } + + pub fn into_string(self) -> Result<String, Buf> { + self.inner.into_string().map_err(|buf| Buf { inner: buf }) + } + + pub fn push_slice(&mut self, s: &Slice) { + self.inner.push_wtf8(&s.inner) + } + + pub fn reserve(&mut self, additional: usize) { + self.inner.reserve(additional) + } + + pub fn reserve_exact(&mut self, additional: usize) { + self.inner.reserve_exact(additional) + } + + pub fn shrink_to_fit(&mut self) { + self.inner.shrink_to_fit() + } + + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity) + } + + #[inline] + pub fn into_box(self) -> Box<Slice> { + unsafe { mem::transmute(self.inner.into_box()) } + } + + #[inline] + pub fn from_box(boxed: Box<Slice>) -> Buf { + let inner: Box<Wtf8> = unsafe { mem::transmute(boxed) }; + Buf { inner: Wtf8Buf::from_box(inner) } + } + + #[inline] + pub fn into_arc(&self) -> Arc<Slice> { + self.as_slice().into_arc() + } + + #[inline] + pub fn into_rc(&self) -> Rc<Slice> { + self.as_slice().into_rc() + } +} + +impl Slice { + pub fn from_str(s: &str) -> &Slice { + unsafe { mem::transmute(Wtf8::from_str(s)) } + } + + pub fn to_str(&self) -> Option<&str> { + self.inner.as_str() + } + + pub fn to_string_lossy(&self) -> Cow<str> { + self.inner.to_string_lossy() + } + + pub fn to_owned(&self) -> Buf { + let mut buf = Wtf8Buf::with_capacity(self.inner.len()); + buf.push_wtf8(&self.inner); + Buf { inner: buf } + } + + #[inline] + pub fn into_box(&self) -> Box<Slice> { + unsafe { mem::transmute(self.inner.into_box()) } + } + + pub fn empty_box() -> Box<Slice> { + unsafe { mem::transmute(Wtf8::empty_box()) } + } + + #[inline] + pub fn into_arc(&self) -> Arc<Slice> { + let arc = self.inner.into_arc(); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } + } + + #[inline] + pub fn into_rc(&self) -> Rc<Slice> { + let rc = self.inner.into_rc(); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } + } +} diff --git a/ctr-std/src/sys/windows/path.rs b/ctr-std/src/sys/windows/path.rs new file mode 100644 index 0000000..98d62a0 --- /dev/null +++ b/ctr-std/src/sys/windows/path.rs @@ -0,0 +1,106 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use path::Prefix; +use ffi::OsStr; +use mem; + +fn os_str_as_u8_slice(s: &OsStr) -> &[u8] { + unsafe { mem::transmute(s) } +} +unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr { + mem::transmute(s) +} + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'/' || b == b'\\' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'\\' +} + +pub fn parse_prefix<'a>(path: &'a OsStr) -> Option<Prefix> { + use path::Prefix::*; + unsafe { + // The unsafety here stems from converting between &OsStr and &[u8] + // and back. This is safe to do because (1) we only look at ASCII + // contents of the encoding and (2) new &OsStr values are produced + // only from ASCII-bounded slices of existing &OsStr values. + let mut path = os_str_as_u8_slice(path); + + if path.starts_with(br"\\") { + // \\ + path = &path[2..]; + if path.starts_with(br"?\") { + // \\?\ + path = &path[2..]; + if path.starts_with(br"UNC\") { + // \\?\UNC\server\share + path = &path[4..]; + let (server, share) = match parse_two_comps(path, is_verbatim_sep) { + Some((server, share)) => + (u8_slice_as_os_str(server), u8_slice_as_os_str(share)), + None => (u8_slice_as_os_str(path), u8_slice_as_os_str(&[])), + }; + return Some(VerbatimUNC(server, share)); + } else { + // \\?\path + let idx = path.iter().position(|&b| b == b'\\'); + if idx == Some(2) && path[1] == b':' { + let c = path[0]; + if c.is_ascii() && (c as char).is_alphabetic() { + // \\?\C:\ path + return Some(VerbatimDisk(c.to_ascii_uppercase())); + } + } + let slice = &path[..idx.unwrap_or(path.len())]; + return Some(Verbatim(u8_slice_as_os_str(slice))); + } + } else if path.starts_with(b".\\") { + // \\.\path + path = &path[2..]; + let pos = path.iter().position(|&b| b == b'\\'); + let slice = &path[..pos.unwrap_or(path.len())]; + return Some(DeviceNS(u8_slice_as_os_str(slice))); + } + match parse_two_comps(path, is_sep_byte) { + Some((server, share)) if !server.is_empty() && !share.is_empty() => { + // \\server\share + return Some(UNC(u8_slice_as_os_str(server), u8_slice_as_os_str(share))); + } + _ => (), + } + } else if path.get(1) == Some(& b':') { + // C: + let c = path[0]; + if c.is_ascii() && (c as char).is_alphabetic() { + return Some(Disk(c.to_ascii_uppercase())); + } + } + return None; + } + + fn parse_two_comps(mut path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> { + let first = match path.iter().position(|x| f(*x)) { + None => return None, + Some(x) => &path[..x], + }; + path = &path[(first.len() + 1)..]; + let idx = path.iter().position(|x| f(*x)); + let second = &path[..idx.unwrap_or(path.len())]; + Some((first, second)) + } +} + +pub const MAIN_SEP_STR: &'static str = "\\"; +pub const MAIN_SEP: char = '\\'; diff --git a/ctr-std/src/sys/windows/pipe.rs b/ctr-std/src/sys/windows/pipe.rs new file mode 100644 index 0000000..df1dd74 --- /dev/null +++ b/ctr-std/src/sys/windows/pipe.rs @@ -0,0 +1,364 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use os::windows::prelude::*; + +use ffi::OsStr; +use io; +use mem; +use path::Path; +use ptr; +use slice; +use sync::atomic::Ordering::SeqCst; +use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT}; +use sys::c; +use sys::fs::{File, OpenOptions}; +use sys::handle::Handle; +use sys::hashmap_random_keys; + +//////////////////////////////////////////////////////////////////////////////// +// Anonymous pipes +//////////////////////////////////////////////////////////////////////////////// + +pub struct AnonPipe { + inner: Handle, +} + +pub struct Pipes { + pub ours: AnonPipe, + pub theirs: AnonPipe, +} + +/// Although this looks similar to `anon_pipe` in the Unix module it's actually +/// subtly different. Here we'll return two pipes in the `Pipes` return value, +/// but one is intended for "us" where as the other is intended for "someone +/// else". +/// +/// Currently the only use case for this function is pipes for stdio on +/// processes in the standard library, so "ours" is the one that'll stay in our +/// process whereas "theirs" will be inherited to a child. +/// +/// The ours/theirs pipes are *not* specifically readable or writable. Each +/// one only supports a read or a write, but which is which depends on the +/// boolean flag given. If `ours_readable` is true then `ours` is readable where +/// `theirs` is writable. Conversely if `ours_readable` is false then `ours` is +/// writable where `theirs` is readable. +/// +/// Also note that the `ours` pipe is always a handle opened up in overlapped +/// mode. This means that technically speaking it should only ever be used +/// with `OVERLAPPED` instances, but also works out ok if it's only ever used +/// once at a time (which we do indeed guarantee). +pub fn anon_pipe(ours_readable: bool) -> io::Result<Pipes> { + // Note that we specifically do *not* use `CreatePipe` here because + // unfortunately the anonymous pipes returned do not support overlapped + // operations. Instead, we create a "hopefully unique" name and create a + // named pipe which has overlapped operations enabled. + // + // Once we do this, we connect do it as usual via `CreateFileW`, and then + // we return those reader/writer halves. Note that the `ours` pipe return + // value is always the named pipe, whereas `theirs` is just the normal file. + // This should hopefully shield us from child processes which assume their + // stdout is a named pipe, which would indeed be odd! + unsafe { + let ours; + let mut name; + let mut tries = 0; + let mut reject_remote_clients_flag = c::PIPE_REJECT_REMOTE_CLIENTS; + loop { + tries += 1; + name = format!(r"\\.\pipe\__rust_anonymous_pipe1__.{}.{}", + c::GetCurrentProcessId(), + random_number()); + let wide_name = OsStr::new(&name) + .encode_wide() + .chain(Some(0)) + .collect::<Vec<_>>(); + let mut flags = c::FILE_FLAG_FIRST_PIPE_INSTANCE | + c::FILE_FLAG_OVERLAPPED; + if ours_readable { + flags |= c::PIPE_ACCESS_INBOUND; + } else { + flags |= c::PIPE_ACCESS_OUTBOUND; + } + + let handle = c::CreateNamedPipeW(wide_name.as_ptr(), + flags, + c::PIPE_TYPE_BYTE | + c::PIPE_READMODE_BYTE | + c::PIPE_WAIT | + reject_remote_clients_flag, + 1, + 4096, + 4096, + 0, + ptr::null_mut()); + + // We pass the FILE_FLAG_FIRST_PIPE_INSTANCE flag above, and we're + // also just doing a best effort at selecting a unique name. If + // ERROR_ACCESS_DENIED is returned then it could mean that we + // accidentally conflicted with an already existing pipe, so we try + // again. + // + // Don't try again too much though as this could also perhaps be a + // legit error. + // If ERROR_INVALID_PARAMETER is returned, this probably means we're + // running on pre-Vista version where PIPE_REJECT_REMOTE_CLIENTS is + // not supported, so we continue retrying without it. This implies + // reduced security on Windows versions older than Vista by allowing + // connections to this pipe from remote machines. + // Proper fix would increase the number of FFI imports and introduce + // significant amount of Windows XP specific code with no clean + // testing strategy + // for more info see https://github.com/rust-lang/rust/pull/37677 + if handle == c::INVALID_HANDLE_VALUE { + let err = io::Error::last_os_error(); + let raw_os_err = err.raw_os_error(); + if tries < 10 { + if raw_os_err == Some(c::ERROR_ACCESS_DENIED as i32) { + continue + } else if reject_remote_clients_flag != 0 && + raw_os_err == Some(c::ERROR_INVALID_PARAMETER as i32) { + reject_remote_clients_flag = 0; + tries -= 1; + continue + } + } + return Err(err) + } + ours = Handle::new(handle); + break + } + + // Connect to the named pipe we just created. This handle is going to be + // returned in `theirs`, so if `ours` is readable we want this to be + // writable, otherwise if `ours` is writable we want this to be + // readable. + // + // Additionally we don't enable overlapped mode on this because most + // client processes aren't enabled to work with that. + let mut opts = OpenOptions::new(); + opts.write(ours_readable); + opts.read(!ours_readable); + opts.share_mode(0); + let theirs = File::open(Path::new(&name), &opts)?; + let theirs = AnonPipe { inner: theirs.into_handle() }; + + Ok(Pipes { + ours: AnonPipe { inner: ours }, + theirs: AnonPipe { inner: theirs.into_handle() }, + }) + } +} + +fn random_number() -> usize { + static N: AtomicUsize = ATOMIC_USIZE_INIT; + loop { + if N.load(SeqCst) != 0 { + return N.fetch_add(1, SeqCst) + } + + N.store(hashmap_random_keys().0 as usize, SeqCst); + } +} + +impl AnonPipe { + pub fn handle(&self) -> &Handle { &self.inner } + pub fn into_handle(self) -> Handle { self.inner } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.inner.read(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.inner.write(buf) + } +} + +pub fn read2(p1: AnonPipe, + v1: &mut Vec<u8>, + p2: AnonPipe, + v2: &mut Vec<u8>) -> io::Result<()> { + let p1 = p1.into_handle(); + let p2 = p2.into_handle(); + + let mut p1 = AsyncPipe::new(p1, v1)?; + let mut p2 = AsyncPipe::new(p2, v2)?; + let objs = [p1.event.raw(), p2.event.raw()]; + + // In a loop we wait for either pipe's scheduled read operation to complete. + // If the operation completes with 0 bytes, that means EOF was reached, in + // which case we just finish out the other pipe entirely. + // + // Note that overlapped I/O is in general super unsafe because we have to + // be careful to ensure that all pointers in play are valid for the entire + // duration of the I/O operation (where tons of operations can also fail). + // The destructor for `AsyncPipe` ends up taking care of most of this. + loop { + let res = unsafe { + c::WaitForMultipleObjects(2, objs.as_ptr(), c::FALSE, c::INFINITE) + }; + if res == c::WAIT_OBJECT_0 { + if !p1.result()? || !p1.schedule_read()? { + return p2.finish() + } + } else if res == c::WAIT_OBJECT_0 + 1 { + if !p2.result()? || !p2.schedule_read()? { + return p1.finish() + } + } else { + return Err(io::Error::last_os_error()) + } + } +} + +struct AsyncPipe<'a> { + pipe: Handle, + event: Handle, + overlapped: Box<c::OVERLAPPED>, // needs a stable address + dst: &'a mut Vec<u8>, + state: State, +} + +#[derive(PartialEq, Debug)] +enum State { + NotReading, + Reading, + Read(usize), +} + +impl<'a> AsyncPipe<'a> { + fn new(pipe: Handle, dst: &'a mut Vec<u8>) -> io::Result<AsyncPipe<'a>> { + // Create an event which we'll use to coordinate our overlapped + // operations, this event will be used in WaitForMultipleObjects + // and passed as part of the OVERLAPPED handle. + // + // Note that we do a somewhat clever thing here by flagging the + // event as being manually reset and setting it initially to the + // signaled state. This means that we'll naturally fall through the + // WaitForMultipleObjects call above for pipes created initially, + // and the only time an even will go back to "unset" will be once an + // I/O operation is successfully scheduled (what we want). + let event = Handle::new_event(true, true)?; + let mut overlapped: Box<c::OVERLAPPED> = unsafe { + Box::new(mem::zeroed()) + }; + overlapped.hEvent = event.raw(); + Ok(AsyncPipe { + pipe, + overlapped, + event, + dst, + state: State::NotReading, + }) + } + + /// Executes an overlapped read operation. + /// + /// Must not currently be reading, and returns whether the pipe is currently + /// at EOF or not. If the pipe is not at EOF then `result()` must be called + /// to complete the read later on (may block), but if the pipe is at EOF + /// then `result()` should not be called as it will just block forever. + fn schedule_read(&mut self) -> io::Result<bool> { + assert_eq!(self.state, State::NotReading); + let amt = unsafe { + let slice = slice_to_end(self.dst); + self.pipe.read_overlapped(slice, &mut *self.overlapped)? + }; + + // If this read finished immediately then our overlapped event will + // remain signaled (it was signaled coming in here) and we'll progress + // down to the method below. + // + // Otherwise the I/O operation is scheduled and the system set our event + // to not signaled, so we flag ourselves into the reading state and move + // on. + self.state = match amt { + Some(0) => return Ok(false), + Some(amt) => State::Read(amt), + None => State::Reading, + }; + Ok(true) + } + + /// Wait for the result of the overlapped operation previously executed. + /// + /// Takes a parameter `wait` which indicates if this pipe is currently being + /// read whether the function should block waiting for the read to complete. + /// + /// Return values: + /// + /// * `true` - finished any pending read and the pipe is not at EOF (keep + /// going) + /// * `false` - finished any pending read and pipe is at EOF (stop issuing + /// reads) + fn result(&mut self) -> io::Result<bool> { + let amt = match self.state { + State::NotReading => return Ok(true), + State::Reading => { + self.pipe.overlapped_result(&mut *self.overlapped, true)? + } + State::Read(amt) => amt, + }; + self.state = State::NotReading; + unsafe { + let len = self.dst.len(); + self.dst.set_len(len + amt); + } + Ok(amt != 0) + } + + /// Finishes out reading this pipe entirely. + /// + /// Waits for any pending and schedule read, and then calls `read_to_end` + /// if necessary to read all the remaining information. + fn finish(&mut self) -> io::Result<()> { + while self.result()? && self.schedule_read()? { + // ... + } + Ok(()) + } +} + +impl<'a> Drop for AsyncPipe<'a> { + fn drop(&mut self) { + match self.state { + State::Reading => {} + _ => return, + } + + // If we have a pending read operation, then we have to make sure that + // it's *done* before we actually drop this type. The kernel requires + // that the `OVERLAPPED` and buffer pointers are valid for the entire + // I/O operation. + // + // To do that, we call `CancelIo` to cancel any pending operation, and + // if that succeeds we wait for the overlapped result. + // + // If anything here fails, there's not really much we can do, so we leak + // the buffer/OVERLAPPED pointers to ensure we're at least memory safe. + if self.pipe.cancel_io().is_err() || self.result().is_err() { + let buf = mem::replace(self.dst, Vec::new()); + let overlapped = Box::new(unsafe { mem::zeroed() }); + let overlapped = mem::replace(&mut self.overlapped, overlapped); + mem::forget((buf, overlapped)); + } + } +} + +unsafe fn slice_to_end(v: &mut Vec<u8>) -> &mut [u8] { + if v.capacity() == 0 { + v.reserve(16); + } + if v.capacity() == v.len() { + v.reserve(1); + } + slice::from_raw_parts_mut(v.as_mut_ptr().offset(v.len() as isize), + v.capacity() - v.len()) +} diff --git a/ctr-std/src/sys/windows/process.rs b/ctr-std/src/sys/windows/process.rs new file mode 100644 index 0000000..be442f4 --- /dev/null +++ b/ctr-std/src/sys/windows/process.rs @@ -0,0 +1,585 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "process_internals", issue = "0")] + +use collections::BTreeMap; +use env::split_paths; +use env; +use ffi::{OsString, OsStr}; +use fmt; +use fs; +use io::{self, Error, ErrorKind}; +use libc::{c_void, EXIT_SUCCESS, EXIT_FAILURE}; +use mem; +use os::windows::ffi::OsStrExt; +use path::Path; +use ptr; +use sys::mutex::Mutex; +use sys::c; +use sys::fs::{OpenOptions, File}; +use sys::handle::Handle; +use sys::pipe::{self, AnonPipe}; +use sys::stdio; +use sys::cvt; +use sys_common::{AsInner, FromInner, IntoInner}; +use sys_common::process::{CommandEnv, EnvKey}; +use borrow::Borrow; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +#[doc(hidden)] +pub struct WindowsEnvKey(OsString); + +impl From<OsString> for WindowsEnvKey { + fn from(k: OsString) -> Self { + let mut buf = k.into_inner().into_inner(); + buf.make_ascii_uppercase(); + WindowsEnvKey(FromInner::from_inner(FromInner::from_inner(buf))) + } +} + +impl From<WindowsEnvKey> for OsString { + fn from(k: WindowsEnvKey) -> Self { k.0 } +} + +impl Borrow<OsStr> for WindowsEnvKey { + fn borrow(&self) -> &OsStr { &self.0 } +} + +impl AsRef<OsStr> for WindowsEnvKey { + fn as_ref(&self) -> &OsStr { &self.0 } +} + +impl EnvKey for WindowsEnvKey {} + + +fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> { + if str.as_ref().encode_wide().any(|b| b == 0) { + Err(io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data")) + } else { + Ok(str) + } +} + +pub struct Command { + program: OsString, + args: Vec<OsString>, + env: CommandEnv<WindowsEnvKey>, + cwd: Option<OsString>, + flags: u32, + detach: bool, // not currently exposed in std::process + stdin: Option<Stdio>, + stdout: Option<Stdio>, + stderr: Option<Stdio>, +} + +pub enum Stdio { + Inherit, + Null, + MakePipe, + Handle(Handle), +} + +pub struct StdioPipes { + pub stdin: Option<AnonPipe>, + pub stdout: Option<AnonPipe>, + pub stderr: Option<AnonPipe>, +} + +struct DropGuard<'a> { + lock: &'a Mutex, +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + Command { + program: program.to_os_string(), + args: Vec::new(), + env: Default::default(), + cwd: None, + flags: 0, + detach: false, + stdin: None, + stdout: None, + stderr: None, + } + } + + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(arg.to_os_string()) + } + pub fn env_mut(&mut self) -> &mut CommandEnv<WindowsEnvKey> { + &mut self.env + } + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(dir.to_os_string()) + } + pub fn stdin(&mut self, stdin: Stdio) { + self.stdin = Some(stdin); + } + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } + pub fn creation_flags(&mut self, flags: u32) { + self.flags = flags; + } + + pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) + -> io::Result<(Process, StdioPipes)> { + let maybe_env = self.env.capture_if_changed(); + // 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 = maybe_env.as_ref().and_then(|env| { + if let Some(v) = env.get(OsStr::new("PATH")) { + // Split the value and test each path to see if the + // program exists. + for path in split_paths(&v) { + let path = path.join(self.program.to_str().unwrap()) + .with_extension(env::consts::EXE_EXTENSION); + if fs::metadata(&path).is_ok() { + return Some(path.into_os_string()) + } + } + } + None + }); + + let mut si = zeroed_startupinfo(); + si.cb = mem::size_of::<c::STARTUPINFO>() as c::DWORD; + si.dwFlags = c::STARTF_USESTDHANDLES; + + let program = program.as_ref().unwrap_or(&self.program); + let mut cmd_str = make_command_line(program, &self.args)?; + cmd_str.push(0); // add null terminator + + // stolen from the libuv code. + let mut flags = self.flags | c::CREATE_UNICODE_ENVIRONMENT; + if self.detach { + flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP; + } + + let (envp, _data) = make_envp(maybe_env)?; + let (dirp, _data) = make_dirp(self.cwd.as_ref())?; + let mut pi = zeroed_process_information(); + + // Prepare all stdio handles to be inherited by the child. This + // currently involves duplicating any existing ones with the ability to + // be inherited by child processes. Note, however, that once an + // inheritable handle is created, *any* spawned child will inherit that + // handle. We only want our own child to inherit this handle, so we wrap + // the remaining portion of this spawn in a mutex. + // + // For more information, msdn also has an article about this race: + // http://support.microsoft.com/kb/315939 + static CREATE_PROCESS_LOCK: Mutex = Mutex::new(); + let _guard = DropGuard::new(&CREATE_PROCESS_LOCK); + + let mut pipes = StdioPipes { + stdin: None, + stdout: None, + stderr: None, + }; + let null = Stdio::Null; + let default_stdin = if needs_stdin {&default} else {&null}; + let stdin = self.stdin.as_ref().unwrap_or(default_stdin); + let stdout = self.stdout.as_ref().unwrap_or(&default); + let stderr = self.stderr.as_ref().unwrap_or(&default); + let stdin = stdin.to_handle(c::STD_INPUT_HANDLE, &mut pipes.stdin)?; + let stdout = stdout.to_handle(c::STD_OUTPUT_HANDLE, + &mut pipes.stdout)?; + let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, + &mut pipes.stderr)?; + si.hStdInput = stdin.raw(); + si.hStdOutput = stdout.raw(); + si.hStdError = stderr.raw(); + + unsafe { + cvt(c::CreateProcessW(ptr::null(), + cmd_str.as_mut_ptr(), + ptr::null_mut(), + ptr::null_mut(), + c::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) }, pipes)) + } + +} + +impl fmt::Debug for Command { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.program)?; + for arg in &self.args { + write!(f, " {:?}", arg)?; + } + Ok(()) + } +} + +impl<'a> DropGuard<'a> { + fn new(lock: &'a Mutex) -> DropGuard<'a> { + unsafe { + lock.lock(); + DropGuard { lock: lock } + } + } +} + +impl<'a> Drop for DropGuard<'a> { + fn drop(&mut self) { + unsafe { + self.lock.unlock(); + } + } +} + +impl Stdio { + fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option<AnonPipe>) + -> io::Result<Handle> { + match *self { + // If no stdio handle is available, then inherit means that it + // should still be unavailable so propagate the + // INVALID_HANDLE_VALUE. + Stdio::Inherit => { + match stdio::get(stdio_id) { + Ok(io) => { + let io = Handle::new(io.handle()); + let ret = io.duplicate(0, true, + c::DUPLICATE_SAME_ACCESS); + io.into_raw(); + return ret + } + Err(..) => Ok(Handle::new(c::INVALID_HANDLE_VALUE)), + } + } + + Stdio::MakePipe => { + let ours_readable = stdio_id != c::STD_INPUT_HANDLE; + let pipes = pipe::anon_pipe(ours_readable)?; + *pipe = Some(pipes.ours); + cvt(unsafe { + c::SetHandleInformation(pipes.theirs.handle().raw(), + c::HANDLE_FLAG_INHERIT, + c::HANDLE_FLAG_INHERIT) + })?; + Ok(pipes.theirs.into_handle()) + } + + Stdio::Handle(ref handle) => { + handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS) + } + + // Open up a reference to NUL with appropriate read/write + // permissions as well as the ability to be inherited to child + // processes (as this is about to be inherited). + Stdio::Null => { + let size = mem::size_of::<c::SECURITY_ATTRIBUTES>(); + let mut sa = c::SECURITY_ATTRIBUTES { + nLength: size as c::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() + }) + } + } + } +} + +impl From<AnonPipe> for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + Stdio::Handle(pipe.into_handle()) + } +} + +impl From<File> for Stdio { + fn from(file: File) -> Stdio { + Stdio::Handle(file.into_handle()) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// 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, +} + +impl Process { + pub fn kill(&mut self) -> io::Result<()> { + cvt(unsafe { + c::TerminateProcess(self.handle.raw(), 1) + })?; + Ok(()) + } + + pub fn id(&self) -> u32 { + unsafe { + c::GetProcessId(self.handle.raw()) as u32 + } + } + + pub fn wait(&mut self) -> io::Result<ExitStatus> { + unsafe { + let res = c::WaitForSingleObject(self.handle.raw(), c::INFINITE); + if res != c::WAIT_OBJECT_0 { + return Err(Error::last_os_error()) + } + let mut status = 0; + cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?; + Ok(ExitStatus(status)) + } + } + + pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { + unsafe { + match c::WaitForSingleObject(self.handle.raw(), 0) { + c::WAIT_OBJECT_0 => {} + c::WAIT_TIMEOUT => { + return Ok(None); + } + _ => return Err(io::Error::last_os_error()), + } + let mut status = 0; + cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?; + Ok(Some(ExitStatus(status))) + } + } + + pub fn handle(&self) -> &Handle { &self.handle } + + pub fn into_handle(self) -> Handle { self.handle } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatus(c::DWORD); + +impl ExitStatus { + pub fn success(&self) -> bool { + self.0 == 0 + } + pub fn code(&self) -> Option<i32> { + Some(self.0 as i32) + } +} + +impl From<c::DWORD> for ExitStatus { + fn from(u: c::DWORD) -> ExitStatus { + ExitStatus(u) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "exit code: {}", self.0) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(c::DWORD); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _); + pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _); + + #[inline] + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} + +fn zeroed_startupinfo() -> c::STARTUPINFO { + c::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: c::INVALID_HANDLE_VALUE, + hStdOutput: c::INVALID_HANDLE_VALUE, + hStdError: c::INVALID_HANDLE_VALUE, + } +} + +fn zeroed_process_information() -> c::PROCESS_INFORMATION { + c::PROCESS_INFORMATION { + hProcess: ptr::null_mut(), + hThread: ptr::null_mut(), + dwProcessId: 0, + dwThreadId: 0 + } +} + +// Produces a wide string *without terminating null*; returns an error if +// `prog` or any of the `args` contain a nul. +fn make_command_line(prog: &OsStr, args: &[OsString]) -> io::Result<Vec<u16>> { + // Encode the command and arguments in a command line string such + // that the spawned process may recover them using CommandLineToArgvW. + let mut cmd: Vec<u16> = Vec::new(); + // Always quote the program name so CreateProcess doesn't interpret args as + // part of the name if the binary wasn't found first time. + append_arg(&mut cmd, prog, true)?; + for arg in args { + cmd.push(' ' as u16); + append_arg(&mut cmd, arg, false)?; + } + return Ok(cmd); + + fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr, force_quotes: bool) -> io::Result<()> { + // 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. + ensure_no_nuls(arg)?; + let arg_bytes = &arg.as_inner().inner.as_inner(); + let quote = force_quotes || 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); + } + Ok(()) + } +} + +fn make_envp(maybe_env: Option<BTreeMap<WindowsEnvKey, OsString>>) + -> io::Result<(*mut c_void, Vec<u16>)> { + // 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. + if let Some(env) = maybe_env { + let mut blk = Vec::new(); + + for (k, v) in env { + blk.extend(ensure_no_nuls(k.0)?.encode_wide()); + blk.push('=' as u16); + blk.extend(ensure_no_nuls(v)?.encode_wide()); + blk.push(0); + } + blk.push(0); + Ok((blk.as_mut_ptr() as *mut c_void, blk)) + } else { + Ok((ptr::null_mut(), Vec::new())) + } +} + +fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> { + + match d { + Some(dir) => { + let mut dir_str: Vec<u16> = ensure_no_nuls(dir)?.encode_wide().collect(); + dir_str.push(0); + Ok((dir_str.as_ptr(), dir_str)) + }, + None => Ok((ptr::null(), Vec::new())) + } +} + +#[cfg(test)] +mod tests { + use ffi::{OsStr, OsString}; + use super::make_command_line; + + #[test] + fn test_make_command_line() { + fn test_wrapper(prog: &str, args: &[&str]) -> String { + let command_line = &make_command_line(OsStr::new(prog), + &args.iter() + .map(|a| OsString::from(a)) + .collect::<Vec<OsString>>()) + .unwrap(); + String::from_utf16(command_line).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/ctr-std/src/sys/windows/rand.rs b/ctr-std/src/sys/windows/rand.rs new file mode 100644 index 0000000..2623236 --- /dev/null +++ b/ctr-std/src/sys/windows/rand.rs @@ -0,0 +1,26 @@ +// Copyright 2013-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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use mem; +use sys::c; + +pub fn hashmap_random_keys() -> (u64, u64) { + let mut v = (0, 0); + let ret = unsafe { + c::RtlGenRandom(&mut v as *mut _ as *mut u8, + mem::size_of_val(&v) as c::ULONG) + }; + if ret == 0 { + panic!("couldn't generate random bytes: {}", + io::Error::last_os_error()); + } + return v +} diff --git a/ctr-std/src/sys/windows/rwlock.rs b/ctr-std/src/sys/windows/rwlock.rs new file mode 100644 index 0000000..3e81ebf --- /dev/null +++ b/ctr-std/src/sys/windows/rwlock.rs @@ -0,0 +1,52 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cell::UnsafeCell; +use sys::c; + +pub struct RWLock { inner: UnsafeCell<c::SRWLOCK> } + +unsafe impl Send for RWLock {} +unsafe impl Sync for RWLock {} + +impl RWLock { + pub const fn new() -> RWLock { + RWLock { inner: UnsafeCell::new(c::SRWLOCK_INIT) } + } + #[inline] + pub unsafe fn read(&self) { + c::AcquireSRWLockShared(self.inner.get()) + } + #[inline] + pub unsafe fn try_read(&self) -> bool { + c::TryAcquireSRWLockShared(self.inner.get()) != 0 + } + #[inline] + pub unsafe fn write(&self) { + c::AcquireSRWLockExclusive(self.inner.get()) + } + #[inline] + pub unsafe fn try_write(&self) -> bool { + c::TryAcquireSRWLockExclusive(self.inner.get()) != 0 + } + #[inline] + pub unsafe fn read_unlock(&self) { + c::ReleaseSRWLockShared(self.inner.get()) + } + #[inline] + pub unsafe fn write_unlock(&self) { + c::ReleaseSRWLockExclusive(self.inner.get()) + } + + #[inline] + pub unsafe fn destroy(&self) { + // ... + } +} diff --git a/ctr-std/src/sys/windows/stack_overflow.rs b/ctr-std/src/sys/windows/stack_overflow.rs new file mode 100644 index 0000000..4a406d7 --- /dev/null +++ b/ctr-std/src/sys/windows/stack_overflow.rs @@ -0,0 +1,52 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg_attr(test, allow(dead_code))] + +use sys_common::util::report_overflow; +use sys::c; + +pub struct Handler; + +impl Handler { + pub unsafe fn new() -> Handler { + // This API isn't available on XP, so don't panic in that case and just + // pray it works out ok. + if c::SetThreadStackGuarantee(&mut 0x5000) == 0 { + if c::GetLastError() as u32 != c::ERROR_CALL_NOT_IMPLEMENTED as u32 { + panic!("failed to reserve stack space for exception handling"); + } + } + Handler + } +} + +extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) + -> c::LONG { + unsafe { + let rec = &(*(*ExceptionInfo).ExceptionRecord); + let code = rec.ExceptionCode; + + if code == c::EXCEPTION_STACK_OVERFLOW { + report_overflow(); + } + c::EXCEPTION_CONTINUE_SEARCH + } +} + +pub unsafe fn init() { + if c::AddVectoredExceptionHandler(0, vectored_handler).is_null() { + panic!("failed to install exception handler"); + } + // Set the thread stack guarantee for the main thread. + let _h = Handler::new(); +} + +pub unsafe fn cleanup() {} diff --git a/ctr-std/src/sys/windows/stdio.rs b/ctr-std/src/sys/windows/stdio.rs new file mode 100644 index 0000000..81b89da --- /dev/null +++ b/ctr-std/src/sys/windows/stdio.rs @@ -0,0 +1,233 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(issue = "0", feature = "windows_stdio")] + +use io::prelude::*; + +use cmp; +use io::{self, Cursor}; +use ptr; +use str; +use sync::Mutex; +use sys::c; +use sys::cvt; +use sys::handle::Handle; + +pub enum Output { + Console(c::HANDLE), + Pipe(c::HANDLE), +} + +pub struct Stdin { + utf8: Mutex<io::Cursor<Vec<u8>>>, +} +pub struct Stdout; +pub struct Stderr; + +pub fn get(handle: c::DWORD) -> io::Result<Output> { + let handle = unsafe { c::GetStdHandle(handle) }; + if handle == c::INVALID_HANDLE_VALUE { + Err(io::Error::last_os_error()) + } else if handle.is_null() { + Err(io::Error::from_raw_os_error(c::ERROR_INVALID_HANDLE as i32)) + } else { + let mut out = 0; + match unsafe { c::GetConsoleMode(handle, &mut out) } { + 0 => Ok(Output::Pipe(handle)), + _ => Ok(Output::Console(handle)), + } + } +} + +fn write(handle: c::DWORD, data: &[u8]) -> io::Result<usize> { + let handle = match try!(get(handle)) { + Output::Console(c) => c, + Output::Pipe(p) => { + let handle = Handle::new(p); + let ret = handle.write(data); + handle.into_raw(); + return ret + } + }; + + // As with stdin on windows, stdout often can't handle writes of large + // sizes. For an example, see #14940. For this reason, don't try to + // write the entire output buffer on windows. + // + // For some other references, it appears that this problem has been + // encountered by others [1] [2]. We choose the number 8K just because + // libuv does the same. + // + // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232 + // [2]: http://www.mail-archive.com/[email protected]/msg00661.html + const OUT_MAX: usize = 8192; + let len = cmp::min(data.len(), OUT_MAX); + let utf8 = match str::from_utf8(&data[..len]) { + Ok(s) => s, + Err(ref e) if e.valid_up_to() == 0 => return Err(invalid_encoding()), + Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(), + }; + let utf16 = utf8.encode_utf16().collect::<Vec<u16>>(); + let mut written = 0; + cvt(unsafe { + c::WriteConsoleW(handle, + utf16.as_ptr() as c::LPCVOID, + utf16.len() as u32, + &mut written, + ptr::null_mut()) + })?; + + // FIXME if this only partially writes the utf16 buffer then we need to + // figure out how many bytes of `data` were actually written + assert_eq!(written as usize, utf16.len()); + Ok(utf8.len()) +} + +impl Stdin { + pub fn new() -> io::Result<Stdin> { + Ok(Stdin { + utf8: Mutex::new(Cursor::new(Vec::new())), + }) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + let handle = match try!(get(c::STD_INPUT_HANDLE)) { + Output::Console(c) => c, + Output::Pipe(p) => { + let handle = Handle::new(p); + let ret = handle.read(buf); + handle.into_raw(); + return ret + } + }; + let mut utf8 = self.utf8.lock().unwrap(); + // Read more if the buffer is empty + if utf8.position() as usize == utf8.get_ref().len() { + let mut utf16 = vec![0u16; 0x1000]; + let mut num = 0; + let mut input_control = readconsole_input_control(CTRL_Z_MASK); + cvt(unsafe { + c::ReadConsoleW(handle, + utf16.as_mut_ptr() as c::LPVOID, + utf16.len() as u32, + &mut num, + &mut input_control as c::PCONSOLE_READCONSOLE_CONTROL) + })?; + utf16.truncate(num as usize); + // FIXME: what to do about this data that has already been read? + let mut data = match String::from_utf16(&utf16) { + Ok(utf8) => utf8.into_bytes(), + Err(..) => return Err(invalid_encoding()), + }; + if let Some(&last_byte) = data.last() { + if last_byte == CTRL_Z { + data.pop(); + } + } + *utf8 = Cursor::new(data); + } + + // MemReader shouldn't error here since we just filled it + utf8.read(buf) + } + + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { + let mut me = self; + (&mut me).read_to_end(buf) + } +} + +#[unstable(reason = "not public", issue = "0", feature = "fd_read")] +impl<'a> Read for &'a Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + (**self).read(buf) + } +} + +impl Stdout { + pub fn new() -> io::Result<Stdout> { + Ok(Stdout) + } + + pub fn write(&self, data: &[u8]) -> io::Result<usize> { + write(c::STD_OUTPUT_HANDLE, data) + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub fn new() -> io::Result<Stderr> { + Ok(Stderr) + } + + pub fn write(&self, data: &[u8]) -> io::Result<usize> { + write(c::STD_ERROR_HANDLE, data) + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } +} + +// FIXME: right now this raw stderr handle is used in a few places because +// std::io::stderr_raw isn't exposed, but once that's exposed this impl +// should go away +impl io::Write for Stderr { + fn write(&mut self, data: &[u8]) -> io::Result<usize> { + Stderr::write(self, data) + } + + fn flush(&mut self) -> io::Result<()> { + Stderr::flush(self) + } +} + +impl Output { + pub fn handle(&self) -> c::HANDLE { + match *self { + Output::Console(c) => c, + Output::Pipe(c) => c, + } + } +} + +fn invalid_encoding() -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, "text was not valid unicode") +} + +fn readconsole_input_control(wakeup_mask: c::ULONG) -> c::CONSOLE_READCONSOLE_CONTROL { + c::CONSOLE_READCONSOLE_CONTROL { + nLength: ::mem::size_of::<c::CONSOLE_READCONSOLE_CONTROL>() as c::ULONG, + nInitialChars: 0, + dwCtrlWakeupMask: wakeup_mask, + dwControlKeyState: 0, + } +} + +const CTRL_Z: u8 = 0x1A; +const CTRL_Z_MASK: c::ULONG = 0x4000000; //1 << 0x1A + +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(c::ERROR_INVALID_HANDLE as i32) +} + +// The default buffer capacity is 64k, but apparently windows +// doesn't like 64k reads on stdin. See #13304 for details, but the +// idea is that on windows we use a slightly smaller buffer that's +// been seen to be acceptable. +pub const STDIN_BUF_SIZE: usize = 8 * 1024; + +pub fn stderr_prints_nothing() -> bool { + false +} diff --git a/ctr-std/src/sys/windows/thread.rs b/ctr-std/src/sys/windows/thread.rs new file mode 100644 index 0000000..b6f6330 --- /dev/null +++ b/ctr-std/src/sys/windows/thread.rs @@ -0,0 +1,100 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use boxed::FnBox; +use io; +use ffi::CStr; +use mem; +use libc::c_void; +use ptr; +use sys::c; +use sys::handle::Handle; +use sys_common::thread::*; +use time::Duration; + +use super::to_u16s; + +pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; + +pub struct Thread { + handle: Handle +} + +impl Thread { + pub unsafe fn new<'a>(stack: usize, p: Box<FnBox() + 'a>) + -> io::Result<Thread> { + let p = box p; + + // FIXME On UNIX, we guard against stack sizes that are too small but + // that's because pthreads enforces that stacks are at least + // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's + // just that below a certain threshold you can't do anything useful. + // That threshold is application and architecture-specific, however. + // Round up to the next 64 kB because that's what the NT kernel does, + // might as well make it explicit. + let stack_size = (stack + 0xfffe) & (!0xfffe); + let ret = c::CreateThread(ptr::null_mut(), stack_size, + thread_start, &*p as *const _ as *mut _, + 0, ptr::null_mut()); + + return if ret as usize == 0 { + Err(io::Error::last_os_error()) + } else { + mem::forget(p); // ownership passed to CreateThread + Ok(Thread { handle: Handle::new(ret) }) + }; + + extern "system" fn thread_start(main: *mut c_void) -> c::DWORD { + unsafe { start_thread(main as *mut u8); } + 0 + } + } + + pub fn set_name(name: &CStr) { + if let Ok(utf8) = name.to_str() { + if let Ok(utf16) = to_u16s(utf8) { + unsafe { c::SetThreadDescription(c::GetCurrentThread(), utf16.as_ptr()); }; + }; + }; + } + + pub fn join(self) { + let rc = unsafe { c::WaitForSingleObject(self.handle.raw(), c::INFINITE) }; + if rc == c::WAIT_FAILED { + panic!("failed to join on thread: {}", + io::Error::last_os_error()); + } + } + + pub fn yield_now() { + // This function will return 0 if there are no other threads to execute, + // but this also means that the yield was useless so this isn't really a + // case that needs to be worried about. + unsafe { c::SwitchToThread(); } + } + + pub fn sleep(dur: Duration) { + unsafe { + c::Sleep(super::dur2timeout(dur)) + } + } + + pub fn handle(&self) -> &Handle { &self.handle } + + pub fn into_handle(self) -> Handle { self.handle } +} + +#[cfg_attr(test, allow(dead_code))] +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option<Guard> { None } + pub unsafe fn init() -> Option<Guard> { None } + pub unsafe fn deinit() {} +} diff --git a/ctr-std/src/sys/windows/thread_local.rs b/ctr-std/src/sys/windows/thread_local.rs new file mode 100644 index 0000000..cdad320 --- /dev/null +++ b/ctr-std/src/sys/windows/thread_local.rs @@ -0,0 +1,251 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use mem; +use ptr; +use sync::atomic::AtomicPtr; +use sync::atomic::Ordering::SeqCst; +use sys::c; + +pub type Key = c::DWORD; +pub type Dtor = unsafe extern fn(*mut u8); + +// Turns out, like pretty much everything, Windows is pretty close the +// functionality that Unix provides, but slightly different! In the case of +// TLS, Windows does not provide an API to provide a destructor for a TLS +// variable. This ends up being pretty crucial to this implementation, so we +// need a way around this. +// +// The solution here ended up being a little obscure, but fear not, the +// internet has informed me [1][2] that this solution is not unique (no way +// I could have thought of it as well!). The key idea is to insert some hook +// somewhere to run arbitrary code on thread termination. With this in place +// we'll be able to run anything we like, including all TLS destructors! +// +// To accomplish this feat, we perform a number of threads, all contained +// within this module: +// +// * All TLS destructors are tracked by *us*, not the windows runtime. This +// means that we have a global list of destructors for each TLS key that +// we know about. +// * When a thread exits, we run over the entire list and run dtors for all +// non-null keys. This attempts to match Unix semantics in this regard. +// +// This ends up having the overhead of using a global list, having some +// locks here and there, and in general just adding some more code bloat. We +// attempt to optimize runtime by forgetting keys that don't have +// destructors, but this only gets us so far. +// +// For more details and nitty-gritty, see the code sections below! +// +// [1]: http://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way +// [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base +// /threading/thread_local_storage_win.cc#L42 + +// ------------------------------------------------------------------------- +// Native bindings +// +// This section is just raw bindings to the native functions that Windows +// provides, There's a few extra calls to deal with destructors. + +#[inline] +pub unsafe fn create(dtor: Option<Dtor>) -> Key { + let key = c::TlsAlloc(); + assert!(key != c::TLS_OUT_OF_INDEXES); + if let Some(f) = dtor { + register_dtor(key, f); + } + return key; +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + let r = c::TlsSetValue(key, value as c::LPVOID); + debug_assert!(r != 0); +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + c::TlsGetValue(key) as *mut u8 +} + +#[inline] +pub unsafe fn destroy(_key: Key) { + rtabort!("can't destroy tls keys on windows") +} + +#[inline] +pub fn requires_synchronized_create() -> bool { + true +} + +// ------------------------------------------------------------------------- +// Dtor registration +// +// Windows has no native support for running destructors so we manage our own +// list of destructors to keep track of how to destroy keys. We then install a +// callback later to get invoked whenever a thread exits, running all +// appropriate destructors. +// +// Currently unregistration from this list is not supported. A destructor can be +// registered but cannot be unregistered. There's various simplifying reasons +// for doing this, the big ones being: +// +// 1. Currently we don't even support deallocating TLS keys, so normal operation +// doesn't need to deallocate a destructor. +// 2. There is no point in time where we know we can unregister a destructor +// because it could always be getting run by some remote thread. +// +// Typically processes have a statically known set of TLS keys which is pretty +// small, and we'd want to keep this memory alive for the whole process anyway +// really. +// +// Perhaps one day we can fold the `Box` here into a static allocation, +// expanding the `StaticKey` structure to contain not only a slot for the TLS +// key but also a slot for the destructor queue on windows. An optimization for +// another day! + +static DTORS: AtomicPtr<Node> = AtomicPtr::new(ptr::null_mut()); + +struct Node { + dtor: Dtor, + key: Key, + next: *mut Node, +} + +unsafe fn register_dtor(key: Key, dtor: Dtor) { + let mut node = Box::new(Node { + key, + dtor, + next: ptr::null_mut(), + }); + + let mut head = DTORS.load(SeqCst); + loop { + node.next = head; + match DTORS.compare_exchange(head, &mut *node, SeqCst, SeqCst) { + Ok(_) => return mem::forget(node), + Err(cur) => head = cur, + } + } +} + +// ------------------------------------------------------------------------- +// Where the Magic (TM) Happens +// +// If you're looking at this code, and wondering "what is this doing?", +// you're not alone! I'll try to break this down step by step: +// +// # What's up with CRT$XLB? +// +// For anything about TLS destructors to work on Windows, we have to be able +// to run *something* when a thread exits. To do so, we place a very special +// static in a very special location. If this is encoded in just the right +// way, the kernel's loader is apparently nice enough to run some function +// of ours whenever a thread exits! How nice of the kernel! +// +// Lots of detailed information can be found in source [1] above, but the +// gist of it is that this is leveraging a feature of Microsoft's PE format +// (executable format) which is not actually used by any compilers today. +// This apparently translates to any callbacks in the ".CRT$XLB" section +// being run on certain events. +// +// So after all that, we use the compiler's #[link_section] feature to place +// a callback pointer into the magic section so it ends up being called. +// +// # What's up with this callback? +// +// The callback specified receives a number of parameters from... someone! +// (the kernel? the runtime? I'm not quite sure!) There are a few events that +// this gets invoked for, but we're currently only interested on when a +// thread or a process "detaches" (exits). The process part happens for the +// last thread and the thread part happens for any normal thread. +// +// # Ok, what's up with running all these destructors? +// +// This will likely need to be improved over time, but this function +// attempts a "poor man's" destructor callback system. Once we've got a list +// of what to run, we iterate over all keys, check their values, and then run +// destructors if the values turn out to be non null (setting them to null just +// beforehand). We do this a few times in a loop to basically match Unix +// semantics. If we don't reach a fixed point after a short while then we just +// inevitably leak something most likely. +// +// # The article mentions weird stuff about "/INCLUDE"? +// +// It sure does! Specifically we're talking about this quote: +// +// The Microsoft run-time library facilitates this process by defining a +// memory image of the TLS Directory and giving it the special name +// “__tls_used” (Intel x86 platforms) or “_tls_used” (other platforms). The +// linker looks for this memory image and uses the data there to create the +// TLS Directory. Other compilers that support TLS and work with the +// Microsoft linker must use this same technique. +// +// Basically what this means is that if we want support for our TLS +// destructors/our hook being called then we need to make sure the linker does +// not omit this symbol. Otherwise it will omit it and our callback won't be +// wired up. +// +// We don't actually use the `/INCLUDE` linker flag here like the article +// mentions because the Rust compiler doesn't propagate linker flags, but +// instead we use a shim function which performs a volatile 1-byte load from +// the address of the symbol to ensure it sticks around. + +#[link_section = ".CRT$XLB"] +#[allow(dead_code, unused_variables)] +#[used] // we don't want LLVM eliminating this symbol for any reason, and + // when the symbol makes it to the linker the linker will take over +pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, + c::LPVOID) = + on_tls_callback; + +#[allow(dead_code, unused_variables)] +unsafe extern "system" fn on_tls_callback(h: c::LPVOID, + dwReason: c::DWORD, + pv: c::LPVOID) { + if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH { + run_dtors(); + } + + // See comments above for what this is doing. Note that we don't need this + // trickery on GNU windows, just on MSVC. + reference_tls_used(); + #[cfg(target_env = "msvc")] + unsafe fn reference_tls_used() { + extern { static _tls_used: u8; } + ::intrinsics::volatile_load(&_tls_used); + } + #[cfg(not(target_env = "msvc"))] + unsafe fn reference_tls_used() {} +} + +#[allow(dead_code)] // actually called above +unsafe fn run_dtors() { + let mut any_run = true; + for _ in 0..5 { + if !any_run { + break + } + any_run = false; + let mut cur = DTORS.load(SeqCst); + while !cur.is_null() { + let ptr = c::TlsGetValue((*cur).key); + + if !ptr.is_null() { + c::TlsSetValue((*cur).key, ptr::null_mut()); + ((*cur).dtor)(ptr as *mut _); + any_run = true; + } + + cur = (*cur).next; + } + } +} diff --git a/ctr-std/src/sys/windows/time.rs b/ctr-std/src/sys/windows/time.rs new file mode 100644 index 0000000..07e64d3 --- /dev/null +++ b/ctr-std/src/sys/windows/time.rs @@ -0,0 +1,206 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cmp::Ordering; +use fmt; +use mem; +use sync::Once; +use sys::c; +use sys::cvt; +use sys_common::mul_div_u64; +use time::Duration; +use convert::TryInto; +use core::hash::{Hash, Hasher}; + +const NANOS_PER_SEC: u64 = 1_000_000_000; +const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100; + +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] +pub struct Instant { + t: c::LARGE_INTEGER, +} + +#[derive(Copy, Clone)] +pub struct SystemTime { + t: c::FILETIME, +} + +const INTERVALS_TO_UNIX_EPOCH: u64 = 11_644_473_600 * INTERVALS_PER_SEC; + +pub const UNIX_EPOCH: SystemTime = SystemTime { + t: c::FILETIME { + dwLowDateTime: INTERVALS_TO_UNIX_EPOCH as u32, + dwHighDateTime: (INTERVALS_TO_UNIX_EPOCH >> 32) as u32, + }, +}; + +impl Instant { + pub fn now() -> Instant { + let mut t = Instant { t: 0 }; + cvt(unsafe { + c::QueryPerformanceCounter(&mut t.t) + }).unwrap(); + t + } + + pub fn sub_instant(&self, other: &Instant) -> Duration { + // Values which are +- 1 need to be considered as basically the same + // units in time due to various measurement oddities, according to + // Windows [1] + // + // [1]: + // https://msdn.microsoft.com/en-us/library/windows/desktop + // /dn553408%28v=vs.85%29.aspx#guidance + if other.t > self.t && other.t - self.t == 1 { + return Duration::new(0, 0) + } + let diff = (self.t as u64).checked_sub(other.t as u64) + .expect("specified instant was later than \ + self"); + let nanos = mul_div_u64(diff, NANOS_PER_SEC, frequency() as u64); + Duration::new(nanos / NANOS_PER_SEC, (nanos % NANOS_PER_SEC) as u32) + } + + pub fn add_duration(&self, other: &Duration) -> Instant { + let freq = frequency() as u64; + let t = other.as_secs().checked_mul(freq).and_then(|i| { + (self.t as u64).checked_add(i) + }).and_then(|i| { + i.checked_add(mul_div_u64(other.subsec_nanos() as u64, freq, + NANOS_PER_SEC)) + }).expect("overflow when adding duration to time"); + Instant { + t: t as c::LARGE_INTEGER, + } + } + + pub fn sub_duration(&self, other: &Duration) -> Instant { + let freq = frequency() as u64; + let t = other.as_secs().checked_mul(freq).and_then(|i| { + (self.t as u64).checked_sub(i) + }).and_then(|i| { + i.checked_sub(mul_div_u64(other.subsec_nanos() as u64, freq, + NANOS_PER_SEC)) + }).expect("overflow when subtracting duration from time"); + Instant { + t: t as c::LARGE_INTEGER, + } + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + unsafe { + let mut t: SystemTime = mem::zeroed(); + c::GetSystemTimeAsFileTime(&mut t.t); + return t + } + } + + fn from_intervals(intervals: i64) -> SystemTime { + SystemTime { + t: c::FILETIME { + dwLowDateTime: intervals as c::DWORD, + dwHighDateTime: (intervals >> 32) as c::DWORD, + } + } + } + + fn intervals(&self) -> i64 { + (self.t.dwLowDateTime as i64) | ((self.t.dwHighDateTime as i64) << 32) + } + + pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> { + let me = self.intervals(); + let other = other.intervals(); + if me >= other { + Ok(intervals2dur((me - other) as u64)) + } else { + Err(intervals2dur((other - me) as u64)) + } + } + + pub fn add_duration(&self, other: &Duration) -> SystemTime { + let intervals = self.intervals().checked_add(dur2intervals(other)) + .expect("overflow when adding duration to time"); + SystemTime::from_intervals(intervals) + } + + pub fn sub_duration(&self, other: &Duration) -> SystemTime { + let intervals = self.intervals().checked_sub(dur2intervals(other)) + .expect("overflow when subtracting from time"); + SystemTime::from_intervals(intervals) + } +} + +impl PartialEq for SystemTime { + fn eq(&self, other: &SystemTime) -> bool { + self.intervals() == other.intervals() + } +} + +impl Eq for SystemTime {} + +impl PartialOrd for SystemTime { + fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for SystemTime { + fn cmp(&self, other: &SystemTime) -> Ordering { + self.intervals().cmp(&other.intervals()) + } +} + +impl fmt::Debug for SystemTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("SystemTime") + .field("intervals", &self.intervals()) + .finish() + } +} + +impl From<c::FILETIME> for SystemTime { + fn from(t: c::FILETIME) -> SystemTime { + SystemTime { t: t } + } +} + +impl Hash for SystemTime { + fn hash<H : Hasher>(&self, state: &mut H) { + self.intervals().hash(state) + } +} + +fn dur2intervals(d: &Duration) -> i64 { + d.as_secs() + .checked_mul(INTERVALS_PER_SEC) + .and_then(|i| i.checked_add(d.subsec_nanos() as u64 / 100)) + .and_then(|i| i.try_into().ok()) + .expect("overflow when converting duration to intervals") +} + +fn intervals2dur(intervals: u64) -> Duration { + Duration::new(intervals / INTERVALS_PER_SEC, + ((intervals % INTERVALS_PER_SEC) * 100) as u32) +} + +fn frequency() -> c::LARGE_INTEGER { + static mut FREQUENCY: c::LARGE_INTEGER = 0; + static ONCE: Once = Once::new(); + + unsafe { + ONCE.call_once(|| { + cvt(c::QueryPerformanceFrequency(&mut FREQUENCY)).unwrap(); + }); + FREQUENCY + } +} |