aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorValentin <[email protected]>2018-06-15 18:57:24 +0200
committerFenrirWolf <[email protected]>2018-06-15 10:57:24 -0600
commitf2a90174bb36b9ad528e863ab34c02ebce002b02 (patch)
tree959e8d67883d3a89e179b3549b1f30d28e51a87c
parentMerge pull request #68 from linouxis9/master (diff)
downloadctru-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
-rw-r--r--.travis.yml2
-rw-r--r--ctr-std/src/alloc.rs113
-rw-r--r--ctr-std/src/build.rs137
-rw-r--r--ctr-std/src/collections/hash/map.rs60
-rw-r--r--ctr-std/src/collections/hash/table.rs166
-rw-r--r--ctr-std/src/collections/mod.rs8
-rw-r--r--ctr-std/src/env.rs33
-rw-r--r--ctr-std/src/f32.rs98
-rw-r--r--ctr-std/src/f64.rs98
-rw-r--r--ctr-std/src/ffi/c_str.rs129
-rw-r--r--ctr-std/src/ffi/os_str.rs32
-rw-r--r--ctr-std/src/fs.rs14
-rw-r--r--ctr-std/src/lib.rs29
-rw-r--r--ctr-std/src/macros.rs17
-rw-r--r--ctr-std/src/num.rs8
-rw-r--r--ctr-std/src/os/horizon/fs.rs393
-rw-r--r--ctr-std/src/os/horizon/mod.rs16
-rw-r--r--ctr-std/src/os/horizon/raw.rs275
-rw-r--r--ctr-std/src/os/mod.rs1
-rw-r--r--ctr-std/src/panicking.rs113
-rw-r--r--ctr-std/src/path.rs28
-rw-r--r--ctr-std/src/primitive_docs.rs77
-rw-r--r--ctr-std/src/process.rs8
-rw-r--r--ctr-std/src/sync/mutex.rs2
-rw-r--r--ctr-std/src/sync/once.rs23
-rw-r--r--ctr-std/src/sync/rwlock.rs4
-rw-r--r--ctr-std/src/sys/cloudabi/abi/bitflags.rs51
-rw-r--r--ctr-std/src/sys/cloudabi/abi/cloudabi.rs2847
-rw-r--r--ctr-std/src/sys/cloudabi/abi/mod.rs13
-rw-r--r--ctr-std/src/sys/cloudabi/args.rs17
-rw-r--r--ctr-std/src/sys/cloudabi/backtrace.rs122
-rw-r--r--ctr-std/src/sys/cloudabi/condvar.rs169
-rw-r--r--ctr-std/src/sys/cloudabi/mod.rs76
-rw-r--r--ctr-std/src/sys/cloudabi/mutex.rs158
-rw-r--r--ctr-std/src/sys/cloudabi/os.rs37
-rw-r--r--ctr-std/src/sys/cloudabi/rwlock.rs237
-rw-r--r--ctr-std/src/sys/cloudabi/shims/args.rs45
-rw-r--r--ctr-std/src/sys/cloudabi/shims/env.rs19
-rw-r--r--ctr-std/src/sys/cloudabi/shims/fs.rs302
-rw-r--r--ctr-std/src/sys/cloudabi/shims/mod.rs32
-rw-r--r--ctr-std/src/sys/cloudabi/shims/net.rs296
-rw-r--r--ctr-std/src/sys/cloudabi/shims/os.rs95
-rw-r--r--ctr-std/src/sys/cloudabi/shims/pipe.rs32
-rw-r--r--ctr-std/src/sys/cloudabi/shims/process.rs159
-rw-r--r--ctr-std/src/sys/cloudabi/stack_overflow.rs23
-rw-r--r--ctr-std/src/sys/cloudabi/stdio.rs83
-rw-r--r--ctr-std/src/sys/cloudabi/thread.rs126
-rw-r--r--ctr-std/src/sys/cloudabi/time.rs111
-rw-r--r--ctr-std/src/sys/horizon/android.rs170
-rw-r--r--ctr-std/src/sys/horizon/args.rs60
-rw-r--r--ctr-std/src/sys/horizon/backtrace/mod.rs119
-rw-r--r--ctr-std/src/sys/horizon/backtrace/printing/dladdr.rs45
-rw-r--r--ctr-std/src/sys/horizon/backtrace/printing/mod.rs43
-rw-r--r--ctr-std/src/sys/horizon/backtrace/tracing/backtrace_fn.rs49
-rw-r--r--ctr-std/src/sys/horizon/backtrace/tracing/gcc_s.rs107
-rw-r--r--ctr-std/src/sys/horizon/backtrace/tracing/mod.rs18
-rw-r--r--ctr-std/src/sys/horizon/cmath.rs43
-rw-r--r--ctr-std/src/sys/horizon/condvar.rs139
-rw-r--r--ctr-std/src/sys/horizon/env.rs185
-rw-r--r--ctr-std/src/sys/horizon/ext/ffi.rs119
-rw-r--r--ctr-std/src/sys/horizon/ext/fs.rs736
-rw-r--r--ctr-std/src/sys/horizon/ext/io.rs108
-rw-r--r--ctr-std/src/sys/horizon/ext/mod.rs59
-rw-r--r--ctr-std/src/sys/horizon/ext/raw.rs33
-rw-r--r--ctr-std/src/sys/horizon/fast_thread_local.rs77
-rw-r--r--ctr-std/src/sys/horizon/fd.rs265
-rw-r--r--ctr-std/src/sys/horizon/fs.rs928
-rw-r--r--ctr-std/src/sys/horizon/l4re.rs441
-rw-r--r--ctr-std/src/sys/horizon/memchr.rs57
-rw-r--r--ctr-std/src/sys/horizon/mod.rs179
-rw-r--r--ctr-std/src/sys/horizon/mutex.rs90
-rw-r--r--ctr-std/src/sys/horizon/net.rs428
-rw-r--r--ctr-std/src/sys/horizon/os.rs191
-rw-r--r--ctr-std/src/sys/horizon/os_str.rs189
-rw-r--r--ctr-std/src/sys/horizon/path.rs29
-rw-r--r--ctr-std/src/sys/horizon/pipe.rs137
-rw-r--r--ctr-std/src/sys/horizon/process.rs151
-rw-r--r--ctr-std/src/sys/horizon/rand.rs241
-rw-r--r--ctr-std/src/sys/horizon/rwlock.rs127
-rw-r--r--ctr-std/src/sys/horizon/stack_overflow.rs220
-rw-r--r--ctr-std/src/sys/horizon/stdio.rs81
-rw-r--r--ctr-std/src/sys/horizon/thread.rs106
-rw-r--r--ctr-std/src/sys/horizon/thread_local.rs71
-rw-r--r--ctr-std/src/sys/horizon/time.rs285
-rw-r--r--ctr-std/src/sys/horizon/weak.rs79
-rw-r--r--ctr-std/src/sys/mod.rs17
-rw-r--r--ctr-std/src/sys/redox/args.rs111
-rw-r--r--ctr-std/src/sys/redox/backtrace/mod.rs42
-rw-r--r--ctr-std/src/sys/redox/backtrace/printing.rs11
-rw-r--r--ctr-std/src/sys/redox/backtrace/tracing.rs107
-rw-r--r--ctr-std/src/sys/redox/cmath.rs43
-rw-r--r--ctr-std/src/sys/redox/condvar.rs121
-rw-r--r--ctr-std/src/sys/redox/env.rs19
-rw-r--r--ctr-std/src/sys/redox/ext/ffi.rs65
-rw-r--r--ctr-std/src/sys/redox/ext/fs.rs349
-rw-r--r--ctr-std/src/sys/redox/ext/io.rs167
-rw-r--r--ctr-std/src/sys/redox/ext/mod.rs54
-rw-r--r--ctr-std/src/sys/redox/ext/process.rs187
-rw-r--r--ctr-std/src/sys/redox/ext/thread.rs49
-rw-r--r--ctr-std/src/sys/redox/fast_thread_local.rs121
-rw-r--r--ctr-std/src/sys/redox/fd.rs95
-rw-r--r--ctr-std/src/sys/redox/fs.rs483
-rw-r--r--ctr-std/src/sys/redox/memchr.rs14
-rw-r--r--ctr-std/src/sys/redox/mod.rs81
-rw-r--r--ctr-std/src/sys/redox/mutex.rs179
-rw-r--r--ctr-std/src/sys/redox/net/dns/answer.rs22
-rw-r--r--ctr-std/src/sys/redox/net/dns/mod.rs215
-rw-r--r--ctr-std/src/sys/redox/net/dns/query.rs18
-rw-r--r--ctr-std/src/sys/redox/net/mod.rs115
-rw-r--r--ctr-std/src/sys/redox/net/netc.rs57
-rw-r--r--ctr-std/src/sys/redox/net/tcp.rs253
-rw-r--r--ctr-std/src/sys/redox/net/udp.rs242
-rw-r--r--ctr-std/src/sys/redox/os.rs219
-rw-r--r--ctr-std/src/sys/redox/os_str.rs189
-rw-r--r--ctr-std/src/sys/redox/path.rs39
-rw-r--r--ctr-std/src/sys/redox/pipe.rs103
-rw-r--r--ctr-std/src/sys/redox/process.rs543
-rw-r--r--ctr-std/src/sys/redox/rand.rs13
-rw-r--r--ctr-std/src/sys/redox/rwlock.rs61
-rw-r--r--ctr-std/src/sys/redox/stack_overflow.rs27
-rw-r--r--ctr-std/src/sys/redox/stdio.rs81
-rw-r--r--ctr-std/src/sys/redox/syscall/arch/arm.rs83
-rw-r--r--ctr-std/src/sys/redox/syscall/arch/x86.rs83
-rw-r--r--ctr-std/src/sys/redox/syscall/arch/x86_64.rs84
-rw-r--r--ctr-std/src/sys/redox/syscall/call.rs363
-rw-r--r--ctr-std/src/sys/redox/syscall/data.rs201
-rw-r--r--ctr-std/src/sys/redox/syscall/error.rs325
-rw-r--r--ctr-std/src/sys/redox/syscall/flag.rs116
-rw-r--r--ctr-std/src/sys/redox/syscall/mod.rs43
-rw-r--r--ctr-std/src/sys/redox/syscall/number.rs83
-rw-r--r--ctr-std/src/sys/redox/thread.rs95
-rw-r--r--ctr-std/src/sys/redox/thread_local.rs71
-rw-r--r--ctr-std/src/sys/redox/time.rs214
-rw-r--r--ctr-std/src/sys/unix/android.rs170
-rw-r--r--ctr-std/src/sys/unix/args.rs202
-rw-r--r--ctr-std/src/sys/unix/backtrace/mod.rs119
-rw-r--r--ctr-std/src/sys/unix/backtrace/printing/dladdr.rs45
-rw-r--r--ctr-std/src/sys/unix/backtrace/printing/mod.rs43
-rw-r--r--ctr-std/src/sys/unix/backtrace/tracing/backtrace_fn.rs49
-rw-r--r--ctr-std/src/sys/unix/backtrace/tracing/gcc_s.rs107
-rw-r--r--ctr-std/src/sys/unix/backtrace/tracing/mod.rs18
-rw-r--r--ctr-std/src/sys/unix/condvar.rs240
-rw-r--r--ctr-std/src/sys/unix/env.rs171
-rw-r--r--ctr-std/src/sys/unix/ext/ffi.rs8
-rw-r--r--ctr-std/src/sys/unix/ext/fs.rs736
-rw-r--r--ctr-std/src/sys/unix/ext/mod.rs28
-rw-r--r--ctr-std/src/sys/unix/ext/net.rs1818
-rw-r--r--ctr-std/src/sys/unix/ext/process.rs203
-rw-r--r--ctr-std/src/sys/unix/ext/raw.rs17
-rw-r--r--ctr-std/src/sys/unix/ext/thread.rs51
-rw-r--r--ctr-std/src/sys/unix/fast_thread_local.rs130
-rw-r--r--ctr-std/src/sys/unix/fd.rs89
-rw-r--r--ctr-std/src/sys/unix/fs.rs341
-rw-r--r--ctr-std/src/sys/unix/l4re.rs441
-rw-r--r--ctr-std/src/sys/unix/memchr.rs31
-rw-r--r--ctr-std/src/sys/unix/mod.rs61
-rw-r--r--ctr-std/src/sys/unix/mutex.rs104
-rw-r--r--ctr-std/src/sys/unix/net.rs73
-rw-r--r--ctr-std/src/sys/unix/os.rs441
-rw-r--r--ctr-std/src/sys/unix/os_str.rs2
-rw-r--r--ctr-std/src/sys/unix/path.rs14
-rw-r--r--ctr-std/src/sys/unix/pipe.rs124
-rw-r--r--ctr-std/src/sys/unix/process/mod.rs22
-rw-r--r--ctr-std/src/sys/unix/process/process_common.rs510
-rw-r--r--ctr-std/src/sys/unix/process/process_fuchsia.rs181
-rw-r--r--ctr-std/src/sys/unix/process/process_unix.rs405
-rw-r--r--ctr-std/src/sys/unix/process/zircon.rs279
-rw-r--r--ctr-std/src/sys/unix/rand.rs200
-rw-r--r--ctr-std/src/sys/unix/rwlock.rs173
-rw-r--r--ctr-std/src/sys/unix/stack_overflow.rs207
-rw-r--r--ctr-std/src/sys/unix/thread.rs431
-rw-r--r--ctr-std/src/sys/unix/thread_local.rs56
-rw-r--r--ctr-std/src/sys/unix/time.rs226
-rw-r--r--ctr-std/src/sys/unix/weak.rs79
-rw-r--r--ctr-std/src/sys/wasm/args.rs62
-rw-r--r--ctr-std/src/sys/wasm/backtrace.rs (renamed from ctr-std/src/sys/unix/backtrace.rs)0
-rw-r--r--ctr-std/src/sys/wasm/cmath.rs119
-rw-r--r--ctr-std/src/sys/wasm/condvar.rs43
-rw-r--r--ctr-std/src/sys/wasm/env.rs19
-rw-r--r--ctr-std/src/sys/wasm/fs.rs304
-rw-r--r--ctr-std/src/sys/wasm/memchr.rs11
-rw-r--r--ctr-std/src/sys/wasm/mod.rs314
-rw-r--r--ctr-std/src/sys/wasm/mutex.rs79
-rw-r--r--ctr-std/src/sys/wasm/net.rs337
-rw-r--r--ctr-std/src/sys/wasm/os.rs112
-rw-r--r--ctr-std/src/sys/wasm/os_str.rs189
-rw-r--r--ctr-std/src/sys/wasm/path.rs29
-rw-r--r--ctr-std/src/sys/wasm/pipe.rs35
-rw-r--r--ctr-std/src/sys/wasm/process.rs (renamed from ctr-std/src/sys/unix/process.rs)35
-rw-r--r--ctr-std/src/sys/wasm/rwlock.rs82
-rw-r--r--ctr-std/src/sys/wasm/stack_overflow.rs23
-rw-r--r--ctr-std/src/sys/wasm/stdio.rs75
-rw-r--r--ctr-std/src/sys/wasm/thread.rs50
-rw-r--r--ctr-std/src/sys/wasm/thread_local.rs50
-rw-r--r--ctr-std/src/sys/wasm/time.rs57
-rw-r--r--ctr-std/src/sys/windows/args.rs107
-rw-r--r--ctr-std/src/sys/windows/backtrace/backtrace_gnu.rs62
-rw-r--r--ctr-std/src/sys/windows/backtrace/mod.rs155
-rw-r--r--ctr-std/src/sys/windows/backtrace/printing/mod.rs20
-rw-r--r--ctr-std/src/sys/windows/backtrace/printing/msvc.rs97
-rw-r--r--ctr-std/src/sys/windows/c.rs1254
-rw-r--r--ctr-std/src/sys/windows/cmath.rs103
-rw-r--r--ctr-std/src/sys/windows/compat.rs81
-rw-r--r--ctr-std/src/sys/windows/condvar.rs65
-rw-r--r--ctr-std/src/sys/windows/dynamic_lib.rs54
-rw-r--r--ctr-std/src/sys/windows/env.rs19
-rw-r--r--ctr-std/src/sys/windows/ext/ffi.rs151
-rw-r--r--ctr-std/src/sys/windows/ext/fs.rs508
-rw-r--r--ctr-std/src/sys/windows/ext/io.rs209
-rw-r--r--ctr-std/src/sys/windows/ext/mod.rs44
-rw-r--r--ctr-std/src/sys/windows/ext/process.rs123
-rw-r--r--ctr-std/src/sys/windows/ext/raw.rs21
-rw-r--r--ctr-std/src/sys/windows/ext/thread.rs31
-rw-r--r--ctr-std/src/sys/windows/fast_thread_local.rs18
-rw-r--r--ctr-std/src/sys/windows/fs.rs804
-rw-r--r--ctr-std/src/sys/windows/handle.rs218
-rw-r--r--ctr-std/src/sys/windows/memchr.rs15
-rw-r--r--ctr-std/src/sys/windows/mod.rs273
-rw-r--r--ctr-std/src/sys/windows/mutex.rs188
-rw-r--r--ctr-std/src/sys/windows/net.rs356
-rw-r--r--ctr-std/src/sys/windows/os.rs337
-rw-r--r--ctr-std/src/sys/windows/os_str.rs182
-rw-r--r--ctr-std/src/sys/windows/path.rs106
-rw-r--r--ctr-std/src/sys/windows/pipe.rs364
-rw-r--r--ctr-std/src/sys/windows/process.rs585
-rw-r--r--ctr-std/src/sys/windows/rand.rs26
-rw-r--r--ctr-std/src/sys/windows/rwlock.rs52
-rw-r--r--ctr-std/src/sys/windows/stack_overflow.rs52
-rw-r--r--ctr-std/src/sys/windows/stdio.rs233
-rw-r--r--ctr-std/src/sys/windows/thread.rs100
-rw-r--r--ctr-std/src/sys/windows/thread_local.rs251
-rw-r--r--ctr-std/src/sys/windows/time.rs206
-rw-r--r--ctr-std/src/sys_common/at_exit_imp.rs2
-rw-r--r--ctr-std/src/sys_common/backtrace.rs18
-rw-r--r--ctr-std/src/sys_common/mod.rs14
-rw-r--r--ctr-std/src/sys_common/net.rs2
-rw-r--r--ctr-std/src/sys_common/poison.rs2
-rw-r--r--ctr-std/src/sys_common/process.rs14
-rw-r--r--ctr-std/src/sys_common/remutex.rs2
-rw-r--r--ctr-std/src/sys_common/thread.rs2
-rw-r--r--ctr-std/src/sys_common/thread_local.rs4
-rw-r--r--ctr-std/src/sys_common/util.rs5
-rw-r--r--ctr-std/src/sys_common/wtf8.rs18
-rw-r--r--ctr-std/src/time.rs3
244 files changed, 36777 insertions, 1061 deletions
diff --git a/.travis.yml b/.travis.yml
index 640f175..d37cc4b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
language: rust
rust:
- - nightly-2018-05-06
+ - nightly-2018-06-09
- nightly
matrix:
diff --git a/ctr-std/src/alloc.rs b/ctr-std/src/alloc.rs
index a857840..3b1a3a4 100644
--- a/ctr-std/src/alloc.rs
+++ b/ctr-std/src/alloc.rs
@@ -13,16 +13,61 @@
#![unstable(issue = "32838", feature = "allocator_api")]
#[doc(inline)] #[allow(deprecated)] pub use alloc_crate::alloc::Heap;
-#[doc(inline)] pub use alloc_crate::alloc::{Global, oom};
+#[doc(inline)] pub use alloc_crate::alloc::{Global, Layout, oom};
#[doc(inline)] pub use alloc_system::System;
#[doc(inline)] pub use core::alloc::*;
-#[cfg(not(stage0))]
+use core::sync::atomic::{AtomicPtr, Ordering};
+use core::{mem, ptr};
+use sys_common::util::dumb_print;
+
+static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
+
+/// Registers a custom OOM hook, replacing any that was previously registered.
+///
+/// The OOM hook is invoked when an infallible memory allocation fails, before
+/// the runtime aborts. The default hook prints a message to standard error,
+/// but this behavior can be customized with the [`set_oom_hook`] and
+/// [`take_oom_hook`] functions.
+///
+/// The hook is provided with a `Layout` struct which contains information
+/// about the allocation that failed.
+///
+/// The OOM hook is a global resource.
+pub fn set_oom_hook(hook: fn(Layout)) {
+ HOOK.store(hook as *mut (), Ordering::SeqCst);
+}
+
+/// Unregisters the current OOM hook, returning it.
+///
+/// *See also the function [`set_oom_hook`].*
+///
+/// If no custom hook is registered, the default hook will be returned.
+pub fn take_oom_hook() -> fn(Layout) {
+ let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst);
+ if hook.is_null() {
+ default_oom_hook
+ } else {
+ unsafe { mem::transmute(hook) }
+ }
+}
+
+fn default_oom_hook(layout: Layout) {
+ dumb_print(format_args!("memory allocation of {} bytes failed", layout.size()));
+}
+
#[cfg(not(test))]
#[doc(hidden)]
#[lang = "oom"]
-pub extern fn rust_oom() -> ! {
- rtabort!("memory allocation failed");
+pub extern fn rust_oom(layout: Layout) -> ! {
+ let hook = HOOK.load(Ordering::SeqCst);
+ let hook: fn(Layout) = if hook.is_null() {
+ default_oom_hook
+ } else {
+ unsafe { mem::transmute(hook) }
+ };
+ hook(layout);
+ unsafe { ::sys::abort_internal(); }
}
#[cfg(not(test))]
@@ -43,13 +88,6 @@ pub mod __default_lib_allocator {
System.alloc(layout) as *mut u8
}
- #[cfg(stage0)]
- #[no_mangle]
- #[rustc_std_internal_symbol]
- pub unsafe extern fn __rdl_oom() -> ! {
- super::oom()
- }
-
#[no_mangle]
#[rustc_std_internal_symbol]
pub unsafe extern fn __rdl_dealloc(ptr: *mut u8,
@@ -74,57 +112,4 @@ pub mod __default_lib_allocator {
let layout = Layout::from_size_align_unchecked(size, align);
System.alloc_zeroed(layout) as *mut u8
}
-
- #[cfg(stage0)]
- pub mod stage0 {
- #[no_mangle]
- #[rustc_std_internal_symbol]
- pub unsafe extern fn __rdl_usable_size(_layout: *const u8,
- _min: *mut usize,
- _max: *mut usize) {
- unimplemented!()
- }
-
- #[no_mangle]
- #[rustc_std_internal_symbol]
- pub unsafe extern fn __rdl_alloc_excess(_size: usize,
- _align: usize,
- _excess: *mut usize,
- _err: *mut u8) -> *mut u8 {
- unimplemented!()
- }
-
- #[no_mangle]
- #[rustc_std_internal_symbol]
- pub unsafe extern fn __rdl_realloc_excess(_ptr: *mut u8,
- _old_size: usize,
- _old_align: usize,
- _new_size: usize,
- _new_align: usize,
- _excess: *mut usize,
- _err: *mut u8) -> *mut u8 {
- unimplemented!()
- }
-
- #[no_mangle]
- #[rustc_std_internal_symbol]
- pub unsafe extern fn __rdl_grow_in_place(_ptr: *mut u8,
- _old_size: usize,
- _old_align: usize,
- _new_size: usize,
- _new_align: usize) -> u8 {
- unimplemented!()
- }
-
- #[no_mangle]
- #[rustc_std_internal_symbol]
- pub unsafe extern fn __rdl_shrink_in_place(_ptr: *mut u8,
- _old_size: usize,
- _old_align: usize,
- _new_size: usize,
- _new_align: usize) -> u8 {
- unimplemented!()
- }
-
- }
}
diff --git a/ctr-std/src/build.rs b/ctr-std/src/build.rs
new file mode 100644
index 0000000..c001e4e
--- /dev/null
+++ b/ctr-std/src/build.rs
@@ -0,0 +1,137 @@
+// 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.
+
+#![deny(warnings)]
+
+extern crate build_helper;
+extern crate cc;
+
+use build_helper::native_lib_boilerplate;
+use std::env;
+use std::fs::File;
+
+fn main() {
+ let target = env::var("TARGET").expect("TARGET was not set");
+ if cfg!(feature = "backtrace") &&
+ !target.contains("cloudabi") &&
+ !target.contains("emscripten") &&
+ !target.contains("fuchsia") &&
+ !target.contains("msvc") &&
+ !target.contains("wasm32")
+ {
+ let _ = build_libbacktrace(&target);
+ }
+
+ if target.contains("linux") {
+ if target.contains("android") {
+ println!("cargo:rustc-link-lib=dl");
+ println!("cargo:rustc-link-lib=log");
+ println!("cargo:rustc-link-lib=gcc");
+ } else if !target.contains("musl") {
+ println!("cargo:rustc-link-lib=dl");
+ println!("cargo:rustc-link-lib=rt");
+ println!("cargo:rustc-link-lib=pthread");
+ }
+ } else if target.contains("freebsd") {
+ println!("cargo:rustc-link-lib=execinfo");
+ println!("cargo:rustc-link-lib=pthread");
+ } else if target.contains("dragonfly") || target.contains("bitrig") ||
+ target.contains("netbsd") || target.contains("openbsd") {
+ println!("cargo:rustc-link-lib=pthread");
+ } else if target.contains("solaris") {
+ println!("cargo:rustc-link-lib=socket");
+ println!("cargo:rustc-link-lib=posix4");
+ println!("cargo:rustc-link-lib=pthread");
+ println!("cargo:rustc-link-lib=resolv");
+ } else if target.contains("apple-darwin") {
+ println!("cargo:rustc-link-lib=System");
+
+ // res_init and friends require -lresolv on macOS/iOS.
+ // See #41582 and http://blog.achernya.com/2013/03/os-x-has-silly-libsystem.html
+ println!("cargo:rustc-link-lib=resolv");
+ } else if target.contains("apple-ios") {
+ println!("cargo:rustc-link-lib=System");
+ println!("cargo:rustc-link-lib=objc");
+ println!("cargo:rustc-link-lib=framework=Security");
+ println!("cargo:rustc-link-lib=framework=Foundation");
+ println!("cargo:rustc-link-lib=resolv");
+ } else if target.contains("windows") {
+ println!("cargo:rustc-link-lib=advapi32");
+ println!("cargo:rustc-link-lib=ws2_32");
+ println!("cargo:rustc-link-lib=userenv");
+ println!("cargo:rustc-link-lib=shell32");
+ } else if target.contains("fuchsia") {
+ // use system-provided libbacktrace
+ if cfg!(feature = "backtrace") {
+ println!("cargo:rustc-link-lib=backtrace");
+ }
+ println!("cargo:rustc-link-lib=zircon");
+ println!("cargo:rustc-link-lib=fdio");
+ } else if target.contains("cloudabi") {
+ if cfg!(feature = "backtrace") {
+ println!("cargo:rustc-link-lib=unwind");
+ }
+ println!("cargo:rustc-link-lib=c");
+ println!("cargo:rustc-link-lib=compiler_rt");
+ }
+}
+
+fn build_libbacktrace(target: &str) -> Result<(), ()> {
+ let native = native_lib_boilerplate("libbacktrace", "libbacktrace", "backtrace", "")?;
+
+ let mut build = cc::Build::new();
+ build
+ .flag("-fvisibility=hidden")
+ .include("../libbacktrace")
+ .include(&native.out_dir)
+ .out_dir(&native.out_dir)
+ .warnings(false)
+ .file("../libbacktrace/alloc.c")
+ .file("../libbacktrace/backtrace.c")
+ .file("../libbacktrace/dwarf.c")
+ .file("../libbacktrace/fileline.c")
+ .file("../libbacktrace/posix.c")
+ .file("../libbacktrace/read.c")
+ .file("../libbacktrace/sort.c")
+ .file("../libbacktrace/state.c");
+
+ if target.contains("darwin") {
+ build.file("../libbacktrace/macho.c");
+ } else if target.contains("windows") {
+ build.file("../libbacktrace/pecoff.c");
+ } else {
+ build.file("../libbacktrace/elf.c");
+
+ if target.contains("64") {
+ build.define("BACKTRACE_ELF_SIZE", "64");
+ } else {
+ build.define("BACKTRACE_ELF_SIZE", "32");
+ }
+ }
+
+ File::create(native.out_dir.join("backtrace-supported.h")).unwrap();
+ build.define("BACKTRACE_SUPPORTED", "1");
+ build.define("BACKTRACE_USES_MALLOC", "1");
+ build.define("BACKTRACE_SUPPORTS_THREADS", "0");
+ build.define("BACKTRACE_SUPPORTS_DATA", "0");
+
+ File::create(native.out_dir.join("config.h")).unwrap();
+ if !target.contains("apple-ios") &&
+ !target.contains("solaris") &&
+ !target.contains("redox") &&
+ !target.contains("android") {
+ build.define("HAVE_DL_ITERATE_PHDR", "1");
+ }
+ build.define("_GNU_SOURCE", "1");
+ build.define("_LARGE_FILES", "1");
+
+ build.compile("backtrace");
+ Ok(())
+}
diff --git a/ctr-std/src/collections/hash/map.rs b/ctr-std/src/collections/hash/map.rs
index a7eb002..9c77acb 100644
--- a/ctr-std/src/collections/hash/map.rs
+++ b/ctr-std/src/collections/hash/map.rs
@@ -11,7 +11,7 @@
use self::Entry::*;
use self::VacantEntryState::*;
-use alloc::{CollectionAllocErr, oom};
+use alloc::CollectionAllocErr;
use cell::Cell;
use borrow::Borrow;
use cmp::max;
@@ -23,8 +23,10 @@ use mem::{self, replace};
use ops::{Deref, Index};
use sys;
-use super::table::{self, Bucket, EmptyBucket, FullBucket, FullBucketMut, RawTable, SafeHash};
+use super::table::{self, Bucket, EmptyBucket, Fallibility, FullBucket, FullBucketMut, RawTable,
+ SafeHash};
use super::table::BucketState::{Empty, Full};
+use super::table::Fallibility::{Fallible, Infallible};
const MIN_NONZERO_RAW_CAPACITY: usize = 32; // must be a power of two
@@ -783,11 +785,11 @@ impl<K, V, S> HashMap<K, V, S>
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn reserve(&mut self, additional: usize) {
- match self.try_reserve(additional) {
+ match self.reserve_internal(additional, Infallible) {
Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"),
- Err(CollectionAllocErr::AllocErr) => oom(),
+ Err(CollectionAllocErr::AllocErr) => unreachable!(),
Ok(()) => { /* yay */ }
- }
+ }
}
/// Tries to reserve capacity for at least `additional` more elements to be inserted
@@ -809,17 +811,24 @@ impl<K, V, S> HashMap<K, V, S>
/// ```
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> {
+ self.reserve_internal(additional, Fallible)
+ }
+
+ fn reserve_internal(&mut self, additional: usize, fallibility: Fallibility)
+ -> Result<(), CollectionAllocErr> {
+
let remaining = self.capacity() - self.len(); // this can't overflow
if remaining < additional {
- let min_cap = self.len().checked_add(additional)
+ let min_cap = self.len()
+ .checked_add(additional)
.ok_or(CollectionAllocErr::CapacityOverflow)?;
let raw_cap = self.resize_policy.try_raw_capacity(min_cap)?;
- self.try_resize(raw_cap)?;
+ self.try_resize(raw_cap, fallibility)?;
} else if self.table.tag() && remaining <= self.len() {
// Probe sequence is too long and table is half full,
// resize early to reduce probing length.
let new_capacity = self.table.capacity() * 2;
- self.try_resize(new_capacity)?;
+ self.try_resize(new_capacity, fallibility)?;
}
Ok(())
}
@@ -831,11 +840,21 @@ impl<K, V, S> HashMap<K, V, S>
/// 2) Ensure `new_raw_cap` is a power of two or zero.
#[inline(never)]
#[cold]
- fn try_resize(&mut self, new_raw_cap: usize) -> Result<(), CollectionAllocErr> {
+ fn try_resize(
+ &mut self,
+ new_raw_cap: usize,
+ fallibility: Fallibility,
+ ) -> Result<(), CollectionAllocErr> {
assert!(self.table.size() <= new_raw_cap);
assert!(new_raw_cap.is_power_of_two() || new_raw_cap == 0);
- let mut old_table = replace(&mut self.table, RawTable::try_new(new_raw_cap)?);
+ let mut old_table = replace(
+ &mut self.table,
+ match fallibility {
+ Infallible => RawTable::new(new_raw_cap),
+ Fallible => RawTable::try_new(new_raw_cap)?,
+ }
+ );
let old_size = old_table.size();
if old_table.size() == 0 {
@@ -2142,14 +2161,13 @@ impl<'a, K, V> Entry<'a, K, V> {
}
impl<'a, K, V: Default> Entry<'a, K, V> {
- #[unstable(feature = "entry_or_default", issue = "44324")]
+ #[stable(feature = "entry_or_default", since = "1.28.0")]
/// Ensures a value is in the entry by inserting the default value if empty,
/// and returns a mutable reference to the value in the entry.
///
/// # Examples
///
/// ```
- /// #![feature(entry_or_default)]
/// # fn main() {
/// use std::collections::HashMap;
///
@@ -2165,7 +2183,6 @@ impl<'a, K, V: Default> Entry<'a, K, V> {
Vacant(entry) => entry.insert(Default::default()),
}
}
-
}
impl<'a, K, V> OccupiedEntry<'a, K, V> {
@@ -2231,6 +2248,11 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
/// Gets a mutable reference to the value in the entry.
///
+ /// If you need a reference to the `OccupiedEntry` which may outlive the
+ /// destruction of the `Entry` value, see [`into_mut`].
+ ///
+ /// [`into_mut`]: #method.into_mut
+ ///
/// # Examples
///
/// ```
@@ -2242,10 +2264,14 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
///
/// assert_eq!(map["poneyland"], 12);
/// if let Entry::Occupied(mut o) = map.entry("poneyland") {
- /// *o.get_mut() += 10;
+ /// *o.get_mut() += 10;
+ /// assert_eq!(*o.get(), 22);
+ ///
+ /// // We can use the same Entry multiple times.
+ /// *o.get_mut() += 2;
/// }
///
- /// assert_eq!(map["poneyland"], 22);
+ /// assert_eq!(map["poneyland"], 24);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn get_mut(&mut self) -> &mut V {
@@ -2255,6 +2281,10 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
/// Converts the OccupiedEntry into a mutable reference to the value in the entry
/// with a lifetime bound to the map itself.
///
+ /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`].
+ ///
+ /// [`get_mut`]: #method.get_mut
+ ///
/// # Examples
///
/// ```
diff --git a/ctr-std/src/collections/hash/table.rs b/ctr-std/src/collections/hash/table.rs
index b50652e..d997fb2 100644
--- a/ctr-std/src/collections/hash/table.rs
+++ b/ctr-std/src/collections/hash/table.rs
@@ -8,14 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use alloc::{Global, Alloc, Layout, CollectionAllocErr, oom};
-use cmp;
+use alloc::{Global, Alloc, Layout, LayoutErr, CollectionAllocErr, oom};
use hash::{BuildHasher, Hash, Hasher};
use marker;
-use mem::{align_of, size_of, needs_drop};
+use mem::{size_of, needs_drop};
use mem;
use ops::{Deref, DerefMut};
use ptr::{self, Unique, NonNull};
+use hint;
use self::BucketState::*;
@@ -651,71 +651,39 @@ impl<K, V, M> GapThenFull<K, V, M>
}
}
-
-/// Rounds up to a multiple of a power of two. Returns the closest multiple
-/// of `target_alignment` that is higher or equal to `unrounded`.
-///
-/// # Panics
-///
-/// Panics if `target_alignment` is not a power of two.
-#[inline]
-fn round_up_to_next(unrounded: usize, target_alignment: usize) -> usize {
- assert!(target_alignment.is_power_of_two());
- (unrounded + target_alignment - 1) & !(target_alignment - 1)
-}
-
-#[test]
-fn test_rounding() {
- assert_eq!(round_up_to_next(0, 4), 0);
- assert_eq!(round_up_to_next(1, 4), 4);
- assert_eq!(round_up_to_next(2, 4), 4);
- assert_eq!(round_up_to_next(3, 4), 4);
- assert_eq!(round_up_to_next(4, 4), 4);
- assert_eq!(round_up_to_next(5, 4), 8);
-}
-
-// Returns a tuple of (pairs_offset, end_of_pairs_offset),
-// from the start of a mallocated array.
-#[inline]
-fn calculate_offsets(hashes_size: usize,
- pairs_size: usize,
- pairs_align: usize)
- -> (usize, usize, bool) {
- let pairs_offset = round_up_to_next(hashes_size, pairs_align);
- let (end_of_pairs, oflo) = pairs_offset.overflowing_add(pairs_size);
-
- (pairs_offset, end_of_pairs, oflo)
+// Returns a Layout which describes the allocation required for a hash table,
+// and the offset of the array of (key, value) pairs in the allocation.
+fn calculate_layout<K, V>(capacity: usize) -> Result<(Layout, usize), LayoutErr> {
+ let hashes = Layout::array::<HashUint>(capacity)?;
+ let pairs = Layout::array::<(K, V)>(capacity)?;
+ hashes.extend(pairs).map(|(layout, _)| {
+ // LLVM seems to have trouble properly const-propagating pairs.align(),
+ // possibly due to the use of NonZeroUsize. This little hack allows it
+ // to generate optimal code.
+ //
+ // See https://github.com/rust-lang/rust/issues/51346 for more details.
+ (
+ layout,
+ hashes.size() + hashes.padding_needed_for(mem::align_of::<(K, V)>()),
+ )
+ })
}
-// Returns a tuple of (minimum required malloc alignment,
-// array_size), from the start of a mallocated array.
-fn calculate_allocation(hash_size: usize,
- hash_align: usize,
- pairs_size: usize,
- pairs_align: usize)
- -> (usize, usize, bool) {
- let (_, end_of_pairs, oflo) = calculate_offsets(hash_size, pairs_size, pairs_align);
-
- let align = cmp::max(hash_align, pairs_align);
-
- (align, end_of_pairs, oflo)
+pub(crate) enum Fallibility {
+ Fallible,
+ Infallible,
}
-#[test]
-fn test_offset_calculation() {
- assert_eq!(calculate_allocation(128, 8, 16, 8), (8, 144, false));
- assert_eq!(calculate_allocation(3, 1, 2, 1), (1, 5, false));
- assert_eq!(calculate_allocation(6, 2, 12, 4), (4, 20, false));
- assert_eq!(calculate_offsets(128, 15, 4), (128, 143, false));
- assert_eq!(calculate_offsets(3, 2, 4), (4, 6, false));
- assert_eq!(calculate_offsets(6, 12, 4), (8, 20, false));
-}
+use self::Fallibility::*;
impl<K, V> RawTable<K, V> {
/// Does not initialize the buckets. The caller should ensure they,
/// at the very least, set every hash to EMPTY_BUCKET.
/// Returns an error if it cannot allocate or capacity overflows.
- unsafe fn try_new_uninitialized(capacity: usize) -> Result<RawTable<K, V>, CollectionAllocErr> {
+ unsafe fn new_uninitialized_internal(
+ capacity: usize,
+ fallibility: Fallibility,
+ ) -> Result<RawTable<K, V>, CollectionAllocErr> {
if capacity == 0 {
return Ok(RawTable {
size: 0,
@@ -725,37 +693,15 @@ impl<K, V> RawTable<K, V> {
});
}
- // No need for `checked_mul` before a more restrictive check performed
- // later in this method.
- let hashes_size = capacity.wrapping_mul(size_of::<HashUint>());
- let pairs_size = capacity.wrapping_mul(size_of::<(K, V)>());
-
// Allocating hashmaps is a little tricky. We need to allocate two
// arrays, but since we know their sizes and alignments up front,
// we just allocate a single array, and then have the subarrays
// point into it.
- //
- // This is great in theory, but in practice getting the alignment
- // right is a little subtle. Therefore, calculating offsets has been
- // factored out into a different function.
- let (alignment, size, oflo) = calculate_allocation(hashes_size,
- align_of::<HashUint>(),
- pairs_size,
- align_of::<(K, V)>());
- if oflo {
- return Err(CollectionAllocErr::CapacityOverflow);
- }
-
- // One check for overflow that covers calculation and rounding of size.
- let size_of_bucket = size_of::<HashUint>().checked_add(size_of::<(K, V)>())
- .ok_or(CollectionAllocErr::CapacityOverflow)?;
- let capacity_mul_size_of_bucket = capacity.checked_mul(size_of_bucket);
- if capacity_mul_size_of_bucket.is_none() || size < capacity_mul_size_of_bucket.unwrap() {
- return Err(CollectionAllocErr::CapacityOverflow);
- }
-
- let buffer = Global.alloc(Layout::from_size_align(size, alignment)
- .map_err(|_| CollectionAllocErr::CapacityOverflow)?)?;
+ let (layout, _) = calculate_layout::<K, V>(capacity)?;
+ let buffer = Global.alloc(layout).map_err(|e| match fallibility {
+ Infallible => oom(layout),
+ Fallible => e,
+ })?;
Ok(RawTable {
capacity_mask: capacity.wrapping_sub(1),
@@ -768,48 +714,50 @@ impl<K, V> RawTable<K, V> {
/// Does not initialize the buckets. The caller should ensure they,
/// at the very least, set every hash to EMPTY_BUCKET.
unsafe fn new_uninitialized(capacity: usize) -> RawTable<K, V> {
- match Self::try_new_uninitialized(capacity) {
+ match Self::new_uninitialized_internal(capacity, Infallible) {
Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"),
- Err(CollectionAllocErr::AllocErr) => oom(),
+ Err(CollectionAllocErr::AllocErr) => unreachable!(),
Ok(table) => { table }
}
}
fn raw_bucket_at(&self, index: usize) -> RawBucket<K, V> {
- let hashes_size = self.capacity() * size_of::<HashUint>();
- let pairs_size = self.capacity() * size_of::<(K, V)>();
-
- let (pairs_offset, _, oflo) =
- calculate_offsets(hashes_size, pairs_size, align_of::<(K, V)>());
- debug_assert!(!oflo, "capacity overflow");
-
+ let (_, pairs_offset) = calculate_layout::<K, V>(self.capacity())
+ .unwrap_or_else(|_| unsafe { hint::unreachable_unchecked() });
let buffer = self.hashes.ptr() as *mut u8;
unsafe {
RawBucket {
hash_start: buffer as *mut HashUint,
- pair_start: buffer.offset(pairs_offset as isize) as *const (K, V),
+ pair_start: buffer.add(pairs_offset) as *const (K, V),
idx: index,
_marker: marker::PhantomData,
}
}
}
- /// Tries to create a new raw table from a given capacity. If it cannot allocate,
- /// it returns with AllocErr.
- pub fn try_new(capacity: usize) -> Result<RawTable<K, V>, CollectionAllocErr> {
+ fn new_internal(
+ capacity: usize,
+ fallibility: Fallibility,
+ ) -> Result<RawTable<K, V>, CollectionAllocErr> {
unsafe {
- let ret = RawTable::try_new_uninitialized(capacity)?;
+ let ret = RawTable::new_uninitialized_internal(capacity, fallibility)?;
ptr::write_bytes(ret.hashes.ptr(), 0, capacity);
Ok(ret)
}
}
+ /// Tries to create a new raw table from a given capacity. If it cannot allocate,
+ /// it returns with AllocErr.
+ pub fn try_new(capacity: usize) -> Result<RawTable<K, V>, CollectionAllocErr> {
+ Self::new_internal(capacity, Fallible)
+ }
+
/// Creates a new raw table from a given capacity. All buckets are
/// initially empty.
pub fn new(capacity: usize) -> RawTable<K, V> {
- match Self::try_new(capacity) {
+ match Self::new_internal(capacity, Infallible) {
Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"),
- Err(CollectionAllocErr::AllocErr) => oom(),
+ Err(CollectionAllocErr::AllocErr) => unreachable!(),
Ok(table) => { table }
}
}
@@ -1173,18 +1121,10 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable<K, V> {
}
}
- let hashes_size = self.capacity() * size_of::<HashUint>();
- let pairs_size = self.capacity() * size_of::<(K, V)>();
- let (align, size, oflo) = calculate_allocation(hashes_size,
- align_of::<HashUint>(),
- pairs_size,
- align_of::<(K, V)>());
-
- debug_assert!(!oflo, "should be impossible");
-
+ let (layout, _) = calculate_layout::<K, V>(self.capacity())
+ .unwrap_or_else(|_| unsafe { hint::unreachable_unchecked() });
unsafe {
- Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).as_opaque(),
- Layout::from_size_align(size, align).unwrap());
+ Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).as_opaque(), layout);
// Remember how everything was allocated out of one buffer
// during initialization? We only need one call to free here.
}
diff --git a/ctr-std/src/collections/mod.rs b/ctr-std/src/collections/mod.rs
index 9cf7382..d8e79b9 100644
--- a/ctr-std/src/collections/mod.rs
+++ b/ctr-std/src/collections/mod.rs
@@ -437,14 +437,6 @@ pub use self::hash_map::HashMap;
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::hash_set::HashSet;
-#[unstable(feature = "collections_range", issue = "30877")]
-#[rustc_deprecated(reason = "renamed and moved to `std::ops::RangeBounds`", since = "1.26.0")]
-#[doc(hidden)]
-/// Range syntax
-pub mod range {
- pub use ops::RangeBounds as RangeArgument;
-}
-
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
pub use heap::CollectionAllocErr;
diff --git a/ctr-std/src/env.rs b/ctr-std/src/env.rs
index a103c0b..91e417c 100644
--- a/ctr-std/src/env.rs
+++ b/ctr-std/src/env.rs
@@ -49,9 +49,11 @@ use sys::os as os_imp;
/// ```
/// use std::env;
///
-/// // We assume that we are in a valid directory.
-/// let path = env::current_dir().unwrap();
-/// println!("The current directory is {}", path.display());
+/// fn main() -> std::io::Result<()> {
+/// let path = env::current_dir()?;
+/// println!("The current directory is {}", path.display());
+/// Ok(())
+/// }
/// ```
#[stable(feature = "env", since = "1.0.0")]
pub fn current_dir() -> io::Result<PathBuf> {
@@ -441,15 +443,18 @@ pub struct JoinPathsError {
/// Joining paths on a Unix-like platform:
///
/// ```
-/// # if cfg!(unix) {
/// use std::env;
/// use std::ffi::OsString;
/// use std::path::Path;
///
-/// let paths = [Path::new("/bin"), Path::new("/usr/bin")];
-/// let path_os_string = env::join_paths(paths.iter()).unwrap();
-/// assert_eq!(path_os_string, OsString::from("/bin:/usr/bin"));
+/// fn main() -> Result<(), env::JoinPathsError> {
+/// # if cfg!(unix) {
+/// let paths = [Path::new("/bin"), Path::new("/usr/bin")];
+/// let path_os_string = env::join_paths(paths.iter())?;
+/// assert_eq!(path_os_string, OsString::from("/bin:/usr/bin"));
/// # }
+/// Ok(())
+/// }
/// ```
///
/// Joining a path containing a colon on a Unix-like platform results in an error:
@@ -471,11 +476,15 @@ pub struct JoinPathsError {
/// use std::env;
/// use std::path::PathBuf;
///
-/// if let Some(path) = env::var_os("PATH") {
-/// let mut paths = env::split_paths(&path).collect::<Vec<_>>();
-/// paths.push(PathBuf::from("/home/xyz/bin"));
-/// let new_path = env::join_paths(paths).unwrap();
-/// env::set_var("PATH", &new_path);
+/// fn main() -> Result<(), env::JoinPathsError> {
+/// if let Some(path) = env::var_os("PATH") {
+/// let mut paths = env::split_paths(&path).collect::<Vec<_>>();
+/// paths.push(PathBuf::from("/home/xyz/bin"));
+/// let new_path = env::join_paths(paths)?;
+/// env::set_var("PATH", &new_path);
+/// }
+///
+/// Ok(())
/// }
/// ```
#[stable(feature = "env", since = "1.0.0")]
diff --git a/ctr-std/src/f32.rs b/ctr-std/src/f32.rs
index 26644c7..ae30321 100644
--- a/ctr-std/src/f32.rs
+++ b/ctr-std/src/f32.rs
@@ -11,22 +11,16 @@
//! This module provides constants which are specific to the implementation
//! of the `f32` floating point data type.
//!
-//! Mathematically significant numbers are provided in the `consts` sub-module.
-//!
//! *[See also the `f32` primitive type](../../std/primitive.f32.html).*
+//!
+//! Mathematically significant numbers are provided in the `consts` sub-module.
#![stable(feature = "rust1", since = "1.0.0")]
#![allow(missing_docs)]
#[cfg(not(test))]
-#[cfg(stage0)]
-use core::num::Float;
-#[cfg(not(test))]
use intrinsics;
#[cfg(not(test))]
-#[cfg(stage0)]
-use num::FpCategory;
-#[cfg(not(test))]
use sys::cmath;
#[stable(feature = "rust1", since = "1.0.0")]
@@ -41,14 +35,12 @@ pub use core::f32::{MIN, MIN_POSITIVE, MAX};
pub use core::f32::consts;
#[cfg(not(test))]
-#[cfg_attr(stage0, lang = "f32")]
-#[cfg_attr(not(stage0), lang = "f32_runtime")]
+#[lang = "f32_runtime"]
impl f32 {
- #[cfg(stage0)]
- f32_core_methods!();
-
/// Returns the largest integer less than or equal to a number.
///
+ /// # Examples
+ ///
/// ```
/// let f = 3.99_f32;
/// let g = 3.0_f32;
@@ -80,6 +72,8 @@ impl f32 {
/// Returns the smallest integer greater than or equal to a number.
///
+ /// # Examples
+ ///
/// ```
/// let f = 3.01_f32;
/// let g = 4.0_f32;
@@ -100,6 +94,8 @@ impl f32 {
/// Returns the nearest integer to a number. Round half-way cases away from
/// `0.0`.
///
+ /// # Examples
+ ///
/// ```
/// let f = 3.3_f32;
/// let g = -3.3_f32;
@@ -115,6 +111,8 @@ impl f32 {
/// Returns the integer part of a number.
///
+ /// # Examples
+ ///
/// ```
/// let f = 3.3_f32;
/// let g = -3.7_f32;
@@ -130,6 +128,8 @@ impl f32 {
/// Returns the fractional part of a number.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -148,6 +148,8 @@ impl f32 {
/// Computes the absolute value of `self`. Returns `NAN` if the
/// number is `NAN`.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -174,6 +176,8 @@ impl f32 {
/// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
/// - `NAN` if the number is `NAN`
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -195,8 +199,12 @@ impl f32 {
}
/// Fused multiply-add. Computes `(self * a) + b` with only one rounding
- /// error. This produces a more accurate result with better performance than
- /// a separate multiplication operation followed by an add.
+ /// error, yielding a more accurate result than an unfused multiply-add.
+ ///
+ /// Using `mul_add` can be more performant than an unfused multiply-add if
+ /// the target architecture has a dedicated `fma` CPU instruction.
+ ///
+ /// # Examples
///
/// ```
/// use std::f32;
@@ -223,6 +231,8 @@ impl f32 {
/// In other words, the result is `self / rhs` rounded to the integer `n`
/// such that `self >= n * rhs`.
///
+ /// # Examples
+ ///
/// ```
/// #![feature(euclidean_division)]
/// let a: f32 = 7.0;
@@ -246,6 +256,8 @@ impl f32 {
///
/// In particular, the result `n` satisfies `0 <= n < rhs.abs()`.
///
+ /// # Examples
+ ///
/// ```
/// #![feature(euclidean_division)]
/// let a: f32 = 7.0;
@@ -271,6 +283,8 @@ impl f32 {
///
/// Using this function is generally faster than using `powf`
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -287,6 +301,8 @@ impl f32 {
/// Raises a number to a floating point power.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -309,6 +325,8 @@ impl f32 {
///
/// Returns NaN if `self` is a negative number.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -332,6 +350,8 @@ impl f32 {
/// Returns `e^(self)`, (the exponential function).
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -356,6 +376,8 @@ impl f32 {
/// Returns `2^(self)`.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -374,6 +396,8 @@ impl f32 {
/// Returns the natural logarithm of the number.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -402,6 +426,8 @@ impl f32 {
/// `self.log2()` can produce more accurate results for base 2, and
/// `self.log10()` can produce more accurate results for base 10.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -418,6 +444,8 @@ impl f32 {
/// Returns the base 2 logarithm of the number.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -439,6 +467,8 @@ impl f32 {
/// Returns the base 10 logarithm of the number.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -464,6 +494,8 @@ impl f32 {
/// * If `self <= other`: `0:0`
/// * Else: `self - other`
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -491,6 +523,8 @@ impl f32 {
/// Takes the cubic root of a number.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -510,6 +544,8 @@ impl f32 {
/// Calculates the length of the hypotenuse of a right-angle triangle given
/// legs of length `x` and `y`.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -529,6 +565,8 @@ impl f32 {
/// Computes the sine of a number (in radians).
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -550,6 +588,8 @@ impl f32 {
/// Computes the cosine of a number (in radians).
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -571,6 +611,8 @@ impl f32 {
/// Computes the tangent of a number (in radians).
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -589,6 +631,8 @@ impl f32 {
/// the range [-pi/2, pi/2] or NaN if the number is outside the range
/// [-1, 1].
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -609,6 +653,8 @@ impl f32 {
/// the range [0, pi] or NaN if the number is outside the range
/// [-1, 1].
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -628,6 +674,8 @@ impl f32 {
/// Computes the arctangent of a number. Return value is in radians in the
/// range [-pi/2, pi/2];
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -651,6 +699,8 @@ impl f32 {
/// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]`
/// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)`
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -680,6 +730,8 @@ impl f32 {
/// Simultaneously computes the sine and cosine of the number, `x`. Returns
/// `(sin(x), cos(x))`.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -701,6 +753,8 @@ impl f32 {
/// Returns `e^(self) - 1` in a way that is accurate even if the
/// number is close to zero.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -720,6 +774,8 @@ impl f32 {
/// Returns `ln(1+n)` (natural logarithm) more accurately than if
/// the operations were performed separately.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -738,6 +794,8 @@ impl f32 {
/// Hyperbolic sine function.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -759,6 +817,8 @@ impl f32 {
/// Hyperbolic cosine function.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -780,6 +840,8 @@ impl f32 {
/// Hyperbolic tangent function.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -801,6 +863,8 @@ impl f32 {
/// Inverse hyperbolic sine function.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -823,6 +887,8 @@ impl f32 {
/// Inverse hyperbolic cosine function.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
@@ -844,6 +910,8 @@ impl f32 {
/// Inverse hyperbolic tangent function.
///
+ /// # Examples
+ ///
/// ```
/// use std::f32;
///
diff --git a/ctr-std/src/f64.rs b/ctr-std/src/f64.rs
index a7e63f5..7950d43 100644
--- a/ctr-std/src/f64.rs
+++ b/ctr-std/src/f64.rs
@@ -11,22 +11,16 @@
//! This module provides constants which are specific to the implementation
//! of the `f64` floating point data type.
//!
-//! Mathematically significant numbers are provided in the `consts` sub-module.
-//!
//! *[See also the `f64` primitive type](../../std/primitive.f64.html).*
+//!
+//! Mathematically significant numbers are provided in the `consts` sub-module.
#![stable(feature = "rust1", since = "1.0.0")]
#![allow(missing_docs)]
#[cfg(not(test))]
-#[cfg(stage0)]
-use core::num::Float;
-#[cfg(not(test))]
use intrinsics;
#[cfg(not(test))]
-#[cfg(stage0)]
-use num::FpCategory;
-#[cfg(not(test))]
use sys::cmath;
#[stable(feature = "rust1", since = "1.0.0")]
@@ -41,14 +35,12 @@ pub use core::f64::{MIN, MIN_POSITIVE, MAX};
pub use core::f64::consts;
#[cfg(not(test))]
-#[cfg_attr(stage0, lang = "f64")]
-#[cfg_attr(not(stage0), lang = "f64_runtime")]
+#[lang = "f64_runtime"]
impl f64 {
- #[cfg(stage0)]
- f64_core_methods!();
-
/// Returns the largest integer less than or equal to a number.
///
+ /// # Examples
+ ///
/// ```
/// let f = 3.99_f64;
/// let g = 3.0_f64;
@@ -64,6 +56,8 @@ impl f64 {
/// Returns the smallest integer greater than or equal to a number.
///
+ /// # Examples
+ ///
/// ```
/// let f = 3.01_f64;
/// let g = 4.0_f64;
@@ -80,6 +74,8 @@ impl f64 {
/// Returns the nearest integer to a number. Round half-way cases away from
/// `0.0`.
///
+ /// # Examples
+ ///
/// ```
/// let f = 3.3_f64;
/// let g = -3.3_f64;
@@ -95,6 +91,8 @@ impl f64 {
/// Returns the integer part of a number.
///
+ /// # Examples
+ ///
/// ```
/// let f = 3.3_f64;
/// let g = -3.7_f64;
@@ -110,6 +108,8 @@ impl f64 {
/// Returns the fractional part of a number.
///
+ /// # Examples
+ ///
/// ```
/// let x = 3.5_f64;
/// let y = -3.5_f64;
@@ -126,6 +126,8 @@ impl f64 {
/// Computes the absolute value of `self`. Returns `NAN` if the
/// number is `NAN`.
///
+ /// # Examples
+ ///
/// ```
/// use std::f64;
///
@@ -152,6 +154,8 @@ impl f64 {
/// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
/// - `NAN` if the number is `NAN`
///
+ /// # Examples
+ ///
/// ```
/// use std::f64;
///
@@ -173,8 +177,12 @@ impl f64 {
}
/// Fused multiply-add. Computes `(self * a) + b` with only one rounding
- /// error. This produces a more accurate result with better performance than
- /// a separate multiplication operation followed by an add.
+ /// error, yielding a more accurate result than an unfused multiply-add.
+ ///
+ /// Using `mul_add` can be more performant than an unfused multiply-add if
+ /// the target architecture has a dedicated `fma` CPU instruction.
+ ///
+ /// # Examples
///
/// ```
/// let m = 10.0_f64;
@@ -199,6 +207,8 @@ impl f64 {
/// In other words, the result is `self / rhs` rounded to the integer `n`
/// such that `self >= n * rhs`.
///
+ /// # Examples
+ ///
/// ```
/// #![feature(euclidean_division)]
/// let a: f64 = 7.0;
@@ -222,6 +232,8 @@ impl f64 {
///
/// In particular, the result `n` satisfies `0 <= n < rhs.abs()`.
///
+ /// # Examples
+ ///
/// ```
/// #![feature(euclidean_division)]
/// let a: f64 = 7.0;
@@ -246,6 +258,8 @@ impl f64 {
///
/// Using this function is generally faster than using `powf`
///
+ /// # Examples
+ ///
/// ```
/// let x = 2.0_f64;
/// let abs_difference = (x.powi(2) - x*x).abs();
@@ -260,6 +274,8 @@ impl f64 {
/// Raises a number to a floating point power.
///
+ /// # Examples
+ ///
/// ```
/// let x = 2.0_f64;
/// let abs_difference = (x.powf(2.0) - x*x).abs();
@@ -276,6 +292,8 @@ impl f64 {
///
/// Returns NaN if `self` is a negative number.
///
+ /// # Examples
+ ///
/// ```
/// let positive = 4.0_f64;
/// let negative = -4.0_f64;
@@ -297,6 +315,8 @@ impl f64 {
/// Returns `e^(self)`, (the exponential function).
///
+ /// # Examples
+ ///
/// ```
/// let one = 1.0_f64;
/// // e^1
@@ -315,6 +335,8 @@ impl f64 {
/// Returns `2^(self)`.
///
+ /// # Examples
+ ///
/// ```
/// let f = 2.0_f64;
///
@@ -331,6 +353,8 @@ impl f64 {
/// Returns the natural logarithm of the number.
///
+ /// # Examples
+ ///
/// ```
/// let one = 1.0_f64;
/// // e^1
@@ -353,6 +377,8 @@ impl f64 {
/// `self.log2()` can produce more accurate results for base 2, and
/// `self.log10()` can produce more accurate results for base 10.
///
+ /// # Examples
+ ///
/// ```
/// let five = 5.0_f64;
///
@@ -367,6 +393,8 @@ impl f64 {
/// Returns the base 2 logarithm of the number.
///
+ /// # Examples
+ ///
/// ```
/// let two = 2.0_f64;
///
@@ -388,6 +416,8 @@ impl f64 {
/// Returns the base 10 logarithm of the number.
///
+ /// # Examples
+ ///
/// ```
/// let ten = 10.0_f64;
///
@@ -407,6 +437,8 @@ impl f64 {
/// * If `self <= other`: `0:0`
/// * Else: `self - other`
///
+ /// # Examples
+ ///
/// ```
/// let x = 3.0_f64;
/// let y = -3.0_f64;
@@ -432,6 +464,8 @@ impl f64 {
/// Takes the cubic root of a number.
///
+ /// # Examples
+ ///
/// ```
/// let x = 8.0_f64;
///
@@ -449,6 +483,8 @@ impl f64 {
/// Calculates the length of the hypotenuse of a right-angle triangle given
/// legs of length `x` and `y`.
///
+ /// # Examples
+ ///
/// ```
/// let x = 2.0_f64;
/// let y = 3.0_f64;
@@ -466,6 +502,8 @@ impl f64 {
/// Computes the sine of a number (in radians).
///
+ /// # Examples
+ ///
/// ```
/// use std::f64;
///
@@ -483,6 +521,8 @@ impl f64 {
/// Computes the cosine of a number (in radians).
///
+ /// # Examples
+ ///
/// ```
/// use std::f64;
///
@@ -500,6 +540,8 @@ impl f64 {
/// Computes the tangent of a number (in radians).
///
+ /// # Examples
+ ///
/// ```
/// use std::f64;
///
@@ -518,6 +560,8 @@ impl f64 {
/// the range [-pi/2, pi/2] or NaN if the number is outside the range
/// [-1, 1].
///
+ /// # Examples
+ ///
/// ```
/// use std::f64;
///
@@ -538,6 +582,8 @@ impl f64 {
/// the range [0, pi] or NaN if the number is outside the range
/// [-1, 1].
///
+ /// # Examples
+ ///
/// ```
/// use std::f64;
///
@@ -557,6 +603,8 @@ impl f64 {
/// Computes the arctangent of a number. Return value is in radians in the
/// range [-pi/2, pi/2];
///
+ /// # Examples
+ ///
/// ```
/// let f = 1.0_f64;
///
@@ -578,6 +626,8 @@ impl f64 {
/// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]`
/// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)`
///
+ /// # Examples
+ ///
/// ```
/// use std::f64;
///
@@ -607,6 +657,8 @@ impl f64 {
/// Simultaneously computes the sine and cosine of the number, `x`. Returns
/// `(sin(x), cos(x))`.
///
+ /// # Examples
+ ///
/// ```
/// use std::f64;
///
@@ -628,6 +680,8 @@ impl f64 {
/// Returns `e^(self) - 1` in a way that is accurate even if the
/// number is close to zero.
///
+ /// # Examples
+ ///
/// ```
/// let x = 7.0_f64;
///
@@ -645,6 +699,8 @@ impl f64 {
/// Returns `ln(1+n)` (natural logarithm) more accurately than if
/// the operations were performed separately.
///
+ /// # Examples
+ ///
/// ```
/// use std::f64;
///
@@ -663,6 +719,8 @@ impl f64 {
/// Hyperbolic sine function.
///
+ /// # Examples
+ ///
/// ```
/// use std::f64;
///
@@ -684,6 +742,8 @@ impl f64 {
/// Hyperbolic cosine function.
///
+ /// # Examples
+ ///
/// ```
/// use std::f64;
///
@@ -705,6 +765,8 @@ impl f64 {
/// Hyperbolic tangent function.
///
+ /// # Examples
+ ///
/// ```
/// use std::f64;
///
@@ -726,6 +788,8 @@ impl f64 {
/// Inverse hyperbolic sine function.
///
+ /// # Examples
+ ///
/// ```
/// let x = 1.0_f64;
/// let f = x.sinh().asinh();
@@ -746,6 +810,8 @@ impl f64 {
/// Inverse hyperbolic cosine function.
///
+ /// # Examples
+ ///
/// ```
/// let x = 1.0_f64;
/// let f = x.cosh().acosh();
@@ -765,6 +831,8 @@ impl f64 {
/// Inverse hyperbolic tangent function.
///
+ /// # Examples
+ ///
/// ```
/// use std::f64;
///
diff --git a/ctr-std/src/ffi/c_str.rs b/ctr-std/src/ffi/c_str.rs
index 7f66b76..52372df 100644
--- a/ctr-std/src/ffi/c_str.rs
+++ b/ctr-std/src/ffi/c_str.rs
@@ -404,9 +404,59 @@ impl CString {
/// let c_string = CString::from_raw(raw);
/// }
/// ```
+ #[cfg(not(target_os = "horizon"))]
#[stable(feature = "cstr_memory", since = "1.4.0")]
pub unsafe fn from_raw(ptr: *mut c_char) -> CString {
- let len = sys::strlen(ptr as *const _) + 1; // Including the NUL byte
+ let len = sys::strlen(ptr) + 1; // Including the NUL byte
+ let slice = slice::from_raw_parts_mut(ptr, len as usize);
+ CString { inner: Box::from_raw(slice as *mut [c_char] as *mut [u8]) }
+ }
+
+
+ /// Retakes ownership of a `CString` that was transferred to C via [`into_raw`].
+ ///
+ /// Additionally, the length of the string will be recalculated from the pointer.
+ ///
+ /// # Safety
+ ///
+ /// This should only ever be called with a pointer that was earlier
+ /// obtained by calling [`into_raw`] on a `CString`. Other usage (e.g. trying to take
+ /// ownership of a string that was allocated by foreign code) is likely to lead
+ /// to undefined behavior or allocator corruption.
+ ///
+ /// > **Note:** If you need to borrow a string that was allocated by
+ /// > foreign code, use [`CStr`]. If you need to take ownership of
+ /// > a string that was allocated by foreign code, you will need to
+ /// > make your own provisions for freeing it appropriately, likely
+ /// > with the foreign code's API to do that.
+ ///
+ /// [`into_raw`]: #method.into_raw
+ /// [`CStr`]: struct.CStr.html
+ ///
+ /// # Examples
+ ///
+ /// Create a `CString`, pass ownership to an `extern` function (via raw pointer), then retake
+ /// ownership with `from_raw`:
+ ///
+ /// ```ignore (extern-declaration)
+ /// use std::ffi::CString;
+ /// use std::os::raw::c_char;
+ ///
+ /// extern {
+ /// fn some_extern_function(s: *mut c_char);
+ /// }
+ ///
+ /// let c_string = CString::new("Hello!").unwrap();
+ /// let raw = c_string.into_raw();
+ /// unsafe {
+ /// some_extern_function(raw);
+ /// let c_string = CString::from_raw(raw);
+ /// }
+ /// ```
+ #[cfg(target_os = "horizon")]
+ #[stable(feature = "cstr_memory", since = "1.4.0")]
+ pub unsafe fn from_raw(ptr: *mut c_char) -> CString {
+ let len = sys::strlen(ptr as *const u8) + 1; // Including the NUL byte
let slice = slice::from_raw_parts_mut(ptr, len as usize);
CString { inner: Box::from_raw(slice as *mut [c_char] as *mut [u8]) }
}
@@ -682,6 +732,14 @@ impl Borrow<CStr> for CString {
fn borrow(&self) -> &CStr { self }
}
+#[stable(feature = "cstring_from_cow_cstr", since = "1.28.0")]
+impl<'a> From<Cow<'a, CStr>> for CString {
+ #[inline]
+ fn from(s: Cow<'a, CStr>) -> Self {
+ s.into_owned()
+ }
+}
+
#[stable(feature = "box_from_c_str", since = "1.17.0")]
impl<'a> From<&'a CStr> for Box<CStr> {
fn from(s: &'a CStr) -> Box<CStr> {
@@ -706,6 +764,30 @@ impl From<CString> for Box<CStr> {
}
}
+#[stable(feature = "cow_from_cstr", since = "1.28.0")]
+impl<'a> From<CString> for Cow<'a, CStr> {
+ #[inline]
+ fn from(s: CString) -> Cow<'a, CStr> {
+ Cow::Owned(s)
+ }
+}
+
+#[stable(feature = "cow_from_cstr", since = "1.28.0")]
+impl<'a> From<&'a CStr> for Cow<'a, CStr> {
+ #[inline]
+ fn from(s: &'a CStr) -> Cow<'a, CStr> {
+ Cow::Borrowed(s)
+ }
+}
+
+#[stable(feature = "cow_from_cstr", since = "1.28.0")]
+impl<'a> From<&'a CString> for Cow<'a, CStr> {
+ #[inline]
+ fn from(s: &'a CString) -> Cow<'a, CStr> {
+ Cow::Borrowed(s.as_c_str())
+ }
+}
+
#[stable(feature = "shared_from_slice2", since = "1.24.0")]
impl From<CString> for Arc<CStr> {
#[inline]
@@ -899,13 +981,56 @@ impl CStr {
/// }
/// # }
/// ```
+ #[cfg(not(target_os = "horizon"))]
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr {
- let len = sys::strlen(ptr as *const _);
+ let len = sys::strlen(ptr);
let ptr = ptr as *const u8;
CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len as usize + 1))
}
+ /// Wraps a raw C string with a safe C string wrapper.
+ ///
+ /// This function will wrap the provided `ptr` with a `CStr` wrapper, which
+ /// allows inspection and interoperation of non-owned C strings. This method
+ /// is unsafe for a number of reasons:
+ ///
+ /// * There is no guarantee to the validity of `ptr`.
+ /// * The returned lifetime is not guaranteed to be the actual lifetime of
+ /// `ptr`.
+ /// * There is no guarantee that the memory pointed to by `ptr` contains a
+ /// valid nul terminator byte at the end of the string.
+ /// * It is not guaranteed that the memory pointed by `ptr` won't change
+ /// before the `CStr` has been destroyed.
+ ///
+ /// > **Note**: This operation is intended to be a 0-cost cast but it is
+ /// > currently implemented with an up-front calculation of the length of
+ /// > the string. This is not guaranteed to always be the case.
+ ///
+ /// # Examples
+ ///
+ /// ```ignore (extern-declaration)
+ /// # fn main() {
+ /// use std::ffi::CStr;
+ /// use std::os::raw::c_char;
+ ///
+ /// extern {
+ /// fn my_string() -> *const c_char;
+ /// }
+ ///
+ /// unsafe {
+ /// let slice = CStr::from_ptr(my_string());
+ /// println!("string returned: {}", slice.to_str().unwrap());
+ /// }
+ /// # }
+ /// ```
+ #[cfg(target_os = "horizon")]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr {
+ let len = sys::strlen(ptr as *const u8);
+ let ptr = ptr as *const u8;
+ CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len as usize + 1))
+ }
/// Creates a C string wrapper from a byte slice.
///
/// This function will cast the provided `bytes` to a `CStr`
diff --git a/ctr-std/src/ffi/os_str.rs b/ctr-std/src/ffi/os_str.rs
index 4850ed0..0a31480 100644
--- a/ctr-std/src/ffi/os_str.rs
+++ b/ctr-std/src/ffi/os_str.rs
@@ -664,6 +664,38 @@ impl<'a> From<&'a OsStr> for Rc<OsStr> {
}
}
+#[stable(feature = "cow_from_osstr", since = "1.28.0")]
+impl<'a> From<OsString> for Cow<'a, OsStr> {
+ #[inline]
+ fn from(s: OsString) -> Cow<'a, OsStr> {
+ Cow::Owned(s)
+ }
+}
+
+#[stable(feature = "cow_from_osstr", since = "1.28.0")]
+impl<'a> From<&'a OsStr> for Cow<'a, OsStr> {
+ #[inline]
+ fn from(s: &'a OsStr) -> Cow<'a, OsStr> {
+ Cow::Borrowed(s)
+ }
+}
+
+#[stable(feature = "cow_from_osstr", since = "1.28.0")]
+impl<'a> From<&'a OsString> for Cow<'a, OsStr> {
+ #[inline]
+ fn from(s: &'a OsString) -> Cow<'a, OsStr> {
+ Cow::Borrowed(s.as_os_str())
+ }
+}
+
+#[stable(feature = "osstring_from_cow_osstr", since = "1.28.0")]
+impl<'a> From<Cow<'a, OsStr>> for OsString {
+ #[inline]
+ fn from(s: Cow<'a, OsStr>) -> Self {
+ s.into_owned()
+ }
+}
+
#[stable(feature = "box_default_extra", since = "1.17.0")]
impl Default for Box<OsStr> {
fn default() -> Box<OsStr> {
diff --git a/ctr-std/src/fs.rs b/ctr-std/src/fs.rs
index 7bd1adc..987687e 100644
--- a/ctr-std/src/fs.rs
+++ b/ctr-std/src/fs.rs
@@ -295,8 +295,6 @@ pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
/// # Examples
///
/// ```no_run
-/// #![feature(fs_read_write)]
-///
/// use std::fs;
/// use std::net::SocketAddr;
///
@@ -331,6 +329,7 @@ pub fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
///
/// fn main() -> std::io::Result<()> {
/// fs::write("foo.txt", b"Lorem ipsum")?;
+/// fs::write("bar.txt", "dolor sit")?;
/// Ok(())
/// }
/// ```
@@ -1699,8 +1698,8 @@ pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
fs_imp::readlink(path.as_ref())
}
-/// Returns the canonical form of a path with all intermediate components
-/// normalized and symbolic links resolved.
+/// Returns the canonical, absolute form of a path with all intermediate
+/// components normalized and symbolic links resolved.
///
/// # Platform-specific behavior
///
@@ -1708,7 +1707,14 @@ pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
/// and the `CreateFile` and `GetFinalPathNameByHandle` functions on Windows.
/// Note that, this [may change in the future][changes].
///
+/// On Windows, this converts the path to use [extended length path][path]
+/// syntax, which allows your program to use longer path names, but means you
+/// can only join backslash-delimited paths to it, and it may be incompatible
+/// with other applications (if passed to the application on the command-line,
+/// or written to a file another application may read).
+///
/// [changes]: ../io/index.html#platform-specific-behavior
+/// [path]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
///
/// # Errors
///
diff --git a/ctr-std/src/lib.rs b/ctr-std/src/lib.rs
index ee237b3..7248a10 100644
--- a/ctr-std/src/lib.rs
+++ b/ctr-std/src/lib.rs
@@ -252,7 +252,6 @@
#![feature(collections_range)]
#![feature(compiler_builtins_lib)]
#![feature(const_fn)]
-#![cfg_attr(stage0, feature(core_float))]
#![feature(core_intrinsics)]
#![feature(dropck_eyepatch)]
#![feature(exact_size_is_empty)]
@@ -260,10 +259,9 @@
#![feature(fs_read_write)]
#![feature(fixed_size_array)]
#![feature(float_from_str_radix)]
-#![cfg_attr(stage0, feature(float_internals))]
#![feature(fn_traits)]
#![feature(fnbox)]
-#![cfg_attr(stage0, feature(generic_param_attrs))]
+#![feature(futures_api)]
#![feature(hashmap_internals)]
#![feature(heap_api)]
#![feature(int_error_internals)]
@@ -277,7 +275,6 @@
#![feature(needs_panic_runtime)]
#![feature(never_type)]
#![feature(exhaustive_patterns)]
-#![feature(nonzero)]
#![feature(num_bits_bytes)]
#![feature(old_wrapping)]
#![feature(on_unimplemented)]
@@ -286,6 +283,7 @@
#![feature(panic_internals)]
#![feature(panic_unwind)]
#![feature(peek)]
+#![feature(pin)]
#![feature(placement_new_protocol)]
#![feature(prelude_import)]
#![feature(ptr_internals)]
@@ -320,6 +318,10 @@
#![cfg_attr(test, feature(update_panic_count))]
#![cfg_attr(windows, feature(used))]
#![feature(doc_alias)]
+#![feature(doc_keyword)]
+#![feature(float_internals)]
+#![feature(panic_info_message)]
+#![cfg_attr(not(stage0), feature(panic_implementation))]
#![default_lib_allocator]
@@ -368,11 +370,6 @@ extern crate ctru_sys as libctru;
#[allow(unused_extern_crates)]
extern crate unwind;
-// compiler-rt intrinsics
-#[doc(masked)]
-#[cfg(stage0)]
-extern crate compiler_builtins;
-
// During testing, this crate is not actually the "real" std library, but rather
// it links to the real std library, which was compiled from this same source
// code. So any lang items std defines are conditionally excluded (or else they
@@ -468,6 +465,20 @@ pub use core::u128;
#[stable(feature = "core_hint", since = "1.27.0")]
pub use core::hint;
+#[unstable(feature = "futures_api",
+ reason = "futures in libcore are unstable",
+ issue = "50547")]
+pub mod task {
+ //! Types and Traits for working with asynchronous tasks.
+ pub use core::task::*;
+ pub use alloc_crate::task::*;
+}
+
+#[unstable(feature = "futures_api",
+ reason = "futures in libcore are unstable",
+ issue = "50547")]
+pub use core::future;
+
pub mod f32;
pub mod f64;
diff --git a/ctr-std/src/macros.rs b/ctr-std/src/macros.rs
index d1274a4..8da70f5 100644
--- a/ctr-std/src/macros.rs
+++ b/ctr-std/src/macros.rs
@@ -364,7 +364,6 @@ pub mod builtin {
///
/// let s = fmt::format(format_args!("hello {}", "world"));
/// assert_eq!(s, format!("hello {}", "world"));
- ///
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[macro_export]
@@ -379,7 +378,7 @@ pub mod builtin {
/// compile time, yielding an expression of type `&'static str`.
///
/// If the environment variable is not defined, then a compilation error
- /// will be emitted. To not emit a compile error, use the [`option_env!`]
+ /// will be emitted. To not emit a compile error, use the [`option_env!`]
/// macro instead.
///
/// [`option_env!`]: ../std/macro.option_env.html
@@ -390,6 +389,20 @@ pub mod builtin {
/// let path: &'static str = env!("PATH");
/// println!("the $PATH variable at the time of compiling was: {}", path);
/// ```
+ ///
+ /// You can customize the error message by passing a string as the second
+ /// parameter:
+ ///
+ /// ```compile_fail
+ /// let doc: &'static str = env!("documentation", "what's that?!");
+ /// ```
+ ///
+ /// If the `documentation` environment variable is not defined, you'll get
+ /// the following error:
+ ///
+ /// ```text
+ /// error: what's that?!
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[macro_export]
macro_rules! env {
diff --git a/ctr-std/src/num.rs b/ctr-std/src/num.rs
index 4b975dd..3f90c1f 100644
--- a/ctr-std/src/num.rs
+++ b/ctr-std/src/num.rs
@@ -21,12 +21,8 @@ pub use core::num::{FpCategory, ParseIntError, ParseFloatError, TryFromIntError}
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::num::Wrapping;
-#[unstable(feature = "nonzero", issue = "49137")]
-#[allow(deprecated)]
-pub use core::num::{
- NonZeroU8, NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32,
- NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize,
-};
+#[stable(feature = "nonzero", since = "1.28.0")]
+pub use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize};
#[cfg(test)] use fmt;
#[cfg(test)] use ops::{Add, Sub, Mul, Div, Rem};
diff --git a/ctr-std/src/os/horizon/fs.rs b/ctr-std/src/os/horizon/fs.rs
new file mode 100644
index 0000000..12e8719
--- /dev/null
+++ b/ctr-std/src/os/horizon/fs.rs
@@ -0,0 +1,393 @@
+// 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.
+
+#![stable(feature = "metadata_ext", since = "1.1.0")]
+
+use libc;
+
+use fs::Metadata;
+use sys_common::AsInner;
+
+#[allow(deprecated)]
+use os::horizon::raw;
+
+/// OS-specific extensions to [`fs::Metadata`].
+///
+/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+pub trait MetadataExt {
+ /// Gain a reference to the underlying `stat` structure which contains
+ /// the raw information returned by the OS.
+ ///
+ /// The contents of the returned [`stat`] are **not** consistent across
+ /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the
+ /// cross-Unix abstractions contained within the raw stat.
+ ///
+ /// [`stat`]: ../../../../std/os/linux/raw/struct.stat.html
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let stat = meta.as_raw_stat();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ #[rustc_deprecated(since = "1.8.0",
+ reason = "deprecated in favor of the accessor \
+ methods of this trait")]
+ #[allow(deprecated)]
+ fn as_raw_stat(&self) -> &raw::stat;
+
+ /// Returns the device ID on which this file resides.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_dev());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_dev(&self) -> u64;
+ /// Returns the inode number.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_ino());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_ino(&self) -> u64;
+ /// Returns the file type and mode.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_mode());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_mode(&self) -> u32;
+ /// Returns the number of hard links to file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_nlink());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_nlink(&self) -> u64;
+ /// Returns the user ID of the file owner.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_uid());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_uid(&self) -> u32;
+ /// Returns the group ID of the file owner.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_gid());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_gid(&self) -> u32;
+ /// Returns the device ID that this file represents. Only relevant for special file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_rdev());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_rdev(&self) -> u64;
+ /// Returns the size of the file (if it is a regular file or a symbolic link) in bytes.
+ ///
+ /// The size of a symbolic link is the length of the pathname it contains,
+ /// without a terminating null byte.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_size());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_size(&self) -> u64;
+ /// Returns the last access time.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_atime());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_atime(&self) -> i64;
+ /// Returns the last access time, nano seconds part.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_atime_nsec());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_atime_nsec(&self) -> i64;
+ /// Returns the last modification time.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_mtime());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_mtime(&self) -> i64;
+ /// Returns the last modification time, nano seconds part.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_mtime_nsec());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_mtime_nsec(&self) -> i64;
+ /// Returns the last status change time.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_ctime());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_ctime(&self) -> i64;
+ /// Returns the last status change time, nano seconds part.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_ctime_nsec());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_ctime_nsec(&self) -> i64;
+ /// Returns the "preferred" blocksize for efficient filesystem I/O.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_blksize());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_blksize(&self) -> u64;
+ /// Returns the number of blocks allocated to the file, 512-byte units.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::linux::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_blocks());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_blocks(&self) -> u64;
+}
+
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+impl MetadataExt for Metadata {
+ #[allow(deprecated)]
+ fn as_raw_stat(&self) -> &raw::stat {
+ unsafe {
+ &*(self.as_inner().as_inner() as *const libc::stat
+ as *const raw::stat)
+ }
+ }
+ fn st_dev(&self) -> u64 {
+ self.as_inner().as_inner().st_dev as u64
+ }
+ fn st_ino(&self) -> u64 {
+ self.as_inner().as_inner().st_ino as u64
+ }
+ fn st_mode(&self) -> u32 {
+ self.as_inner().as_inner().st_mode as u32
+ }
+ fn st_nlink(&self) -> u64 {
+ self.as_inner().as_inner().st_nlink as u64
+ }
+ fn st_uid(&self) -> u32 {
+ self.as_inner().as_inner().st_uid as u32
+ }
+ fn st_gid(&self) -> u32 {
+ self.as_inner().as_inner().st_gid as u32
+ }
+ fn st_rdev(&self) -> u64 {
+ self.as_inner().as_inner().st_rdev as u64
+ }
+ fn st_size(&self) -> u64 {
+ self.as_inner().as_inner().st_size as u64
+ }
+ fn st_atime(&self) -> i64 {
+ self.as_inner().as_inner().st_atime as i64
+ }
+ fn st_atime_nsec(&self) -> i64 {
+ //self.as_inner().as_inner().st_atime_nsec as i64
+ 0i64
+ }
+ fn st_mtime(&self) -> i64 {
+ self.as_inner().as_inner().st_mtime as i64
+ }
+ fn st_mtime_nsec(&self) -> i64 {
+ //self.as_inner().as_inner().st_mtime_nsec as i64
+ 0i64
+ }
+ fn st_ctime(&self) -> i64 {
+ //self.as_inner().as_inner().st_ctime as i64
+ 0i64
+ }
+ fn st_ctime_nsec(&self) -> i64 {
+ //self.as_inner().as_inner().st_ctime_nsec as i64
+ 0i64
+ }
+ fn st_blksize(&self) -> u64 {
+ self.as_inner().as_inner().st_blksize as u64
+ }
+ fn st_blocks(&self) -> u64 {
+ self.as_inner().as_inner().st_blocks as u64
+ }
+}
diff --git a/ctr-std/src/os/horizon/mod.rs b/ctr-std/src/os/horizon/mod.rs
new file mode 100644
index 0000000..8ec44b9
--- /dev/null
+++ b/ctr-std/src/os/horizon/mod.rs
@@ -0,0 +1,16 @@
+// 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.
+
+//! Linux-specific definitions
+
+#![stable(feature = "raw_ext", since = "1.1.0")]
+
+pub mod raw;
+pub mod fs;
diff --git a/ctr-std/src/os/horizon/raw.rs b/ctr-std/src/os/horizon/raw.rs
new file mode 100644
index 0000000..bb1830a
--- /dev/null
+++ b/ctr-std/src/os/horizon/raw.rs
@@ -0,0 +1,275 @@
+// 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.
+
+//! Linux-specific raw type definitions
+
+#![stable(feature = "raw_ext", since = "1.1.0")]
+#![rustc_deprecated(since = "1.8.0",
+ reason = "these type aliases are no longer supported by \
+ the standard library, the `libc` crate on \
+ crates.io should be used instead for the correct \
+ definitions")]
+#![allow(deprecated)]
+#![allow(missing_debug_implementations)]
+
+use os::raw::c_ulong;
+
+#[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64;
+#[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = u32;
+
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = c_ulong;
+
+#[doc(inline)]
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub use self::arch::{off_t, ino_t, nlink_t, blksize_t, blkcnt_t, stat, time_t};
+
+#[cfg(any(target_arch = "x86",
+ target_arch = "le32",
+ target_arch = "powerpc",
+ target_arch = "arm",
+ target_arch = "asmjs",
+ target_arch = "wasm32"))]
+mod arch {
+ use os::raw::{c_long, c_short, c_uint};
+
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
+
+ #[repr(C)]
+ #[derive(Clone)]
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub struct stat {
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_dev: u64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub __pad1: c_short,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub __st_ino: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mode: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_nlink: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_uid: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_gid: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_rdev: u64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub __pad2: c_uint,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_size: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_blksize: i32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_blocks: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_atime: i32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_atime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mtime: i32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mtime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ctime: i32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ctime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ino: u64,
+ }
+}
+
+#[cfg(target_arch = "mips")]
+mod arch {
+ use os::raw::{c_long, c_ulong};
+
+ #[cfg(target_env = "musl")]
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = i64;
+ #[cfg(not(target_env = "musl"))]
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u64;
+ #[cfg(target_env = "musl")]
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64;
+ #[cfg(not(target_env = "musl"))]
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u64;
+ #[cfg(target_env = "musl")]
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
+ #[cfg(not(target_env = "musl"))]
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
+
+ #[repr(C)]
+ #[derive(Clone)]
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub struct stat {
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_dev: c_ulong,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_pad1: [c_long; 3],
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ino: u64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mode: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_nlink: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_uid: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_gid: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_rdev: c_ulong,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_pad2: [c_long; 2],
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_size: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_atime: i32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_atime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mtime: i32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mtime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ctime: i32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ctime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_blksize: i32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_blocks: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_pad5: [c_long; 14],
+ }
+}
+
+#[cfg(any(target_arch = "mips64",
+ target_arch = "s390x",
+ target_arch = "sparc64"))]
+mod arch {
+ pub use libc::{off_t, ino_t, nlink_t, blksize_t, blkcnt_t, stat, time_t};
+}
+
+#[cfg(target_arch = "aarch64")]
+mod arch {
+ use os::raw::{c_long, c_int};
+
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
+
+ #[repr(C)]
+ #[derive(Clone)]
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub struct stat {
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_dev: u64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ino: u64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mode: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_nlink: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_uid: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_gid: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_rdev: u64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub __pad1: u64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_size: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_blksize: i32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub __pad2: c_int,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_blocks: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_atime: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_atime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mtime: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mtime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ctime: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ctime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub __unused: [c_int; 2],
+ }
+}
+
+#[cfg(any(target_arch = "x86_64", target_arch = "powerpc64"))]
+mod arch {
+ use os::raw::{c_long, c_int};
+
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
+ #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
+
+ #[repr(C)]
+ #[derive(Clone)]
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub struct stat {
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_dev: u64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ino: u64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_nlink: u64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mode: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_uid: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_gid: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub __pad0: c_int,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_rdev: u64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_size: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_blksize: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_blocks: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_atime: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_atime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mtime: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mtime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ctime: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ctime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub __unused: [c_long; 3],
+ }
+}
diff --git a/ctr-std/src/os/mod.rs b/ctr-std/src/os/mod.rs
index ac78094..0bf1b0d 100644
--- a/ctr-std/src/os/mod.rs
+++ b/ctr-std/src/os/mod.rs
@@ -47,6 +47,7 @@ cfg_if! {
#[cfg(target_os = "solaris")] pub mod solaris;
#[cfg(target_os = "emscripten")] pub mod emscripten;
#[cfg(target_os = "fuchsia")] pub mod fuchsia;
+ #[cfg(target_os = "horizon")] pub mod horizon;
#[cfg(any(target_os = "redox", unix))]
#[stable(feature = "rust1", since = "1.0.0")]
diff --git a/ctr-std/src/panicking.rs b/ctr-std/src/panicking.rs
index ad0a337..321d634 100644
--- a/ctr-std/src/panicking.rs
+++ b/ctr-std/src/panicking.rs
@@ -76,7 +76,9 @@ static mut HOOK: Hook = Hook::Default;
/// is invoked. As such, the hook will run with both the aborting and unwinding
/// runtimes. The default hook prints a message to standard error and generates
/// a backtrace if requested, but this behavior can be customized with the
-/// `set_hook` and `take_hook` functions.
+/// `set_hook` and [`take_hook`] functions.
+///
+/// [`take_hook`]: ./fn.take_hook.html
///
/// The hook is provided with a `PanicInfo` struct which contains information
/// about the origin of the panic, including the payload passed to `panic!` and
@@ -121,6 +123,10 @@ pub fn set_hook(hook: Box<Fn(&PanicInfo) + 'static + Sync + Send>) {
/// Unregisters the current panic hook, returning it.
///
+/// *See also the function [`set_hook`].*
+///
+/// [`set_hook`]: ./fn.set_hook.html
+///
/// If no custom hook is registered, the default hook will be returned.
///
/// # Panics
@@ -217,7 +223,8 @@ fn default_hook(info: &PanicInfo) {
}
let write = |err: &mut ::io::Write| {
- let _ = write!(err, "{}", error_text);
+ let _ = writeln!(err, "thread '{}' panicked at '{}', {}",
+ name, msg, location);
#[cfg(feature = "backtrace")]
{
@@ -337,8 +344,8 @@ pub fn panicking() -> bool {
/// Entry point of panic from the libcore crate.
#[cfg(not(test))]
+#[cfg(stage0)]
#[lang = "panic_fmt"]
-#[unwind(allowed)]
pub extern fn rust_begin_panic(msg: fmt::Arguments,
file: &'static str,
line: u32,
@@ -346,59 +353,107 @@ pub extern fn rust_begin_panic(msg: fmt::Arguments,
begin_panic_fmt(&msg, &(file, line, col))
}
+/// Entry point of panic from the libcore crate.
+#[cfg(not(test))]
+#[cfg(not(stage0))]
+#[panic_implementation]
+#[unwind(allowed)]
+pub fn rust_begin_panic(info: &PanicInfo) -> ! {
+ continue_panic_fmt(&info)
+}
+
/// The entry point for panicking with a formatted message.
///
/// This is designed to reduce the amount of code required at the call
/// site as much as possible (so that `panic!()` has as low an impact
/// on (e.g.) the inlining of other functions as possible), by moving
/// the actual formatting into this shared place.
+#[cfg(stage0)]
#[unstable(feature = "libstd_sys_internals",
reason = "used by the panic! macro",
issue = "0")]
#[inline(never)] #[cold]
pub fn begin_panic_fmt(msg: &fmt::Arguments,
file_line_col: &(&'static str, u32, u32)) -> ! {
- use fmt::Write;
-
// We do two allocations here, unfortunately. But (a) they're
// required with the current scheme, and (b) we don't handle
// panic + OOM properly anyway (see comment in begin_panic
// below).
rust_panic_with_hook(&mut PanicPayload::new(msg), Some(msg), file_line_col);
+}
- struct PanicPayload<'a> {
- inner: &'a fmt::Arguments<'a>,
- string: Option<String>,
+// NOTE(stage0) move into `continue_panic_fmt` on next stage0 update
+struct PanicPayload<'a> {
+ inner: &'a fmt::Arguments<'a>,
+ string: Option<String>,
+}
+
+impl<'a> PanicPayload<'a> {
+ fn new(inner: &'a fmt::Arguments<'a>) -> PanicPayload<'a> {
+ PanicPayload { inner, string: None }
}
- impl<'a> PanicPayload<'a> {
- fn new(inner: &'a fmt::Arguments<'a>) -> PanicPayload<'a> {
- PanicPayload { inner, string: None }
- }
+ fn fill(&mut self) -> &mut String {
+ use fmt::Write;
- fn fill(&mut self) -> &mut String {
- let inner = self.inner;
- self.string.get_or_insert_with(|| {
- let mut s = String::new();
- drop(s.write_fmt(*inner));
- s
- })
- }
+ let inner = self.inner;
+ self.string.get_or_insert_with(|| {
+ let mut s = String::new();
+ drop(s.write_fmt(*inner));
+ s
+ })
}
+}
- unsafe impl<'a> BoxMeUp for PanicPayload<'a> {
- fn box_me_up(&mut self) -> *mut (Any + Send) {
- let contents = mem::replace(self.fill(), String::new());
- Box::into_raw(Box::new(contents))
- }
+unsafe impl<'a> BoxMeUp for PanicPayload<'a> {
+ fn box_me_up(&mut self) -> *mut (Any + Send) {
+ let contents = mem::replace(self.fill(), String::new());
+ Box::into_raw(Box::new(contents))
+ }
- fn get(&mut self) -> &(Any + Send) {
- self.fill()
- }
+ fn get(&mut self) -> &(Any + Send) {
+ self.fill()
}
}
+/// The entry point for panicking with a formatted message.
+///
+/// This is designed to reduce the amount of code required at the call
+/// site as much as possible (so that `panic!()` has as low an impact
+/// on (e.g.) the inlining of other functions as possible), by moving
+/// the actual formatting into this shared place.
+#[cfg(not(stage0))]
+#[unstable(feature = "libstd_sys_internals",
+ reason = "used by the panic! macro",
+ issue = "0")]
+#[inline(never)] #[cold]
+pub fn begin_panic_fmt(msg: &fmt::Arguments,
+ file_line_col: &(&'static str, u32, u32)) -> ! {
+ let (file, line, col) = *file_line_col;
+ let info = PanicInfo::internal_constructor(
+ Some(msg),
+ Location::internal_constructor(file, line, col),
+ );
+ continue_panic_fmt(&info)
+}
+
+#[cfg(not(stage0))]
+fn continue_panic_fmt(info: &PanicInfo) -> ! {
+ // We do two allocations here, unfortunately. But (a) they're
+ // required with the current scheme, and (b) we don't handle
+ // panic + OOM properly anyway (see comment in begin_panic
+ // below).
+
+ let loc = info.location().unwrap(); // The current implementation always returns Some
+ let msg = info.message().unwrap(); // The current implementation always returns Some
+ let file_line_col = (loc.file(), loc.line(), loc.column());
+ rust_panic_with_hook(
+ &mut PanicPayload::new(msg),
+ info.message(),
+ &file_line_col);
+}
+
/// This is the entry point of panicking for panic!() and assert!().
#[unstable(feature = "libstd_sys_internals",
reason = "used by the panic! macro",
@@ -449,7 +504,7 @@ pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u3
/// abort or unwind.
fn rust_panic_with_hook(payload: &mut BoxMeUp,
message: Option<&fmt::Arguments>,
- file_line_col: &(&'static str, u32, u32)) -> ! {
+ file_line_col: &(&str, u32, u32)) -> ! {
let (file, line, col) = *file_line_col;
let panics = update_panic_count(1);
diff --git a/ctr-std/src/path.rs b/ctr-std/src/path.rs
index 861c7dd..27f57bd 100644
--- a/ctr-std/src/path.rs
+++ b/ctr-std/src/path.rs
@@ -201,10 +201,10 @@ impl<'a> Prefix<'a> {
os_str_as_u8_slice(s).len()
}
match *self {
- #[cfg(target_os = "horizon")]
+ #[cfg(target_os = "horizon")]
+ Verbatim(x) => 1 + os_str_len(x),
+ #[cfg(not(target_os = "horizon"))]
Verbatim(x) => 1 + os_str_len(x),
- #[cfg(target_os = "windows")]
- Verbatim(x) => 4 + os_str_len(x),
VerbatimUNC(x, y) => {
8 + os_str_len(x) +
if os_str_len(y) > 0 {
@@ -1463,7 +1463,7 @@ impl<P: AsRef<Path>> iter::Extend<P> for PathBuf {
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Debug for PathBuf {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&**self, formatter)
}
}
@@ -1507,6 +1507,22 @@ impl<'a> From<PathBuf> for Cow<'a, Path> {
}
}
+#[stable(feature = "cow_from_pathbuf_ref", since = "1.28.0")]
+impl<'a> From<&'a PathBuf> for Cow<'a, Path> {
+ #[inline]
+ fn from(p: &'a PathBuf) -> Cow<'a, Path> {
+ Cow::Borrowed(p.as_path())
+ }
+}
+
+#[stable(feature = "pathbuf_from_cow_path", since = "1.28.0")]
+impl<'a> From<Cow<'a, Path>> for PathBuf {
+ #[inline]
+ fn from(p: Cow<'a, Path>) -> Self {
+ p.into_owned()
+ }
+}
+
#[stable(feature = "shared_from_slice2", since = "1.24.0")]
impl From<PathBuf> for Arc<Path> {
#[inline]
@@ -2287,8 +2303,8 @@ impl Path {
fs::symlink_metadata(self)
}
- /// Returns the canonical form of the path with all intermediate components
- /// normalized and symbolic links resolved.
+ /// Returns the canonical, absolute form of the path with all intermediate
+ /// components normalized and symbolic links resolved.
///
/// This is an alias to [`fs::canonicalize`].
///
diff --git a/ctr-std/src/primitive_docs.rs b/ctr-std/src/primitive_docs.rs
index 919d964..7074928 100644
--- a/ctr-std/src/primitive_docs.rs
+++ b/ctr-std/src/primitive_docs.rs
@@ -116,6 +116,8 @@ mod prim_bool { }
///
/// # `!` and generics
///
+/// ## Infallible errors
+///
/// The main place you'll see `!` used explicitly is in generic code. Consider the [`FromStr`]
/// trait:
///
@@ -144,9 +146,60 @@ mod prim_bool { }
/// [`Ok`] variant. This illustrates another behaviour of `!` - it can be used to "delete" certain
/// enum variants from generic types like `Result`.
///
+/// ## Infinite loops
+///
+/// While [`Result<T, !>`] is very useful for removing errors, `!` can also be used to remove
+/// successes as well. If we think of [`Result<T, !>`] as "if this function returns, it has not
+/// errored," we get a very intuitive idea of [`Result<!, E>`] as well: if the function returns, it
+/// *has* errored.
+///
+/// For example, consider the case of a simple web server, which can be simplified to:
+///
+/// ```ignore (hypothetical-example)
+/// loop {
+/// let (client, request) = get_request().expect("disconnected");
+/// let response = request.process();
+/// response.send(client);
+/// }
+/// ```
+///
+/// Currently, this isn't ideal, because we simply panic whenever we fail to get a new connection.
+/// Instead, we'd like to keep track of this error, like this:
+///
+/// ```ignore (hypothetical-example)
+/// loop {
+/// match get_request() {
+/// Err(err) => break err,
+/// Ok((client, request)) => {
+/// let response = request.process();
+/// response.send(client);
+/// },
+/// }
+/// }
+/// ```
+///
+/// Now, when the server disconnects, we exit the loop with an error instead of panicking. While it
+/// might be intuitive to simply return the error, we might want to wrap it in a [`Result<!, E>`]
+/// instead:
+///
+/// ```ignore (hypothetical-example)
+/// fn server_loop() -> Result<!, ConnectionError> {
+/// loop {
+/// let (client, request) = get_request()?;
+/// let response = request.process();
+/// response.send(client);
+/// }
+/// }
+/// ```
+///
+/// Now, we can use `?` instead of `match`, and the return type makes a lot more sense: if the loop
+/// ever stops, it means that an error occurred. We don't even have to wrap the loop in an `Ok`
+/// because `!` coerces to `Result<!, ConnectionError>` automatically.
+///
/// [`String::from_str`]: str/trait.FromStr.html#tymethod.from_str
/// [`Result<String, !>`]: result/enum.Result.html
/// [`Result<T, !>`]: result/enum.Result.html
+/// [`Result<!, E>`]: result/enum.Result.html
/// [`Ok`]: result/enum.Result.html#variant.Ok
/// [`String`]: string/struct.String.html
/// [`Err`]: result/enum.Result.html#variant.Err
@@ -317,6 +370,8 @@ mod prim_unit { }
//
/// Raw, unsafe pointers, `*const T`, and `*mut T`.
///
+/// *[See also the `std::ptr` module](ptr/index.html).*
+///
/// Working with raw pointers in Rust is uncommon,
/// typically limited to a few patterns.
///
@@ -391,8 +446,6 @@ mod prim_unit { }
/// but C APIs hand out a lot of pointers generally, so are a common source
/// of raw pointers in Rust.
///
-/// *[See also the `std::ptr` module](ptr/index.html).*
-///
/// [`null`]: ../std/ptr/fn.null.html
/// [`null_mut`]: ../std/ptr/fn.null_mut.html
/// [`is_null`]: ../std/primitive.pointer.html#method.is_null
@@ -510,6 +563,8 @@ mod prim_array { }
//
/// A dynamically-sized view into a contiguous sequence, `[T]`.
///
+/// *[See also the `std::slice` module](slice/index.html).*
+///
/// Slices are a view into a block of memory represented as a pointer and a
/// length.
///
@@ -532,8 +587,6 @@ mod prim_array { }
/// assert_eq!(x, &[1, 7, 3]);
/// ```
///
-/// *[See also the `std::slice` module](slice/index.html).*
-///
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_slice { }
@@ -541,15 +594,13 @@ mod prim_slice { }
//
/// String slices.
///
+/// *[See also the `std::str` module](str/index.html).*
+///
/// The `str` type, also called a 'string slice', is the most primitive string
/// type. It is usually seen in its borrowed form, `&str`. It is also the type
/// of string literals, `&'static str`.
///
-/// Strings slices are always valid UTF-8.
-///
-/// This documentation describes a number of methods and trait implementations
-/// on the `str` type. For technical reasons, there is additional, separate
-/// documentation in the [`std::str`](str/index.html) module as well.
+/// String slices are always valid UTF-8.
///
/// # Examples
///
@@ -809,11 +860,11 @@ mod prim_u128 { }
//
/// The pointer-sized signed integer type.
///
+/// *[See also the `std::isize` module](isize/index.html).*
+///
/// The size of this primitive is how many bytes it takes to reference any
/// location in memory. For example, on a 32 bit target, this is 4 bytes
/// and on a 64 bit target, this is 8 bytes.
-///
-/// *[See also the `std::isize` module](isize/index.html).*
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_isize { }
@@ -821,11 +872,11 @@ mod prim_isize { }
//
/// The pointer-sized unsigned integer type.
///
+/// *[See also the `std::usize` module](usize/index.html).*
+///
/// The size of this primitive is how many bytes it takes to reference any
/// location in memory. For example, on a 32 bit target, this is 4 bytes
/// and on a 64 bit target, this is 8 bytes.
-///
-/// *[See also the `std::usize` module](usize/index.html).*
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_usize { }
diff --git a/ctr-std/src/process.rs b/ctr-std/src/process.rs
index 00051d4..732f43c 100644
--- a/ctr-std/src/process.rs
+++ b/ctr-std/src/process.rs
@@ -1099,7 +1099,7 @@ impl fmt::Display for ExitStatus {
/// [RFC #1937]: https://github.com/rust-lang/rfcs/pull/1937
#[derive(Clone, Copy, Debug)]
#[unstable(feature = "process_exitcode_placeholder", issue = "48711")]
-pub struct ExitCode(imp::ExitCode);
+pub struct ExitCode(pub u8);
#[unstable(feature = "process_exitcode_placeholder", issue = "48711")]
impl ExitCode {
@@ -1109,7 +1109,7 @@ impl ExitCode {
/// termination, so there's no need to return this from `main` unless
/// you're also returning other possible codes.
#[unstable(feature = "process_exitcode_placeholder", issue = "48711")]
- pub const SUCCESS: ExitCode = ExitCode(imp::ExitCode::SUCCESS);
+ pub const SUCCESS: ExitCode = ExitCode(0 as _);
/// The canonical ExitCode for unsuccessful termination on this platform.
///
@@ -1117,7 +1117,7 @@ impl ExitCode {
/// instead returning `Err(_)` and `Ok(())` respectively, which will
/// return the same codes (but will also `eprintln!` the error).
#[unstable(feature = "process_exitcode_placeholder", issue = "48711")]
- pub const FAILURE: ExitCode = ExitCode(imp::ExitCode::FAILURE);
+ pub const FAILURE: ExitCode = ExitCode(-1 as _);
}
impl Child {
@@ -1493,7 +1493,7 @@ impl<E: fmt::Debug> Termination for Result<!, E> {
impl Termination for ExitCode {
#[inline]
fn report(self) -> i32 {
- self.0.as_i32()
+ self.0 as i32
}
}
diff --git a/ctr-std/src/sync/mutex.rs b/ctr-std/src/sync/mutex.rs
index 3b4904c..f3503b0 100644
--- a/ctr-std/src/sync/mutex.rs
+++ b/ctr-std/src/sync/mutex.rs
@@ -150,7 +150,7 @@ unsafe impl<T: ?Sized + Send> Sync for Mutex<T> { }
/// [`lock`]: struct.Mutex.html#method.lock
/// [`try_lock`]: struct.Mutex.html#method.try_lock
/// [`Mutex`]: struct.Mutex.html
-#[must_use]
+#[must_use = "if unused the Mutex will immediately unlock"]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct MutexGuard<'a, T: ?Sized + 'a> {
// funny underscores due to how Deref/DerefMut currently work (they
diff --git a/ctr-std/src/sync/once.rs b/ctr-std/src/sync/once.rs
index 6fd8b6a..7eb7be2 100644
--- a/ctr-std/src/sync/once.rs
+++ b/ctr-std/src/sync/once.rs
@@ -73,16 +73,17 @@ use thread::{self, Thread};
/// A synchronization primitive which can be used to run a one-time global
/// initialization. Useful for one-time initialization for FFI or related
/// functionality. This type can only be constructed with the [`ONCE_INIT`]
-/// value.
+/// value or the equivalent [`Once::new`] constructor.
///
/// [`ONCE_INIT`]: constant.ONCE_INIT.html
+/// [`Once::new`]: struct.Once.html#method.new
///
/// # Examples
///
/// ```
-/// use std::sync::{Once, ONCE_INIT};
+/// use std::sync::Once;
///
-/// static START: Once = ONCE_INIT;
+/// static START: Once = Once::new();
///
/// START.call_once(|| {
/// // run initialization here
@@ -180,10 +181,10 @@ impl Once {
/// # Examples
///
/// ```
- /// use std::sync::{Once, ONCE_INIT};
+ /// use std::sync::Once;
///
/// static mut VAL: usize = 0;
- /// static INIT: Once = ONCE_INIT;
+ /// static INIT: Once = Once::new();
///
/// // Accessing a `static mut` is unsafe much of the time, but if we do so
/// // in a synchronized fashion (e.g. write once or read all) then we're
@@ -248,10 +249,10 @@ impl Once {
/// ```
/// #![feature(once_poison)]
///
- /// use std::sync::{Once, ONCE_INIT};
+ /// use std::sync::Once;
/// use std::thread;
///
- /// static INIT: Once = ONCE_INIT;
+ /// static INIT: Once = Once::new();
///
/// // poison the once
/// let handle = thread::spawn(|| {
@@ -431,10 +432,10 @@ impl OnceState {
/// ```
/// #![feature(once_poison)]
///
- /// use std::sync::{Once, ONCE_INIT};
+ /// use std::sync::Once;
/// use std::thread;
///
- /// static INIT: Once = ONCE_INIT;
+ /// static INIT: Once = Once::new();
///
/// // poison the once
/// let handle = thread::spawn(|| {
@@ -452,9 +453,9 @@ impl OnceState {
/// ```
/// #![feature(once_poison)]
///
- /// use std::sync::{Once, ONCE_INIT};
+ /// use std::sync::Once;
///
- /// static INIT: Once = ONCE_INIT;
+ /// static INIT: Once = Once::new();
///
/// INIT.call_once_force(|state| {
/// assert!(!state.poisoned());
diff --git a/ctr-std/src/sync/rwlock.rs b/ctr-std/src/sync/rwlock.rs
index f7fdedc..e3db60c 100644
--- a/ctr-std/src/sync/rwlock.rs
+++ b/ctr-std/src/sync/rwlock.rs
@@ -94,7 +94,7 @@ unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {}
/// [`read`]: struct.RwLock.html#method.read
/// [`try_read`]: struct.RwLock.html#method.try_read
/// [`RwLock`]: struct.RwLock.html
-#[must_use]
+#[must_use = "if unused the RwLock will immediately unlock"]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct RwLockReadGuard<'a, T: ?Sized + 'a> {
__lock: &'a RwLock<T>,
@@ -115,7 +115,7 @@ unsafe impl<'a, T: ?Sized + Sync> Sync for RwLockReadGuard<'a, T> {}
/// [`write`]: struct.RwLock.html#method.write
/// [`try_write`]: struct.RwLock.html#method.try_write
/// [`RwLock`]: struct.RwLock.html
-#[must_use]
+#[must_use = "if unused the RwLock will immediately unlock"]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> {
__lock: &'a RwLock<T>,
diff --git a/ctr-std/src/sys/cloudabi/abi/bitflags.rs b/ctr-std/src/sys/cloudabi/abi/bitflags.rs
new file mode 100644
index 0000000..f764cc1
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/abi/bitflags.rs
@@ -0,0 +1,51 @@
+// Copyright (c) 2018 Nuxi (https://nuxi.nl/) and contributors.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+// Appease Rust's tidy.
+// ignore-license
+
+#[cfg(feature = "bitflags")]
+#[macro_use]
+extern crate bitflags;
+
+// Minimal implementation of bitflags! in case we can't depend on the bitflags
+// crate. Only implements `bits()` and a `from_bits_truncate()` that doesn't
+// actually truncate.
+#[cfg(not(feature = "bitflags"))]
+macro_rules! bitflags {
+ (
+ $(#[$attr:meta])*
+ pub struct $name:ident: $type:ty {
+ $($(#[$const_attr:meta])* const $const:ident = $val:expr;)*
+ }
+ ) => {
+ $(#[$attr])*
+ #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+ pub struct $name { bits: $type }
+ impl $name {
+ $($(#[$const_attr])* pub const $const: $name = $name{ bits: $val };)*
+ pub fn bits(&self) -> $type { self.bits }
+ pub fn from_bits_truncate(bits: $type) -> Self { $name{ bits } }
+ }
+ }
+}
diff --git a/ctr-std/src/sys/cloudabi/abi/cloudabi.rs b/ctr-std/src/sys/cloudabi/abi/cloudabi.rs
new file mode 100644
index 0000000..2909db5
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/abi/cloudabi.rs
@@ -0,0 +1,2847 @@
+// Copyright (c) 2016-2017 Nuxi (https://nuxi.nl/) and contributors.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+//
+// This file is automatically generated. Do not edit.
+//
+// Source: https://github.com/NuxiNL/cloudabi
+
+// Appease Rust's tidy.
+// ignore-license
+// ignore-tidy-linelength
+
+//! **PLEASE NOTE: This entire crate including this
+//! documentation is automatically generated from
+//! [`cloudabi.txt`](https://github.com/NuxiNL/cloudabi/blob/master/cloudabi.txt)**
+//!
+//! # Nuxi CloudABI
+//!
+//! CloudABI is what you get if you take POSIX, add capability-based
+//! security, and remove everything that's incompatible with that. The
+//! result is a minimal ABI consisting of only 49 syscalls.
+//!
+//! CloudABI doesn't have its own kernel, but instead is implemented in existing
+//! kernels: FreeBSD has CloudABI support for x86-64 and arm64, and [a patch-set
+//! for NetBSD](https://github.com/NuxiNL/netbsd) and [a patch-set for
+//! Linux](https://github.com/NuxiNL/linux) are available as well. This means that
+//! CloudABI binaries can be executed on different operating systems, without any
+//! modification.
+//!
+//! ## Capability-Based Security
+//!
+//! Capability-based security means that processes can only perform
+//! actions that have no global impact. Processes cannot open files by
+//! their absolute path, cannot open network connections, and cannot
+//! observe global system state such as the process table.
+//!
+//! The capabilities of a process are fully determined by its set of open
+//! file descriptors (fds). For example, files can only be opened if the
+//! process already has a file descriptor to a directory the file is in.
+//!
+//! Unlike in POSIX, where processes are normally started with file
+//! descriptors 0, 1, and 2 reserved for standard input, output, and
+//! error, CloudABI does not reserve any file descriptor numbers for
+//! specific purposes.
+//!
+//! In CloudABI, a process depends on its parent process to launch it with
+//! the right set of resources, since the process will not be able to open
+//! any new resources. For example, a simple static web server would need
+//! to be started with a file descriptor to a [TCP
+//! listener](https://github.com/NuxiNL/flower), and a file descriptor to
+//! the directory for which to serve files. The web server will then be
+//! unable to do anything other than reading files in that directory, and
+//! process incoming network connections.
+//!
+//! So, unknown CloudABI binaries can safely be executed without the need
+//! for containers, virtual machines, or other sandboxing technologies.
+//!
+//! Watch [Ed Schouten's Talk at
+//! 32C3](https://www.youtube.com/watch?v=3N29vrPoDv8) for more
+//! information about what capability-based security for UNIX means.
+//!
+//! ## Cloudlibc
+//!
+//! [Cloudlibc](https://github.com/NuxiNL/cloudlibc) is an implementation
+//! of the C standard library, without all CloudABI-incompatible
+//! functions. For example, Cloudlibc does not have `printf`, but does
+//! have `fprintf`. It does not have `open`, but does have `openat`.
+//!
+//! ## CloudABI-Ports
+//!
+//! [CloudABI-Ports](https://github.com/NuxiNL/cloudabi-ports) is a
+//! collection of ports of commonly used libraries and applications to
+//! CloudABI. It contains software such as `zlib`, `libpng`, `boost`,
+//! `memcached`, and much more. The software is patched to not depend on
+//! any global state, such as files in `/etc` or `/dev`, using `open()`,
+//! etc.
+//!
+//! ## Using CloudABI
+//!
+//! Instructions for using CloudABI (including kernel modules/patches,
+//! toolchain, and ports) are available for several operating systems:
+//!
+//! - [Arch Linux](https://nuxi.nl/cloudabi/archlinux/)
+//! - [Debian, Ubuntu, and other Debian derivatives](https://nuxi.nl/cloudabi/debian/)
+//! - [FreeBSD, PC-BSD and DragonFly BSD](https://nuxi.nl/cloudabi/freebsd/)
+//! - [Mac OS X](https://nuxi.nl/cloudabi/mac/)
+//! - [NetBSD](https://nuxi.nl/cloudabi/netbsd/)
+//!
+//! ## Specification of the ABI
+//!
+//! The entire ABI is specified in a a file called
+//! [`cloudabi.txt`](https://github.com/NuxiNL/cloudabi/blob/master/cloudabi.txt),
+//! from which all
+//! [headers](https://github.com/NuxiNL/cloudabi/tree/master/headers)
+//! and documentation (including the one you're reading now) is generated.
+
+#![no_std]
+#![allow(non_camel_case_types)]
+
+include!("bitflags.rs");
+
+/// File or memory access pattern advisory information.
+#[repr(u8)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub enum advice {
+ /// The application expects that it will not access the
+ /// specified data in the near future.
+ DONTNEED = 1,
+ /// The application expects to access the specified data
+ /// once and then not reuse it thereafter.
+ NOREUSE = 2,
+ /// The application has no advice to give on its behavior
+ /// with respect to the specified data.
+ NORMAL = 3,
+ /// The application expects to access the specified data
+ /// in a random order.
+ RANDOM = 4,
+ /// The application expects to access the specified data
+ /// sequentially from lower offsets to higher offsets.
+ SEQUENTIAL = 5,
+ /// The application expects to access the specified data
+ /// in the near future.
+ WILLNEED = 6,
+ #[doc(hidden)] _NonExhaustive = -1 as isize as u8,
+}
+
+/// Enumeration describing the kind of value stored in [`auxv`](struct.auxv.html).
+#[repr(u32)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub enum auxtype {
+ /// Base address of the binary argument data provided to
+ /// [`proc_exec()`](fn.proc_exec.html).
+ ARGDATA = 256,
+ /// Length of the binary argument data provided to
+ /// [`proc_exec()`](fn.proc_exec.html).
+ ARGDATALEN = 257,
+ /// Base address at which the executable is placed in
+ /// memory.
+ BASE = 7,
+ /// Base address of a buffer of random data that may be
+ /// used for non-cryptographic purposes, for example as a
+ /// canary for stack smashing protection.
+ CANARY = 258,
+ /// Length of a buffer of random data that may be used
+ /// for non-cryptographic purposes, for example as a
+ /// canary for stack smashing protection.
+ CANARYLEN = 259,
+ /// Number of CPUs that the system this process is running
+ /// on has.
+ NCPUS = 260,
+ /// Terminator of the auxiliary vector.
+ NULL = 0,
+ /// Smallest memory object size for which individual
+ /// memory protection controls can be configured.
+ PAGESZ = 6,
+ /// Address of the first ELF program header of the
+ /// executable.
+ PHDR = 3,
+ /// Number of ELF program headers of the executable.
+ PHNUM = 4,
+ /// Identifier of the process.
+ ///
+ /// This environment does not provide any simple numerical
+ /// process identifiers, for the reason that these are not
+ /// useful in distributed contexts. Instead, processes are
+ /// identified by a UUID.
+ ///
+ /// This record should point to sixteen bytes of binary
+ /// data, containing a version 4 UUID (fully random).
+ PID = 263,
+ /// Address of the ELF header of the vDSO.
+ ///
+ /// The vDSO is a shared library that is mapped in the
+ /// address space of the process. It provides entry points
+ /// for every system call supported by the environment,
+ /// all having a corresponding symbol that is prefixed
+ /// with `cloudabi_sys_`. System calls should be invoked
+ /// through these entry points.
+ ///
+ /// The first advantage of letting processes call into a
+ /// vDSO to perform system calls instead of raising
+ /// hardware traps is that it allows for easy emulation of
+ /// executables on top of existing operating systems. The
+ /// second advantage is that in cases where an operating
+ /// system provides native support for CloudABI executables,
+ /// it may still implement partial userspace
+ /// implementations of these system calls to improve
+ /// performance (e.g., [`clock_time_get()`](fn.clock_time_get.html)). It also provides
+ /// a more dynamic way of adding, removing or replacing
+ /// system calls.
+ SYSINFO_EHDR = 262,
+ /// Thread ID of the initial thread of the process.
+ TID = 261,
+ #[doc(hidden)] _NonExhaustive = -1 as isize as u32,
+}
+
+/// Identifiers for clocks.
+#[repr(u32)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub enum clockid {
+ /// The system-wide monotonic clock, which is defined as a
+ /// clock measuring real time, whose value cannot be
+ /// adjusted and which cannot have negative clock jumps.
+ ///
+ /// The epoch of this clock is undefined. The absolute
+ /// time value of this clock therefore has no meaning.
+ MONOTONIC = 1,
+ /// The CPU-time clock associated with the current
+ /// process.
+ PROCESS_CPUTIME_ID = 2,
+ /// The system-wide clock measuring real time. Time value
+ /// zero corresponds with 1970-01-01T00:00:00Z.
+ REALTIME = 3,
+ /// The CPU-time clock associated with the current thread.
+ THREAD_CPUTIME_ID = 4,
+ #[doc(hidden)] _NonExhaustive = -1 as isize as u32,
+}
+
+/// A userspace condition variable.
+#[repr(C)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub struct condvar(pub u32);
+/// The condition variable is in its initial state. There
+/// are no threads waiting to be woken up. If the
+/// condition variable has any other value, the kernel
+/// must be called to wake up any sleeping threads.
+pub const CONDVAR_HAS_NO_WAITERS: condvar = condvar(0);
+
+/// Identifier for a device containing a file system. Can be used
+/// in combination with [`inode`](struct.inode.html) to uniquely identify a file on the
+/// local system.
+#[repr(C)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub struct device(pub u64);
+
+/// A reference to the offset of a directory entry.
+#[repr(C)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub struct dircookie(pub u64);
+/// Permanent reference to the first directory entry
+/// within a directory.
+pub const DIRCOOKIE_START: dircookie = dircookie(0);
+
+/// Error codes returned by system calls.
+///
+/// Not all of these error codes are returned by the system calls
+/// provided by this environment, but are either used in userspace
+/// exclusively or merely provided for alignment with POSIX.
+#[repr(u16)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub enum errno {
+ /// No error occurred. System call completed successfully.
+ SUCCESS = 0,
+ /// Argument list too long.
+ TOOBIG = 1,
+ /// Permission denied.
+ ACCES = 2,
+ /// Address in use.
+ ADDRINUSE = 3,
+ /// Address not available.
+ ADDRNOTAVAIL = 4,
+ /// Address family not supported.
+ AFNOSUPPORT = 5,
+ /// Resource unavailable, or operation would block.
+ AGAIN = 6,
+ /// Connection already in progress.
+ ALREADY = 7,
+ /// Bad file descriptor.
+ BADF = 8,
+ /// Bad message.
+ BADMSG = 9,
+ /// Device or resource busy.
+ BUSY = 10,
+ /// Operation canceled.
+ CANCELED = 11,
+ /// No child processes.
+ CHILD = 12,
+ /// Connection aborted.
+ CONNABORTED = 13,
+ /// Connection refused.
+ CONNREFUSED = 14,
+ /// Connection reset.
+ CONNRESET = 15,
+ /// Resource deadlock would occur.
+ DEADLK = 16,
+ /// Destination address required.
+ DESTADDRREQ = 17,
+ /// Mathematics argument out of domain of function.
+ DOM = 18,
+ /// Reserved.
+ DQUOT = 19,
+ /// File exists.
+ EXIST = 20,
+ /// Bad address.
+ FAULT = 21,
+ /// File too large.
+ FBIG = 22,
+ /// Host is unreachable.
+ HOSTUNREACH = 23,
+ /// Identifier removed.
+ IDRM = 24,
+ /// Illegal byte sequence.
+ ILSEQ = 25,
+ /// Operation in progress.
+ INPROGRESS = 26,
+ /// Interrupted function.
+ INTR = 27,
+ /// Invalid argument.
+ INVAL = 28,
+ /// I/O error.
+ IO = 29,
+ /// Socket is connected.
+ ISCONN = 30,
+ /// Is a directory.
+ ISDIR = 31,
+ /// Too many levels of symbolic links.
+ LOOP = 32,
+ /// File descriptor value too large.
+ MFILE = 33,
+ /// Too many links.
+ MLINK = 34,
+ /// Message too large.
+ MSGSIZE = 35,
+ /// Reserved.
+ MULTIHOP = 36,
+ /// Filename too long.
+ NAMETOOLONG = 37,
+ /// Network is down.
+ NETDOWN = 38,
+ /// Connection aborted by network.
+ NETRESET = 39,
+ /// Network unreachable.
+ NETUNREACH = 40,
+ /// Too many files open in system.
+ NFILE = 41,
+ /// No buffer space available.
+ NOBUFS = 42,
+ /// No such device.
+ NODEV = 43,
+ /// No such file or directory.
+ NOENT = 44,
+ /// Executable file format error.
+ NOEXEC = 45,
+ /// No locks available.
+ NOLCK = 46,
+ /// Reserved.
+ NOLINK = 47,
+ /// Not enough space.
+ NOMEM = 48,
+ /// No message of the desired type.
+ NOMSG = 49,
+ /// Protocol not available.
+ NOPROTOOPT = 50,
+ /// No space left on device.
+ NOSPC = 51,
+ /// Function not supported.
+ NOSYS = 52,
+ /// The socket is not connected.
+ NOTCONN = 53,
+ /// Not a directory or a symbolic link to a directory.
+ NOTDIR = 54,
+ /// Directory not empty.
+ NOTEMPTY = 55,
+ /// State not recoverable.
+ NOTRECOVERABLE = 56,
+ /// Not a socket.
+ NOTSOCK = 57,
+ /// Not supported, or operation not supported on socket.
+ NOTSUP = 58,
+ /// Inappropriate I/O control operation.
+ NOTTY = 59,
+ /// No such device or address.
+ NXIO = 60,
+ /// Value too large to be stored in data type.
+ OVERFLOW = 61,
+ /// Previous owner died.
+ OWNERDEAD = 62,
+ /// Operation not permitted.
+ PERM = 63,
+ /// Broken pipe.
+ PIPE = 64,
+ /// Protocol error.
+ PROTO = 65,
+ /// Protocol not supported.
+ PROTONOSUPPORT = 66,
+ /// Protocol wrong type for socket.
+ PROTOTYPE = 67,
+ /// Result too large.
+ RANGE = 68,
+ /// Read-only file system.
+ ROFS = 69,
+ /// Invalid seek.
+ SPIPE = 70,
+ /// No such process.
+ SRCH = 71,
+ /// Reserved.
+ STALE = 72,
+ /// Connection timed out.
+ TIMEDOUT = 73,
+ /// Text file busy.
+ TXTBSY = 74,
+ /// Cross-device link.
+ XDEV = 75,
+ /// Extension: Capabilities insufficient.
+ NOTCAPABLE = 76,
+ #[doc(hidden)] _NonExhaustive = -1 as isize as u16,
+}
+
+bitflags! {
+ /// The state of the file descriptor subscribed to with
+ /// [`FD_READ`](enum.eventtype.html#variant.FD_READ) or [`FD_WRITE`](enum.eventtype.html#variant.FD_WRITE).
+ #[repr(C)]
+ pub struct eventrwflags: u16 {
+ /// The peer of this socket has closed or disconnected.
+ const HANGUP = 0x0001;
+ }
+}
+
+/// Type of a subscription to an event or its occurrence.
+#[repr(u8)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub enum eventtype {
+ /// The time value of clock [`subscription.union.clock.clock_id`](struct.subscription_clock.html#structfield.clock_id)
+ /// has reached timestamp [`subscription.union.clock.timeout`](struct.subscription_clock.html#structfield.timeout).
+ CLOCK = 1,
+ /// Condition variable [`subscription.union.condvar.condvar`](struct.subscription_condvar.html#structfield.condvar) has
+ /// been woken up and [`subscription.union.condvar.lock`](struct.subscription_condvar.html#structfield.lock) has been
+ /// acquired for writing.
+ CONDVAR = 2,
+ /// File descriptor [`subscription.union.fd_readwrite.fd`](struct.subscription_fd_readwrite.html#structfield.fd) has
+ /// data available for reading. This event always triggers
+ /// for regular files.
+ FD_READ = 3,
+ /// File descriptor [`subscription.union.fd_readwrite.fd`](struct.subscription_fd_readwrite.html#structfield.fd) has
+ /// capacity available for writing. This event always
+ /// triggers for regular files.
+ FD_WRITE = 4,
+ /// Lock [`subscription.union.lock.lock`](struct.subscription_lock.html#structfield.lock) has been acquired for
+ /// reading.
+ LOCK_RDLOCK = 5,
+ /// Lock [`subscription.union.lock.lock`](struct.subscription_lock.html#structfield.lock) has been acquired for
+ /// writing.
+ LOCK_WRLOCK = 6,
+ /// The process associated with process descriptor
+ /// [`subscription.union.proc_terminate.fd`](struct.subscription_proc_terminate.html#structfield.fd) has terminated.
+ PROC_TERMINATE = 7,
+ #[doc(hidden)] _NonExhaustive = -1 as isize as u8,
+}
+
+/// Exit code generated by a process when exiting.
+pub type exitcode = u32;
+
+/// A file descriptor number.
+///
+/// Unlike on POSIX-compliant systems, none of the file descriptor
+/// numbers are reserved for a purpose (e.g., stdin, stdout,
+/// stderr). Operating systems are not required to allocate new
+/// file descriptors in ascending order.
+#[repr(C)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub struct fd(pub u32);
+/// Returned to the child process by [`proc_fork()`](fn.proc_fork.html).
+pub const PROCESS_CHILD: fd = fd(0xffffffff);
+/// Passed to [`mem_map()`](fn.mem_map.html) when creating a mapping to
+/// anonymous memory.
+pub const MAP_ANON_FD : fd = fd(0xffffffff);
+
+bitflags! {
+ /// File descriptor flags.
+ #[repr(C)]
+ pub struct fdflags: u16 {
+ /// Append mode: Data written to the file is always
+ /// appended to the file's end.
+ const APPEND = 0x0001;
+ /// Write according to synchronized I/O data integrity
+ /// completion. Only the data stored in the file is
+ /// synchronized.
+ const DSYNC = 0x0002;
+ /// Non-blocking mode.
+ const NONBLOCK = 0x0004;
+ /// Synchronized read I/O operations.
+ const RSYNC = 0x0008;
+ /// Write according to synchronized I/O file integrity
+ /// completion. In addition to synchronizing the data
+ /// stored in the file, the system may also synchronously
+ /// update the file's metadata.
+ const SYNC = 0x0010;
+ }
+}
+
+bitflags! {
+ /// Which file descriptor attributes to adjust.
+ #[repr(C)]
+ pub struct fdsflags: u16 {
+ /// Adjust the file descriptor flags stored in
+ /// [`fdstat.fs_flags`](struct.fdstat.html#structfield.fs_flags).
+ const FLAGS = 0x0001;
+ /// Restrict the rights of the file descriptor to the
+ /// rights stored in [`fdstat.fs_rights_base`](struct.fdstat.html#structfield.fs_rights_base) and
+ /// [`fdstat.fs_rights_inheriting`](struct.fdstat.html#structfield.fs_rights_inheriting).
+ const RIGHTS = 0x0002;
+ }
+}
+
+/// Relative offset within a file.
+pub type filedelta = i64;
+
+/// Non-negative file size or length of a region within a file.
+pub type filesize = u64;
+
+/// The type of a file descriptor or file.
+#[repr(u8)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub enum filetype {
+ /// The type of the file descriptor or file is unknown or
+ /// is different from any of the other types specified.
+ UNKNOWN = 0,
+ /// The file descriptor or file refers to a block device
+ /// inode.
+ BLOCK_DEVICE = 16,
+ /// The file descriptor or file refers to a character
+ /// device inode.
+ CHARACTER_DEVICE = 17,
+ /// The file descriptor or file refers to a directory
+ /// inode.
+ DIRECTORY = 32,
+ /// The file descriptor refers to a process handle.
+ PROCESS = 80,
+ /// The file descriptor or file refers to a regular file
+ /// inode.
+ REGULAR_FILE = 96,
+ /// The file descriptor refers to a shared memory object.
+ SHARED_MEMORY = 112,
+ /// The file descriptor or file refers to a datagram
+ /// socket.
+ SOCKET_DGRAM = 128,
+ /// The file descriptor or file refers to a byte-stream
+ /// socket.
+ SOCKET_STREAM = 130,
+ /// The file refers to a symbolic link inode.
+ SYMBOLIC_LINK = 144,
+ #[doc(hidden)] _NonExhaustive = -1 as isize as u8,
+}
+
+bitflags! {
+ /// Which file attributes to adjust.
+ #[repr(C)]
+ pub struct fsflags: u16 {
+ /// Adjust the last data access timestamp to the value
+ /// stored in [`filestat.st_atim`](struct.filestat.html#structfield.st_atim).
+ const ATIM = 0x0001;
+ /// Adjust the last data access timestamp to the time
+ /// of clock [`REALTIME`](enum.clockid.html#variant.REALTIME).
+ const ATIM_NOW = 0x0002;
+ /// Adjust the last data modification timestamp to the
+ /// value stored in [`filestat.st_mtim`](struct.filestat.html#structfield.st_mtim).
+ const MTIM = 0x0004;
+ /// Adjust the last data modification timestamp to the
+ /// time of clock [`REALTIME`](enum.clockid.html#variant.REALTIME).
+ const MTIM_NOW = 0x0008;
+ /// Truncate or extend the file to the size stored in
+ /// [`filestat.st_size`](struct.filestat.html#structfield.st_size).
+ const SIZE = 0x0010;
+ }
+}
+
+/// File serial number that is unique within its file system.
+#[repr(C)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub struct inode(pub u64);
+
+/// Number of hard links to an inode.
+pub type linkcount = u32;
+
+/// A userspace read-recursive readers-writer lock, similar to a
+/// Linux futex or a FreeBSD umtx.
+#[repr(C)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub struct lock(pub u32);
+/// Value indicating that the lock is in its initial
+/// unlocked state.
+pub const LOCK_UNLOCKED : lock = lock(0x00000000);
+/// Bitmask indicating that the lock is write-locked. If
+/// set, the lower 30 bits of the lock contain the
+/// identifier of the thread that owns the write lock.
+/// Otherwise, the lower 30 bits of the lock contain the
+/// number of acquired read locks.
+pub const LOCK_WRLOCKED : lock = lock(0x40000000);
+/// Bitmask indicating that the lock is either read locked
+/// or write locked, and that one or more threads have
+/// their execution suspended, waiting to acquire the
+/// lock. The last owner of the lock must call the
+/// kernel to unlock.
+///
+/// When the lock is acquired for reading and this bit is
+/// set, it means that one or more threads are attempting
+/// to acquire this lock for writing. In that case, other
+/// threads should only acquire additional read locks if
+/// suspending execution would cause a deadlock. It is
+/// preferred to suspend execution, as this prevents
+/// starvation of writers.
+pub const LOCK_KERNEL_MANAGED: lock = lock(0x80000000);
+/// Value indicating that the lock is in an incorrect
+/// state. A lock cannot be in its initial unlocked state,
+/// while also managed by the kernel.
+pub const LOCK_BOGUS : lock = lock(0x80000000);
+
+bitflags! {
+ /// Flags determining the method of how paths are resolved.
+ #[repr(C)]
+ pub struct lookupflags: u32 {
+ /// As long as the resolved path corresponds to a symbolic
+ /// link, it is expanded.
+ const SYMLINK_FOLLOW = 0x00000001;
+ }
+}
+
+bitflags! {
+ /// Memory mapping flags.
+ #[repr(C)]
+ pub struct mflags: u8 {
+ /// Instead of mapping the contents of the file provided,
+ /// create a mapping to anonymous memory. The file
+ /// descriptor argument must be set to [`MAP_ANON_FD`](constant.MAP_ANON_FD.html),
+ /// and the offset must be set to zero.
+ const ANON = 0x01;
+ /// Require that the mapping is performed at the base
+ /// address provided.
+ const FIXED = 0x02;
+ /// Changes are private.
+ const PRIVATE = 0x04;
+ /// Changes are shared.
+ const SHARED = 0x08;
+ }
+}
+
+bitflags! {
+ /// Memory page protection options.
+ ///
+ /// This implementation enforces the `W^X` property: Pages cannot be
+ /// mapped for execution while also mapped for writing.
+ #[repr(C)]
+ pub struct mprot: u8 {
+ /// Page can be executed.
+ const EXEC = 0x01;
+ /// Page can be written.
+ const WRITE = 0x02;
+ /// Page can be read.
+ const READ = 0x04;
+ }
+}
+
+bitflags! {
+ /// Methods of synchronizing memory with physical storage.
+ #[repr(C)]
+ pub struct msflags: u8 {
+ /// Perform asynchronous writes.
+ const ASYNC = 0x01;
+ /// Invalidate cached data.
+ const INVALIDATE = 0x02;
+ /// Perform synchronous writes.
+ const SYNC = 0x04;
+ }
+}
+
+/// Specifies the number of threads sleeping on a condition
+/// variable that should be woken up.
+pub type nthreads = u32;
+
+bitflags! {
+ /// Open flags used by [`file_open()`](fn.file_open.html).
+ #[repr(C)]
+ pub struct oflags: u16 {
+ /// Create file if it does not exist.
+ const CREAT = 0x0001;
+ /// Fail if not a directory.
+ const DIRECTORY = 0x0002;
+ /// Fail if file already exists.
+ const EXCL = 0x0004;
+ /// Truncate file to size 0.
+ const TRUNC = 0x0008;
+ }
+}
+
+bitflags! {
+ /// Flags provided to [`sock_recv()`](fn.sock_recv.html).
+ #[repr(C)]
+ pub struct riflags: u16 {
+ /// Returns the message without removing it from the
+ /// socket's receive queue.
+ const PEEK = 0x0004;
+ /// On byte-stream sockets, block until the full amount
+ /// of data can be returned.
+ const WAITALL = 0x0010;
+ }
+}
+
+bitflags! {
+ /// File descriptor rights, determining which actions may be
+ /// performed.
+ #[repr(C)]
+ pub struct rights: u64 {
+ /// The right to invoke [`fd_datasync()`](fn.fd_datasync.html).
+ ///
+ /// If [`FILE_OPEN`](struct.rights.html#associatedconstant.FILE_OPEN) is set, includes the right to
+ /// invoke [`file_open()`](fn.file_open.html) with [`DSYNC`](struct.fdflags.html#associatedconstant.DSYNC).
+ const FD_DATASYNC = 0x0000000000000001;
+ /// The right to invoke [`fd_read()`](fn.fd_read.html) and [`sock_recv()`](fn.sock_recv.html).
+ ///
+ /// If [`MEM_MAP`](struct.rights.html#associatedconstant.MEM_MAP) is set, includes the right to
+ /// invoke [`mem_map()`](fn.mem_map.html) with memory protection option
+ /// [`READ`](struct.mprot.html#associatedconstant.READ).
+ ///
+ /// If [`FD_SEEK`](struct.rights.html#associatedconstant.FD_SEEK) is set, includes the right to invoke
+ /// [`fd_pread()`](fn.fd_pread.html).
+ const FD_READ = 0x0000000000000002;
+ /// The right to invoke [`fd_seek()`](fn.fd_seek.html). This flag implies
+ /// [`FD_TELL`](struct.rights.html#associatedconstant.FD_TELL).
+ const FD_SEEK = 0x0000000000000004;
+ /// The right to invoke [`fd_stat_put()`](fn.fd_stat_put.html) with
+ /// [`FLAGS`](struct.fdsflags.html#associatedconstant.FLAGS).
+ const FD_STAT_PUT_FLAGS = 0x0000000000000008;
+ /// The right to invoke [`fd_sync()`](fn.fd_sync.html).
+ ///
+ /// If [`FILE_OPEN`](struct.rights.html#associatedconstant.FILE_OPEN) is set, includes the right to
+ /// invoke [`file_open()`](fn.file_open.html) with [`RSYNC`](struct.fdflags.html#associatedconstant.RSYNC) and
+ /// [`DSYNC`](struct.fdflags.html#associatedconstant.DSYNC).
+ const FD_SYNC = 0x0000000000000010;
+ /// The right to invoke [`fd_seek()`](fn.fd_seek.html) in such a way that the
+ /// file offset remains unaltered (i.e., [`CUR`](enum.whence.html#variant.CUR) with
+ /// offset zero).
+ const FD_TELL = 0x0000000000000020;
+ /// The right to invoke [`fd_write()`](fn.fd_write.html) and [`sock_send()`](fn.sock_send.html).
+ ///
+ /// If [`MEM_MAP`](struct.rights.html#associatedconstant.MEM_MAP) is set, includes the right to
+ /// invoke [`mem_map()`](fn.mem_map.html) with memory protection option
+ /// [`WRITE`](struct.mprot.html#associatedconstant.WRITE).
+ ///
+ /// If [`FD_SEEK`](struct.rights.html#associatedconstant.FD_SEEK) is set, includes the right to
+ /// invoke [`fd_pwrite()`](fn.fd_pwrite.html).
+ const FD_WRITE = 0x0000000000000040;
+ /// The right to invoke [`file_advise()`](fn.file_advise.html).
+ const FILE_ADVISE = 0x0000000000000080;
+ /// The right to invoke [`file_allocate()`](fn.file_allocate.html).
+ const FILE_ALLOCATE = 0x0000000000000100;
+ /// The right to invoke [`file_create()`](fn.file_create.html) with
+ /// [`DIRECTORY`](enum.filetype.html#variant.DIRECTORY).
+ const FILE_CREATE_DIRECTORY = 0x0000000000000200;
+ /// If [`FILE_OPEN`](struct.rights.html#associatedconstant.FILE_OPEN) is set, the right to invoke
+ /// [`file_open()`](fn.file_open.html) with [`CREAT`](struct.oflags.html#associatedconstant.CREAT).
+ const FILE_CREATE_FILE = 0x0000000000000400;
+ /// The right to invoke [`file_link()`](fn.file_link.html) with the file
+ /// descriptor as the source directory.
+ const FILE_LINK_SOURCE = 0x0000000000001000;
+ /// The right to invoke [`file_link()`](fn.file_link.html) with the file
+ /// descriptor as the target directory.
+ const FILE_LINK_TARGET = 0x0000000000002000;
+ /// The right to invoke [`file_open()`](fn.file_open.html).
+ const FILE_OPEN = 0x0000000000004000;
+ /// The right to invoke [`file_readdir()`](fn.file_readdir.html).
+ const FILE_READDIR = 0x0000000000008000;
+ /// The right to invoke [`file_readlink()`](fn.file_readlink.html).
+ const FILE_READLINK = 0x0000000000010000;
+ /// The right to invoke [`file_rename()`](fn.file_rename.html) with the file
+ /// descriptor as the source directory.
+ const FILE_RENAME_SOURCE = 0x0000000000020000;
+ /// The right to invoke [`file_rename()`](fn.file_rename.html) with the file
+ /// descriptor as the target directory.
+ const FILE_RENAME_TARGET = 0x0000000000040000;
+ /// The right to invoke [`file_stat_fget()`](fn.file_stat_fget.html).
+ const FILE_STAT_FGET = 0x0000000000080000;
+ /// The right to invoke [`file_stat_fput()`](fn.file_stat_fput.html) with
+ /// [`SIZE`](struct.fsflags.html#associatedconstant.SIZE).
+ ///
+ /// If [`FILE_OPEN`](struct.rights.html#associatedconstant.FILE_OPEN) is set, includes the right to
+ /// invoke [`file_open()`](fn.file_open.html) with [`TRUNC`](struct.oflags.html#associatedconstant.TRUNC).
+ const FILE_STAT_FPUT_SIZE = 0x0000000000100000;
+ /// The right to invoke [`file_stat_fput()`](fn.file_stat_fput.html) with
+ /// [`ATIM`](struct.fsflags.html#associatedconstant.ATIM), [`ATIM_NOW`](struct.fsflags.html#associatedconstant.ATIM_NOW), [`MTIM`](struct.fsflags.html#associatedconstant.MTIM),
+ /// and [`MTIM_NOW`](struct.fsflags.html#associatedconstant.MTIM_NOW).
+ const FILE_STAT_FPUT_TIMES = 0x0000000000200000;
+ /// The right to invoke [`file_stat_get()`](fn.file_stat_get.html).
+ const FILE_STAT_GET = 0x0000000000400000;
+ /// The right to invoke [`file_stat_put()`](fn.file_stat_put.html) with
+ /// [`ATIM`](struct.fsflags.html#associatedconstant.ATIM), [`ATIM_NOW`](struct.fsflags.html#associatedconstant.ATIM_NOW), [`MTIM`](struct.fsflags.html#associatedconstant.MTIM),
+ /// and [`MTIM_NOW`](struct.fsflags.html#associatedconstant.MTIM_NOW).
+ const FILE_STAT_PUT_TIMES = 0x0000000000800000;
+ /// The right to invoke [`file_symlink()`](fn.file_symlink.html).
+ const FILE_SYMLINK = 0x0000000001000000;
+ /// The right to invoke [`file_unlink()`](fn.file_unlink.html).
+ const FILE_UNLINK = 0x0000000002000000;
+ /// The right to invoke [`mem_map()`](fn.mem_map.html) with [`mprot`](struct.mprot.html) set to
+ /// zero.
+ const MEM_MAP = 0x0000000004000000;
+ /// If [`MEM_MAP`](struct.rights.html#associatedconstant.MEM_MAP) is set, the right to invoke
+ /// [`mem_map()`](fn.mem_map.html) with [`EXEC`](struct.mprot.html#associatedconstant.EXEC).
+ const MEM_MAP_EXEC = 0x0000000008000000;
+ /// If [`FD_READ`](struct.rights.html#associatedconstant.FD_READ) is set, includes the right to
+ /// invoke [`poll()`](fn.poll.html) to subscribe to [`FD_READ`](enum.eventtype.html#variant.FD_READ).
+ ///
+ /// If [`FD_WRITE`](struct.rights.html#associatedconstant.FD_WRITE) is set, includes the right to
+ /// invoke [`poll()`](fn.poll.html) to subscribe to [`FD_WRITE`](enum.eventtype.html#variant.FD_WRITE).
+ const POLL_FD_READWRITE = 0x0000000010000000;
+ /// The right to invoke [`poll()`](fn.poll.html) to subscribe to
+ /// [`PROC_TERMINATE`](enum.eventtype.html#variant.PROC_TERMINATE).
+ const POLL_PROC_TERMINATE = 0x0000000040000000;
+ /// The right to invoke [`proc_exec()`](fn.proc_exec.html).
+ const PROC_EXEC = 0x0000000100000000;
+ /// The right to invoke [`sock_shutdown()`](fn.sock_shutdown.html).
+ const SOCK_SHUTDOWN = 0x0000008000000000;
+ }
+}
+
+bitflags! {
+ /// Flags returned by [`sock_recv()`](fn.sock_recv.html).
+ #[repr(C)]
+ pub struct roflags: u16 {
+ /// Returned by [`sock_recv()`](fn.sock_recv.html): List of file descriptors
+ /// has been truncated.
+ const FDS_TRUNCATED = 0x0001;
+ /// Returned by [`sock_recv()`](fn.sock_recv.html): Message data has been
+ /// truncated.
+ const DATA_TRUNCATED = 0x0008;
+ }
+}
+
+/// Indicates whether an object is stored in private or shared
+/// memory.
+#[repr(u8)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub enum scope {
+ /// The object is stored in private memory.
+ PRIVATE = 4,
+ /// The object is stored in shared memory.
+ SHARED = 8,
+ #[doc(hidden)] _NonExhaustive = -1 as isize as u8,
+}
+
+bitflags! {
+ /// Which channels on a socket need to be shut down.
+ #[repr(C)]
+ pub struct sdflags: u8 {
+ /// Disables further receive operations.
+ const RD = 0x01;
+ /// Disables further send operations.
+ const WR = 0x02;
+ }
+}
+
+bitflags! {
+ /// Flags provided to [`sock_send()`](fn.sock_send.html). As there are currently no flags
+ /// defined, it must be set to zero.
+ #[repr(C)]
+ pub struct siflags: u16 {
+ const DEFAULT = 0;
+ }
+}
+
+/// Signal condition.
+#[repr(u8)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub enum signal {
+ /// Process abort signal.
+ ///
+ /// Action: Terminates the process.
+ ABRT = 1,
+ /// Alarm clock.
+ ///
+ /// Action: Terminates the process.
+ ALRM = 2,
+ /// Access to an undefined portion of a memory object.
+ ///
+ /// Action: Terminates the process.
+ BUS = 3,
+ /// Child process terminated, stopped, or continued.
+ ///
+ /// Action: Ignored.
+ CHLD = 4,
+ /// Continue executing, if stopped.
+ ///
+ /// Action: Continues executing, if stopped.
+ CONT = 5,
+ /// Erroneous arithmetic operation.
+ ///
+ /// Action: Terminates the process.
+ FPE = 6,
+ /// Hangup.
+ ///
+ /// Action: Terminates the process.
+ HUP = 7,
+ /// Illegal instruction.
+ ///
+ /// Action: Terminates the process.
+ ILL = 8,
+ /// Terminate interrupt signal.
+ ///
+ /// Action: Terminates the process.
+ INT = 9,
+ /// Kill.
+ ///
+ /// Action: Terminates the process.
+ KILL = 10,
+ /// Write on a pipe with no one to read it.
+ ///
+ /// Action: Ignored.
+ PIPE = 11,
+ /// Terminal quit signal.
+ ///
+ /// Action: Terminates the process.
+ QUIT = 12,
+ /// Invalid memory reference.
+ ///
+ /// Action: Terminates the process.
+ SEGV = 13,
+ /// Stop executing.
+ ///
+ /// Action: Stops executing.
+ STOP = 14,
+ /// Bad system call.
+ ///
+ /// Action: Terminates the process.
+ SYS = 15,
+ /// Termination signal.
+ ///
+ /// Action: Terminates the process.
+ TERM = 16,
+ /// Trace/breakpoint trap.
+ ///
+ /// Action: Terminates the process.
+ TRAP = 17,
+ /// Terminal stop signal.
+ ///
+ /// Action: Stops executing.
+ TSTP = 18,
+ /// Background process attempting read.
+ ///
+ /// Action: Stops executing.
+ TTIN = 19,
+ /// Background process attempting write.
+ ///
+ /// Action: Stops executing.
+ TTOU = 20,
+ /// High bandwidth data is available at a socket.
+ ///
+ /// Action: Ignored.
+ URG = 21,
+ /// User-defined signal 1.
+ ///
+ /// Action: Terminates the process.
+ USR1 = 22,
+ /// User-defined signal 2.
+ ///
+ /// Action: Terminates the process.
+ USR2 = 23,
+ /// Virtual timer expired.
+ ///
+ /// Action: Terminates the process.
+ VTALRM = 24,
+ /// CPU time limit exceeded.
+ ///
+ /// Action: Terminates the process.
+ XCPU = 25,
+ /// File size limit exceeded.
+ ///
+ /// Action: Terminates the process.
+ XFSZ = 26,
+ #[doc(hidden)] _NonExhaustive = -1 as isize as u8,
+}
+
+bitflags! {
+ /// Flags determining how the timestamp provided in
+ /// [`subscription.union.clock.timeout`](struct.subscription_clock.html#structfield.timeout) should be interpreted.
+ #[repr(C)]
+ pub struct subclockflags: u16 {
+ /// If set, treat the timestamp provided in
+ /// [`subscription.union.clock.timeout`](struct.subscription_clock.html#structfield.timeout) as an absolute timestamp
+ /// of clock [`subscription.union.clock.clock_id`](struct.subscription_clock.html#structfield.clock_id).
+ ///
+ /// If clear, treat the timestamp provided in
+ /// [`subscription.union.clock.timeout`](struct.subscription_clock.html#structfield.timeout) relative to the current
+ /// time value of clock [`subscription.union.clock.clock_id`](struct.subscription_clock.html#structfield.clock_id).
+ const ABSTIME = 0x0001;
+ }
+}
+
+bitflags! {
+ /// Flags influencing the method of polling for read or writing on
+ /// a file descriptor.
+ #[repr(C)]
+ pub struct subrwflags: u16 {
+ /// Deprecated. Must be set by callers and ignored by
+ /// implementations.
+ const POLL = 0x0001;
+ }
+}
+
+/// Unique system-local identifier of a thread. This identifier is
+/// only valid during the lifetime of the thread.
+///
+/// Threads must be aware of their thread identifier, as it is
+/// written it into locks when acquiring them for writing. It is
+/// not advised to use these identifiers for any other purpose.
+///
+/// As the thread identifier is also stored in [`lock`](struct.lock.html) when
+/// [`LOCK_WRLOCKED`](constant.LOCK_WRLOCKED.html) is set, the top two bits of the thread
+/// must always be set to zero.
+#[repr(C)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub struct tid(pub u32);
+
+/// Timestamp in nanoseconds.
+pub type timestamp = u64;
+
+bitflags! {
+ /// Specifies whether files are unlinked or directories are
+ /// removed.
+ #[repr(C)]
+ pub struct ulflags: u8 {
+ /// If set, removes a directory. Otherwise, unlinks any
+ /// non-directory file.
+ const REMOVEDIR = 0x01;
+ }
+}
+
+/// User-provided value that can be attached to objects that is
+/// retained when extracted from the kernel.
+pub type userdata = u64;
+
+/// Relative to which position the offset of the file descriptor
+/// should be set.
+#[repr(u8)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub enum whence {
+ /// Seek relative to current position.
+ CUR = 1,
+ /// Seek relative to end-of-file.
+ END = 2,
+ /// Seek relative to start-of-file.
+ SET = 3,
+ #[doc(hidden)] _NonExhaustive = -1 as isize as u8,
+}
+
+/// Auxiliary vector entry.
+///
+/// The auxiliary vector is a list of key-value pairs that is
+/// provided to the process on startup. Unlike structures, it is
+/// extensible, as it is possible to add new records later on.
+/// The auxiliary vector is always terminated by an entry having
+/// type [`NULL`](enum.auxtype.html#variant.NULL).
+///
+/// The auxiliary vector is part of the x86-64 ABI, but is used by
+/// this environment on all architectures.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct auxv {
+ /// The type of the auxiliary vector entry.
+ pub a_type: auxtype,
+ pub union: auxv_union
+}
+/// A union inside `auxv`.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union auxv_union {
+ /// Used when `a_type` is [`ARGDATALEN`](enum.auxtype.html#variant.ARGDATALEN), [`CANARYLEN`](enum.auxtype.html#variant.CANARYLEN), [`NCPUS`](enum.auxtype.html#variant.NCPUS), [`PAGESZ`](enum.auxtype.html#variant.PAGESZ), [`PHNUM`](enum.auxtype.html#variant.PHNUM), or [`TID`](enum.auxtype.html#variant.TID).
+/// A numerical value.
+ pub a_val: usize,
+ /// Used when `a_type` is [`ARGDATA`](enum.auxtype.html#variant.ARGDATA), [`BASE`](enum.auxtype.html#variant.BASE), [`CANARY`](enum.auxtype.html#variant.CANARY), [`PHDR`](enum.auxtype.html#variant.PHDR), [`PID`](enum.auxtype.html#variant.PID), or [`SYSINFO_EHDR`](enum.auxtype.html#variant.SYSINFO_EHDR).
+/// A pointer value.
+ pub a_ptr: *mut (),
+}
+#[test]
+#[cfg(target_pointer_width = "32")]
+fn auxv_layout_test_32() {
+ assert_eq!(::core::mem::size_of::<auxv>(), 8);
+ assert_eq!(::core::mem::align_of::<auxv>(), 4);
+ unsafe {
+ let obj: auxv = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.a_type as *const _ as usize - base, 0);
+ assert_eq!(&obj.union.a_val as *const _ as usize - base, 4);
+ assert_eq!(&obj.union.a_ptr as *const _ as usize - base, 4);
+ }
+}
+#[test]
+#[cfg(target_pointer_width = "64")]
+fn auxv_layout_test_64() {
+ assert_eq!(::core::mem::size_of::<auxv>(), 16);
+ assert_eq!(::core::mem::align_of::<auxv>(), 8);
+ unsafe {
+ let obj: auxv = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.a_type as *const _ as usize - base, 0);
+ assert_eq!(&obj.union.a_val as *const _ as usize - base, 8);
+ assert_eq!(&obj.union.a_ptr as *const _ as usize - base, 8);
+ }
+}
+
+/// A region of memory for scatter/gather writes.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct ciovec {
+ /// The address and length of the buffer to be written.
+ pub buf: (*const (), usize),
+}
+#[test]
+#[cfg(target_pointer_width = "32")]
+fn ciovec_layout_test_32() {
+ assert_eq!(::core::mem::size_of::<ciovec>(), 8);
+ assert_eq!(::core::mem::align_of::<ciovec>(), 4);
+ unsafe {
+ let obj: ciovec = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.buf.0 as *const _ as usize - base, 0);
+ assert_eq!(&obj.buf.1 as *const _ as usize - base, 4);
+ }
+}
+#[test]
+#[cfg(target_pointer_width = "64")]
+fn ciovec_layout_test_64() {
+ assert_eq!(::core::mem::size_of::<ciovec>(), 16);
+ assert_eq!(::core::mem::align_of::<ciovec>(), 8);
+ unsafe {
+ let obj: ciovec = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.buf.0 as *const _ as usize - base, 0);
+ assert_eq!(&obj.buf.1 as *const _ as usize - base, 8);
+ }
+}
+
+/// A directory entry.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct dirent {
+ /// The offset of the next directory entry stored in this
+ /// directory.
+ pub d_next: dircookie,
+ /// The serial number of the file referred to by this
+ /// directory entry.
+ pub d_ino: inode,
+ /// The length of the name of the directory entry.
+ pub d_namlen: u32,
+ /// The type of the file referred to by this directory
+ /// entry.
+ pub d_type: filetype,
+}
+#[test]
+fn dirent_layout_test() {
+ assert_eq!(::core::mem::size_of::<dirent>(), 24);
+ assert_eq!(::core::mem::align_of::<dirent>(), 8);
+ unsafe {
+ let obj: dirent = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.d_next as *const _ as usize - base, 0);
+ assert_eq!(&obj.d_ino as *const _ as usize - base, 8);
+ assert_eq!(&obj.d_namlen as *const _ as usize - base, 16);
+ assert_eq!(&obj.d_type as *const _ as usize - base, 20);
+ }
+}
+
+/// An event that occurred.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct event {
+ /// User-provided value that got attached to
+ /// [`subscription.userdata`](struct.subscription.html#structfield.userdata).
+ pub userdata: userdata,
+ /// If non-zero, an error that occurred while processing
+ /// the subscription request.
+ pub error: errno,
+ /// The type of the event that occurred.
+ pub type_: eventtype,
+ pub union: event_union
+}
+/// A union inside `event`.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union event_union {
+ /// Used when `type_` is [`FD_READ`](enum.eventtype.html#variant.FD_READ) or [`FD_WRITE`](enum.eventtype.html#variant.FD_WRITE).
+ pub fd_readwrite: event_fd_readwrite,
+ /// Used when `type_` is [`PROC_TERMINATE`](enum.eventtype.html#variant.PROC_TERMINATE).
+ pub proc_terminate: event_proc_terminate,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct event_fd_readwrite {
+ /// The number of bytes available
+ /// for reading or writing.
+ pub nbytes: filesize,
+ /// Obsolete.
+ pub unused: [u8; 4],
+ /// The state of the file
+ /// descriptor.
+ pub flags: eventrwflags,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct event_proc_terminate {
+ /// Obsolete.
+ pub unused: [u8; 4],
+ /// If zero, the process has
+ /// exited.
+ /// Otherwise, the signal
+ /// condition causing it to
+ /// terminated.
+ pub signal: signal,
+ /// If exited, the exit code of
+ /// the process.
+ pub exitcode: exitcode,
+}
+#[test]
+fn event_layout_test() {
+ assert_eq!(::core::mem::size_of::<event>(), 32);
+ assert_eq!(::core::mem::align_of::<event>(), 8);
+ unsafe {
+ let obj: event = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.userdata as *const _ as usize - base, 0);
+ assert_eq!(&obj.error as *const _ as usize - base, 8);
+ assert_eq!(&obj.type_ as *const _ as usize - base, 10);
+ assert_eq!(&obj.union.fd_readwrite.nbytes as *const _ as usize - base, 16);
+ assert_eq!(&obj.union.fd_readwrite.unused as *const _ as usize - base, 24);
+ assert_eq!(&obj.union.fd_readwrite.flags as *const _ as usize - base, 28);
+ assert_eq!(&obj.union.proc_terminate.unused as *const _ as usize - base, 16);
+ assert_eq!(&obj.union.proc_terminate.signal as *const _ as usize - base, 20);
+ assert_eq!(&obj.union.proc_terminate.exitcode as *const _ as usize - base, 24);
+ }
+}
+
+/// File descriptor attributes.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct fdstat {
+ /// File type.
+ pub fs_filetype: filetype,
+ /// File descriptor flags.
+ pub fs_flags: fdflags,
+ /// Rights that apply to this file descriptor.
+ pub fs_rights_base: rights,
+ /// Maximum set of rights that can be installed on new
+ /// file descriptors that are created through this file
+ /// descriptor, e.g., through [`file_open()`](fn.file_open.html).
+ pub fs_rights_inheriting: rights,
+}
+#[test]
+fn fdstat_layout_test() {
+ assert_eq!(::core::mem::size_of::<fdstat>(), 24);
+ assert_eq!(::core::mem::align_of::<fdstat>(), 8);
+ unsafe {
+ let obj: fdstat = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.fs_filetype as *const _ as usize - base, 0);
+ assert_eq!(&obj.fs_flags as *const _ as usize - base, 2);
+ assert_eq!(&obj.fs_rights_base as *const _ as usize - base, 8);
+ assert_eq!(&obj.fs_rights_inheriting as *const _ as usize - base, 16);
+ }
+}
+
+/// File attributes.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct filestat {
+ /// Device ID of device containing the file.
+ pub st_dev: device,
+ /// File serial number.
+ pub st_ino: inode,
+ /// File type.
+ pub st_filetype: filetype,
+ /// Number of hard links to the file.
+ pub st_nlink: linkcount,
+ /// For regular files, the file size in bytes. For
+ /// symbolic links, the length in bytes of the pathname
+ /// contained in the symbolic link.
+ pub st_size: filesize,
+ /// Last data access timestamp.
+ pub st_atim: timestamp,
+ /// Last data modification timestamp.
+ pub st_mtim: timestamp,
+ /// Last file status change timestamp.
+ pub st_ctim: timestamp,
+}
+#[test]
+fn filestat_layout_test() {
+ assert_eq!(::core::mem::size_of::<filestat>(), 56);
+ assert_eq!(::core::mem::align_of::<filestat>(), 8);
+ unsafe {
+ let obj: filestat = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.st_dev as *const _ as usize - base, 0);
+ assert_eq!(&obj.st_ino as *const _ as usize - base, 8);
+ assert_eq!(&obj.st_filetype as *const _ as usize - base, 16);
+ assert_eq!(&obj.st_nlink as *const _ as usize - base, 20);
+ assert_eq!(&obj.st_size as *const _ as usize - base, 24);
+ assert_eq!(&obj.st_atim as *const _ as usize - base, 32);
+ assert_eq!(&obj.st_mtim as *const _ as usize - base, 40);
+ assert_eq!(&obj.st_ctim as *const _ as usize - base, 48);
+ }
+}
+
+/// A region of memory for scatter/gather reads.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct iovec {
+ /// The address and length of the buffer to be filled.
+ pub buf: (*mut (), usize),
+}
+#[test]
+#[cfg(target_pointer_width = "32")]
+fn iovec_layout_test_32() {
+ assert_eq!(::core::mem::size_of::<iovec>(), 8);
+ assert_eq!(::core::mem::align_of::<iovec>(), 4);
+ unsafe {
+ let obj: iovec = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.buf.0 as *const _ as usize - base, 0);
+ assert_eq!(&obj.buf.1 as *const _ as usize - base, 4);
+ }
+}
+#[test]
+#[cfg(target_pointer_width = "64")]
+fn iovec_layout_test_64() {
+ assert_eq!(::core::mem::size_of::<iovec>(), 16);
+ assert_eq!(::core::mem::align_of::<iovec>(), 8);
+ unsafe {
+ let obj: iovec = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.buf.0 as *const _ as usize - base, 0);
+ assert_eq!(&obj.buf.1 as *const _ as usize - base, 8);
+ }
+}
+
+/// Path lookup properties.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct lookup {
+ /// The working directory at which the resolution of the
+ /// path starts.
+ pub fd: fd,
+ /// Flags determining the method of how the path is
+ /// resolved.
+ pub flags: lookupflags,
+}
+#[test]
+fn lookup_layout_test() {
+ assert_eq!(::core::mem::size_of::<lookup>(), 8);
+ assert_eq!(::core::mem::align_of::<lookup>(), 4);
+ unsafe {
+ let obj: lookup = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.fd as *const _ as usize - base, 0);
+ assert_eq!(&obj.flags as *const _ as usize - base, 4);
+ }
+}
+
+/// Entry point for a process (`_start`).
+///
+/// **auxv**:
+/// The auxiliary vector. See [`auxv`](struct.auxv.html).
+pub type processentry = unsafe extern "C" fn(
+ auxv: *const auxv,
+) -> ();
+
+/// Arguments of [`sock_recv()`](fn.sock_recv.html).
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct recv_in {
+ /// List of scatter/gather vectors where message data
+ /// should be stored.
+ pub ri_data: (*const iovec, usize),
+ /// Buffer where numbers of incoming file descriptors
+ /// should be stored.
+ pub ri_fds: (*mut fd, usize),
+ /// Message flags.
+ pub ri_flags: riflags,
+}
+#[test]
+#[cfg(target_pointer_width = "32")]
+fn recv_in_layout_test_32() {
+ assert_eq!(::core::mem::size_of::<recv_in>(), 20);
+ assert_eq!(::core::mem::align_of::<recv_in>(), 4);
+ unsafe {
+ let obj: recv_in = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.ri_data.0 as *const _ as usize - base, 0);
+ assert_eq!(&obj.ri_data.1 as *const _ as usize - base, 4);
+ assert_eq!(&obj.ri_fds.0 as *const _ as usize - base, 8);
+ assert_eq!(&obj.ri_fds.1 as *const _ as usize - base, 12);
+ assert_eq!(&obj.ri_flags as *const _ as usize - base, 16);
+ }
+}
+#[test]
+#[cfg(target_pointer_width = "64")]
+fn recv_in_layout_test_64() {
+ assert_eq!(::core::mem::size_of::<recv_in>(), 40);
+ assert_eq!(::core::mem::align_of::<recv_in>(), 8);
+ unsafe {
+ let obj: recv_in = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.ri_data.0 as *const _ as usize - base, 0);
+ assert_eq!(&obj.ri_data.1 as *const _ as usize - base, 8);
+ assert_eq!(&obj.ri_fds.0 as *const _ as usize - base, 16);
+ assert_eq!(&obj.ri_fds.1 as *const _ as usize - base, 24);
+ assert_eq!(&obj.ri_flags as *const _ as usize - base, 32);
+ }
+}
+
+/// Results of [`sock_recv()`](fn.sock_recv.html).
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct recv_out {
+ /// Number of bytes stored in [`recv_in.ri_data`](struct.recv_in.html#structfield.ri_data).
+ pub ro_datalen: usize,
+ /// Number of file descriptors stored in [`recv_in.ri_fds`](struct.recv_in.html#structfield.ri_fds).
+ pub ro_fdslen: usize,
+ /// Fields that were used by previous implementations.
+ pub ro_unused: [u8; 40],
+ /// Message flags.
+ pub ro_flags: roflags,
+}
+#[test]
+#[cfg(target_pointer_width = "32")]
+fn recv_out_layout_test_32() {
+ assert_eq!(::core::mem::size_of::<recv_out>(), 52);
+ assert_eq!(::core::mem::align_of::<recv_out>(), 4);
+ unsafe {
+ let obj: recv_out = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.ro_datalen as *const _ as usize - base, 0);
+ assert_eq!(&obj.ro_fdslen as *const _ as usize - base, 4);
+ assert_eq!(&obj.ro_unused as *const _ as usize - base, 8);
+ assert_eq!(&obj.ro_flags as *const _ as usize - base, 48);
+ }
+}
+#[test]
+#[cfg(target_pointer_width = "64")]
+fn recv_out_layout_test_64() {
+ assert_eq!(::core::mem::size_of::<recv_out>(), 64);
+ assert_eq!(::core::mem::align_of::<recv_out>(), 8);
+ unsafe {
+ let obj: recv_out = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.ro_datalen as *const _ as usize - base, 0);
+ assert_eq!(&obj.ro_fdslen as *const _ as usize - base, 8);
+ assert_eq!(&obj.ro_unused as *const _ as usize - base, 16);
+ assert_eq!(&obj.ro_flags as *const _ as usize - base, 56);
+ }
+}
+
+/// Arguments of [`sock_send()`](fn.sock_send.html).
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct send_in {
+ /// List of scatter/gather vectors where message data
+ /// should be retrieved.
+ pub si_data: (*const ciovec, usize),
+ /// File descriptors that need to be attached to the
+ /// message.
+ pub si_fds: (*const fd, usize),
+ /// Message flags.
+ pub si_flags: siflags,
+}
+#[test]
+#[cfg(target_pointer_width = "32")]
+fn send_in_layout_test_32() {
+ assert_eq!(::core::mem::size_of::<send_in>(), 20);
+ assert_eq!(::core::mem::align_of::<send_in>(), 4);
+ unsafe {
+ let obj: send_in = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.si_data.0 as *const _ as usize - base, 0);
+ assert_eq!(&obj.si_data.1 as *const _ as usize - base, 4);
+ assert_eq!(&obj.si_fds.0 as *const _ as usize - base, 8);
+ assert_eq!(&obj.si_fds.1 as *const _ as usize - base, 12);
+ assert_eq!(&obj.si_flags as *const _ as usize - base, 16);
+ }
+}
+#[test]
+#[cfg(target_pointer_width = "64")]
+fn send_in_layout_test_64() {
+ assert_eq!(::core::mem::size_of::<send_in>(), 40);
+ assert_eq!(::core::mem::align_of::<send_in>(), 8);
+ unsafe {
+ let obj: send_in = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.si_data.0 as *const _ as usize - base, 0);
+ assert_eq!(&obj.si_data.1 as *const _ as usize - base, 8);
+ assert_eq!(&obj.si_fds.0 as *const _ as usize - base, 16);
+ assert_eq!(&obj.si_fds.1 as *const _ as usize - base, 24);
+ assert_eq!(&obj.si_flags as *const _ as usize - base, 32);
+ }
+}
+
+/// Results of [`sock_send()`](fn.sock_send.html).
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct send_out {
+ /// Number of bytes transmitted.
+ pub so_datalen: usize,
+}
+#[test]
+#[cfg(target_pointer_width = "32")]
+fn send_out_layout_test_32() {
+ assert_eq!(::core::mem::size_of::<send_out>(), 4);
+ assert_eq!(::core::mem::align_of::<send_out>(), 4);
+ unsafe {
+ let obj: send_out = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.so_datalen as *const _ as usize - base, 0);
+ }
+}
+#[test]
+#[cfg(target_pointer_width = "64")]
+fn send_out_layout_test_64() {
+ assert_eq!(::core::mem::size_of::<send_out>(), 8);
+ assert_eq!(::core::mem::align_of::<send_out>(), 8);
+ unsafe {
+ let obj: send_out = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.so_datalen as *const _ as usize - base, 0);
+ }
+}
+
+/// Subscription to an event.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct subscription {
+ /// User-provided value that is attached to the
+ /// subscription in the kernel and returned through
+ /// [`event.userdata`](struct.event.html#structfield.userdata).
+ pub userdata: userdata,
+ /// Used by previous implementations. Ignored.
+ pub unused: u16,
+ /// The type of the event to which to subscribe.
+ ///
+ /// Currently, [`CONDVAR`](enum.eventtype.html#variant.CONDVAR),
+ /// [`LOCK_RDLOCK`](enum.eventtype.html#variant.LOCK_RDLOCK), and [`LOCK_WRLOCK`](enum.eventtype.html#variant.LOCK_WRLOCK)
+ /// must be provided as the first subscription and may
+ /// only be followed by up to one other subscription,
+ /// having type [`CLOCK`](enum.eventtype.html#variant.CLOCK).
+ pub type_: eventtype,
+ pub union: subscription_union
+}
+/// A union inside `subscription`.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union subscription_union {
+ /// Used when `type_` is [`CLOCK`](enum.eventtype.html#variant.CLOCK).
+ pub clock: subscription_clock,
+ /// Used when `type_` is [`CONDVAR`](enum.eventtype.html#variant.CONDVAR).
+ pub condvar: subscription_condvar,
+ /// Used when `type_` is [`FD_READ`](enum.eventtype.html#variant.FD_READ) or [`FD_WRITE`](enum.eventtype.html#variant.FD_WRITE).
+ pub fd_readwrite: subscription_fd_readwrite,
+ /// Used when `type_` is [`LOCK_RDLOCK`](enum.eventtype.html#variant.LOCK_RDLOCK) or [`LOCK_WRLOCK`](enum.eventtype.html#variant.LOCK_WRLOCK).
+ pub lock: subscription_lock,
+ /// Used when `type_` is [`PROC_TERMINATE`](enum.eventtype.html#variant.PROC_TERMINATE).
+ pub proc_terminate: subscription_proc_terminate,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct subscription_clock {
+ /// The user-defined unique
+ /// identifier of the clock.
+ pub identifier: userdata,
+ /// The clock against which the
+ /// timestamp should be compared.
+ pub clock_id: clockid,
+ /// The absolute or relative
+ /// timestamp.
+ pub timeout: timestamp,
+ /// The amount of time that the
+ /// kernel may wait additionally
+ /// to coalesce with other events.
+ pub precision: timestamp,
+ /// Flags specifying whether the
+ /// timeout is absolute or
+ /// relative.
+ pub flags: subclockflags,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct subscription_condvar {
+ /// The condition variable on
+ /// which to wait to be woken up.
+ pub condvar: *mut condvar,
+ /// The lock that will be
+ /// released while waiting.
+ ///
+ /// The lock will be reacquired
+ /// for writing when the condition
+ /// variable triggers.
+ pub lock: *mut lock,
+ /// Whether the condition variable
+ /// is stored in private or shared
+ /// memory.
+ pub condvar_scope: scope,
+ /// Whether the lock is stored in
+ /// private or shared memory.
+ pub lock_scope: scope,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct subscription_fd_readwrite {
+ /// The file descriptor on which
+ /// to wait for it to become ready
+ /// for reading or writing.
+ pub fd: fd,
+ /// Under which conditions to
+ /// trigger.
+ pub flags: subrwflags,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct subscription_lock {
+ /// The lock that will be acquired
+ /// for reading or writing.
+ pub lock: *mut lock,
+ /// Whether the lock is stored in
+ /// private or shared memory.
+ pub lock_scope: scope,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct subscription_proc_terminate {
+ /// The process descriptor on
+ /// which to wait for process
+ /// termination.
+ pub fd: fd,
+}
+#[test]
+#[cfg(target_pointer_width = "32")]
+fn subscription_layout_test_32() {
+ assert_eq!(::core::mem::size_of::<subscription>(), 56);
+ assert_eq!(::core::mem::align_of::<subscription>(), 8);
+ unsafe {
+ let obj: subscription = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.userdata as *const _ as usize - base, 0);
+ assert_eq!(&obj.unused as *const _ as usize - base, 8);
+ assert_eq!(&obj.type_ as *const _ as usize - base, 10);
+ assert_eq!(&obj.union.clock.identifier as *const _ as usize - base, 16);
+ assert_eq!(&obj.union.clock.clock_id as *const _ as usize - base, 24);
+ assert_eq!(&obj.union.clock.timeout as *const _ as usize - base, 32);
+ assert_eq!(&obj.union.clock.precision as *const _ as usize - base, 40);
+ assert_eq!(&obj.union.clock.flags as *const _ as usize - base, 48);
+ assert_eq!(&obj.union.condvar.condvar as *const _ as usize - base, 16);
+ assert_eq!(&obj.union.condvar.lock as *const _ as usize - base, 20);
+ assert_eq!(&obj.union.condvar.condvar_scope as *const _ as usize - base, 24);
+ assert_eq!(&obj.union.condvar.lock_scope as *const _ as usize - base, 25);
+ assert_eq!(&obj.union.fd_readwrite.fd as *const _ as usize - base, 16);
+ assert_eq!(&obj.union.fd_readwrite.flags as *const _ as usize - base, 20);
+ assert_eq!(&obj.union.lock.lock as *const _ as usize - base, 16);
+ assert_eq!(&obj.union.lock.lock_scope as *const _ as usize - base, 20);
+ assert_eq!(&obj.union.proc_terminate.fd as *const _ as usize - base, 16);
+ }
+}
+#[test]
+#[cfg(target_pointer_width = "64")]
+fn subscription_layout_test_64() {
+ assert_eq!(::core::mem::size_of::<subscription>(), 56);
+ assert_eq!(::core::mem::align_of::<subscription>(), 8);
+ unsafe {
+ let obj: subscription = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.userdata as *const _ as usize - base, 0);
+ assert_eq!(&obj.unused as *const _ as usize - base, 8);
+ assert_eq!(&obj.type_ as *const _ as usize - base, 10);
+ assert_eq!(&obj.union.clock.identifier as *const _ as usize - base, 16);
+ assert_eq!(&obj.union.clock.clock_id as *const _ as usize - base, 24);
+ assert_eq!(&obj.union.clock.timeout as *const _ as usize - base, 32);
+ assert_eq!(&obj.union.clock.precision as *const _ as usize - base, 40);
+ assert_eq!(&obj.union.clock.flags as *const _ as usize - base, 48);
+ assert_eq!(&obj.union.condvar.condvar as *const _ as usize - base, 16);
+ assert_eq!(&obj.union.condvar.lock as *const _ as usize - base, 24);
+ assert_eq!(&obj.union.condvar.condvar_scope as *const _ as usize - base, 32);
+ assert_eq!(&obj.union.condvar.lock_scope as *const _ as usize - base, 33);
+ assert_eq!(&obj.union.fd_readwrite.fd as *const _ as usize - base, 16);
+ assert_eq!(&obj.union.fd_readwrite.flags as *const _ as usize - base, 20);
+ assert_eq!(&obj.union.lock.lock as *const _ as usize - base, 16);
+ assert_eq!(&obj.union.lock.lock_scope as *const _ as usize - base, 24);
+ assert_eq!(&obj.union.proc_terminate.fd as *const _ as usize - base, 16);
+ }
+}
+
+/// The Thread Control Block (TCB).
+///
+/// After a thread begins execution (at program startup or when
+/// created through [`thread_create()`](fn.thread_create.html)), the CPU's registers
+/// controlling Thread-Local Storage (TLS) will already be
+/// initialized. They will point to an area only containing the
+/// TCB.
+///
+/// If the thread needs space for storing thread-specific
+/// variables, the thread may allocate a larger area and adjust
+/// the CPU's registers to point to that area instead. However, it
+/// does need to make sure that the TCB is copied over to the new
+/// TLS area.
+///
+/// The purpose of the TCB is that it allows light-weight
+/// emulators to store information related to individual threads.
+/// For example, it may be used to store a copy of the CPU
+/// registers prior emulation, so that TLS for the host system
+/// can be restored if needed.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct tcb {
+ /// Pointer that may be freely assigned by the system. Its
+ /// value cannot be interpreted by the application.
+ pub parent: *mut (),
+}
+#[test]
+#[cfg(target_pointer_width = "32")]
+fn tcb_layout_test_32() {
+ assert_eq!(::core::mem::size_of::<tcb>(), 4);
+ assert_eq!(::core::mem::align_of::<tcb>(), 4);
+ unsafe {
+ let obj: tcb = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.parent as *const _ as usize - base, 0);
+ }
+}
+#[test]
+#[cfg(target_pointer_width = "64")]
+fn tcb_layout_test_64() {
+ assert_eq!(::core::mem::size_of::<tcb>(), 8);
+ assert_eq!(::core::mem::align_of::<tcb>(), 8);
+ unsafe {
+ let obj: tcb = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.parent as *const _ as usize - base, 0);
+ }
+}
+
+/// Entry point for additionally created threads.
+///
+/// **tid**:
+/// Thread ID of the current thread.
+///
+/// **aux**:
+/// Copy of the value stored in
+/// [`threadattr.argument`](struct.threadattr.html#structfield.argument).
+pub type threadentry = unsafe extern "C" fn(
+ tid: tid,
+ aux: *mut (),
+) -> ();
+
+/// Attributes for thread creation.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct threadattr {
+ /// Initial program counter value.
+ pub entry_point: threadentry,
+ /// Region allocated to serve as stack space.
+ pub stack: (*mut (), usize),
+ /// Argument to be forwarded to the entry point function.
+ pub argument: *mut (),
+}
+#[test]
+#[cfg(target_pointer_width = "32")]
+fn threadattr_layout_test_32() {
+ assert_eq!(::core::mem::size_of::<threadattr>(), 16);
+ assert_eq!(::core::mem::align_of::<threadattr>(), 4);
+ unsafe {
+ let obj: threadattr = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.entry_point as *const _ as usize - base, 0);
+ assert_eq!(&obj.stack.0 as *const _ as usize - base, 4);
+ assert_eq!(&obj.stack.1 as *const _ as usize - base, 8);
+ assert_eq!(&obj.argument as *const _ as usize - base, 12);
+ }
+}
+#[test]
+#[cfg(target_pointer_width = "64")]
+fn threadattr_layout_test_64() {
+ assert_eq!(::core::mem::size_of::<threadattr>(), 32);
+ assert_eq!(::core::mem::align_of::<threadattr>(), 8);
+ unsafe {
+ let obj: threadattr = ::core::mem::uninitialized();
+ let base = &obj as *const _ as usize;
+ assert_eq!(&obj.entry_point as *const _ as usize - base, 0);
+ assert_eq!(&obj.stack.0 as *const _ as usize - base, 8);
+ assert_eq!(&obj.stack.1 as *const _ as usize - base, 16);
+ assert_eq!(&obj.argument as *const _ as usize - base, 24);
+ }
+}
+
+/// The table with pointers to all syscall implementations.
+#[allow(improper_ctypes)]
+extern "C" {
+ fn cloudabi_sys_clock_res_get(_: clockid, _: *mut timestamp) -> errno;
+ fn cloudabi_sys_clock_time_get(_: clockid, _: timestamp, _: *mut timestamp) -> errno;
+ fn cloudabi_sys_condvar_signal(_: *mut condvar, _: scope, _: nthreads) -> errno;
+ fn cloudabi_sys_fd_close(_: fd) -> errno;
+ fn cloudabi_sys_fd_create1(_: filetype, _: *mut fd) -> errno;
+ fn cloudabi_sys_fd_create2(_: filetype, _: *mut fd, _: *mut fd) -> errno;
+ fn cloudabi_sys_fd_datasync(_: fd) -> errno;
+ fn cloudabi_sys_fd_dup(_: fd, _: *mut fd) -> errno;
+ fn cloudabi_sys_fd_pread(_: fd, _: *const iovec, _: usize, _: filesize, _: *mut usize) -> errno;
+ fn cloudabi_sys_fd_pwrite(_: fd, _: *const ciovec, _: usize, _: filesize, _: *mut usize) -> errno;
+ fn cloudabi_sys_fd_read(_: fd, _: *const iovec, _: usize, _: *mut usize) -> errno;
+ fn cloudabi_sys_fd_replace(_: fd, _: fd) -> errno;
+ fn cloudabi_sys_fd_seek(_: fd, _: filedelta, _: whence, _: *mut filesize) -> errno;
+ fn cloudabi_sys_fd_stat_get(_: fd, _: *mut fdstat) -> errno;
+ fn cloudabi_sys_fd_stat_put(_: fd, _: *const fdstat, _: fdsflags) -> errno;
+ fn cloudabi_sys_fd_sync(_: fd) -> errno;
+ fn cloudabi_sys_fd_write(_: fd, _: *const ciovec, _: usize, _: *mut usize) -> errno;
+ fn cloudabi_sys_file_advise(_: fd, _: filesize, _: filesize, _: advice) -> errno;
+ fn cloudabi_sys_file_allocate(_: fd, _: filesize, _: filesize) -> errno;
+ fn cloudabi_sys_file_create(_: fd, _: *const u8, _: usize, _: filetype) -> errno;
+ fn cloudabi_sys_file_link(_: lookup, _: *const u8, _: usize, _: fd, _: *const u8, _: usize) -> errno;
+ fn cloudabi_sys_file_open(_: lookup, _: *const u8, _: usize, _: oflags, _: *const fdstat, _: *mut fd) -> errno;
+ fn cloudabi_sys_file_readdir(_: fd, _: *mut (), _: usize, _: dircookie, _: *mut usize) -> errno;
+ fn cloudabi_sys_file_readlink(_: fd, _: *const u8, _: usize, _: *mut u8, _: usize, _: *mut usize) -> errno;
+ fn cloudabi_sys_file_rename(_: fd, _: *const u8, _: usize, _: fd, _: *const u8, _: usize) -> errno;
+ fn cloudabi_sys_file_stat_fget(_: fd, _: *mut filestat) -> errno;
+ fn cloudabi_sys_file_stat_fput(_: fd, _: *const filestat, _: fsflags) -> errno;
+ fn cloudabi_sys_file_stat_get(_: lookup, _: *const u8, _: usize, _: *mut filestat) -> errno;
+ fn cloudabi_sys_file_stat_put(_: lookup, _: *const u8, _: usize, _: *const filestat, _: fsflags) -> errno;
+ fn cloudabi_sys_file_symlink(_: *const u8, _: usize, _: fd, _: *const u8, _: usize) -> errno;
+ fn cloudabi_sys_file_unlink(_: fd, _: *const u8, _: usize, _: ulflags) -> errno;
+ fn cloudabi_sys_lock_unlock(_: *mut lock, _: scope) -> errno;
+ fn cloudabi_sys_mem_advise(_: *mut (), _: usize, _: advice) -> errno;
+ fn cloudabi_sys_mem_map(_: *mut (), _: usize, _: mprot, _: mflags, _: fd, _: filesize, _: *mut *mut ()) -> errno;
+ fn cloudabi_sys_mem_protect(_: *mut (), _: usize, _: mprot) -> errno;
+ fn cloudabi_sys_mem_sync(_: *mut (), _: usize, _: msflags) -> errno;
+ fn cloudabi_sys_mem_unmap(_: *mut (), _: usize) -> errno;
+ fn cloudabi_sys_poll(_: *const subscription, _: *mut event, _: usize, _: *mut usize) -> errno;
+ fn cloudabi_sys_proc_exec(_: fd, _: *const (), _: usize, _: *const fd, _: usize) -> errno;
+ fn cloudabi_sys_proc_exit(_: exitcode) -> !;
+ fn cloudabi_sys_proc_fork(_: *mut fd, _: *mut tid) -> errno;
+ fn cloudabi_sys_proc_raise(_: signal) -> errno;
+ fn cloudabi_sys_random_get(_: *mut (), _: usize) -> errno;
+ fn cloudabi_sys_sock_recv(_: fd, _: *const recv_in, _: *mut recv_out) -> errno;
+ fn cloudabi_sys_sock_send(_: fd, _: *const send_in, _: *mut send_out) -> errno;
+ fn cloudabi_sys_sock_shutdown(_: fd, _: sdflags) -> errno;
+ fn cloudabi_sys_thread_create(_: *mut threadattr, _: *mut tid) -> errno;
+ fn cloudabi_sys_thread_exit(_: *mut lock, _: scope) -> !;
+ fn cloudabi_sys_thread_yield() -> errno;
+}
+
+/// Obtains the resolution of a clock.
+///
+/// ## Parameters
+///
+/// **clock_id**:
+/// The clock for which the resolution needs to be
+/// returned.
+///
+/// **resolution**:
+/// The resolution of the clock.
+#[inline]
+pub unsafe fn clock_res_get(clock_id_: clockid, resolution_: &mut timestamp) -> errno {
+ cloudabi_sys_clock_res_get(clock_id_, resolution_)
+}
+
+/// Obtains the time value of a clock.
+///
+/// ## Parameters
+///
+/// **clock_id**:
+/// The clock for which the time needs to be
+/// returned.
+///
+/// **precision**:
+/// The maximum lag (exclusive) that the returned
+/// time value may have, compared to its actual
+/// value.
+///
+/// **time**:
+/// The time value of the clock.
+#[inline]
+pub unsafe fn clock_time_get(clock_id_: clockid, precision_: timestamp, time_: &mut timestamp) -> errno {
+ cloudabi_sys_clock_time_get(clock_id_, precision_, time_)
+}
+
+/// Wakes up threads waiting on a userspace condition variable.
+///
+/// If an invocation of this system call causes all waiting
+/// threads to be woken up, the value of the condition variable
+/// is set to [`CONDVAR_HAS_NO_WAITERS`](constant.CONDVAR_HAS_NO_WAITERS.html). As long as the condition
+/// variable is set to this value, it is not needed to invoke this
+/// system call.
+///
+/// ## Parameters
+///
+/// **condvar**:
+/// The userspace condition variable that has
+/// waiting threads.
+///
+/// **scope**:
+/// Whether the condition variable is stored in
+/// private or shared memory.
+///
+/// **nwaiters**:
+/// The number of threads that need to be woken
+/// up. If it exceeds the number of waiting
+/// threads, all threads are woken up.
+#[inline]
+pub unsafe fn condvar_signal(condvar_: *mut condvar, scope_: scope, nwaiters_: nthreads) -> errno {
+ cloudabi_sys_condvar_signal(condvar_, scope_, nwaiters_)
+}
+
+/// Closes a file descriptor.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The file descriptor that needs to be closed.
+#[inline]
+pub unsafe fn fd_close(fd_: fd) -> errno {
+ cloudabi_sys_fd_close(fd_)
+}
+
+/// Creates a file descriptor.
+///
+/// ## Parameters
+///
+/// **type**:
+/// Possible values:
+///
+/// - [`SHARED_MEMORY`](enum.filetype.html#variant.SHARED_MEMORY):
+/// Creates an anonymous shared memory
+/// object.
+///
+/// **fd**:
+/// The file descriptor that has been created.
+#[inline]
+pub unsafe fn fd_create1(type_: filetype, fd_: &mut fd) -> errno {
+ cloudabi_sys_fd_create1(type_, fd_)
+}
+
+/// Creates a pair of file descriptors.
+///
+/// ## Parameters
+///
+/// **type**:
+/// Possible values:
+///
+/// - [`SOCKET_DGRAM`](enum.filetype.html#variant.SOCKET_DGRAM):
+/// Creates a UNIX datagram socket pair.
+/// - [`SOCKET_STREAM`](enum.filetype.html#variant.SOCKET_STREAM):
+/// Creates a UNIX byte-stream socket
+/// pair.
+///
+/// **fd1**:
+/// The first file descriptor of the pair.
+///
+/// **fd2**:
+/// The second file descriptor of the pair.
+#[inline]
+pub unsafe fn fd_create2(type_: filetype, fd1_: &mut fd, fd2_: &mut fd) -> errno {
+ cloudabi_sys_fd_create2(type_, fd1_, fd2_)
+}
+
+/// Synchronizes the data of a file to disk.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The file descriptor of the file whose data
+/// needs to be synchronized to disk.
+#[inline]
+pub unsafe fn fd_datasync(fd_: fd) -> errno {
+ cloudabi_sys_fd_datasync(fd_)
+}
+
+/// Duplicates a file descriptor.
+///
+/// ## Parameters
+///
+/// **from**:
+/// The file descriptor that needs to be
+/// duplicated.
+///
+/// **fd**:
+/// The new file descriptor.
+#[inline]
+pub unsafe fn fd_dup(from_: fd, fd_: &mut fd) -> errno {
+ cloudabi_sys_fd_dup(from_, fd_)
+}
+
+/// Reads from a file descriptor, without using and updating the
+/// file descriptor's offset.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The file descriptor from which data should be
+/// read.
+///
+/// **iovs**:
+/// List of scatter/gather vectors where data
+/// should be stored.
+///
+/// **offset**:
+/// The offset within the file at which reading
+/// should start.
+///
+/// **nread**:
+/// The number of bytes read.
+#[inline]
+pub unsafe fn fd_pread(fd_: fd, iovs_: &[iovec], offset_: filesize, nread_: &mut usize) -> errno {
+ cloudabi_sys_fd_pread(fd_, iovs_.as_ptr(), iovs_.len(), offset_, nread_)
+}
+
+/// Writes to a file descriptor, without using and updating the
+/// file descriptor's offset.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The file descriptor to which data should be
+/// written.
+///
+/// **iovs**:
+/// List of scatter/gather vectors where data
+/// should be retrieved.
+///
+/// **offset**:
+/// The offset within the file at which writing
+/// should start.
+///
+/// **nwritten**:
+/// The number of bytes written.
+#[inline]
+pub unsafe fn fd_pwrite(fd_: fd, iovs_: &[ciovec], offset_: filesize, nwritten_: &mut usize) -> errno {
+ cloudabi_sys_fd_pwrite(fd_, iovs_.as_ptr(), iovs_.len(), offset_, nwritten_)
+}
+
+/// Reads from a file descriptor.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The file descriptor from which data should be
+/// read.
+///
+/// **iovs**:
+/// List of scatter/gather vectors where data
+/// should be stored.
+///
+/// **nread**:
+/// The number of bytes read.
+#[inline]
+pub unsafe fn fd_read(fd_: fd, iovs_: &[iovec], nread_: &mut usize) -> errno {
+ cloudabi_sys_fd_read(fd_, iovs_.as_ptr(), iovs_.len(), nread_)
+}
+
+/// Atomically replaces a file descriptor by a copy of another
+/// file descriptor.
+///
+/// Due to the strong focus on thread safety, this environment
+/// does not provide a mechanism to duplicate a file descriptor to
+/// an arbitrary number, like dup2(). This would be prone to race
+/// conditions, as an actual file descriptor with the same number
+/// could be allocated by a different thread at the same time.
+///
+/// This system call provides a way to atomically replace file
+/// descriptors, which would disappear if dup2() were to be
+/// removed entirely.
+///
+/// ## Parameters
+///
+/// **from**:
+/// The file descriptor that needs to be copied.
+///
+/// **to**:
+/// The file descriptor that needs to be
+/// overwritten.
+#[inline]
+pub unsafe fn fd_replace(from_: fd, to_: fd) -> errno {
+ cloudabi_sys_fd_replace(from_, to_)
+}
+
+/// Moves the offset of the file descriptor.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The file descriptor whose offset has to be
+/// moved.
+///
+/// **offset**:
+/// The number of bytes to move.
+///
+/// **whence**:
+/// Relative to which position the move should
+/// take place.
+///
+/// **newoffset**:
+/// The new offset of the file descriptor,
+/// relative to the start of the file.
+#[inline]
+pub unsafe fn fd_seek(fd_: fd, offset_: filedelta, whence_: whence, newoffset_: &mut filesize) -> errno {
+ cloudabi_sys_fd_seek(fd_, offset_, whence_, newoffset_)
+}
+
+/// Gets attributes of a file descriptor.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The file descriptor whose attributes have to
+/// be obtained.
+///
+/// **buf**:
+/// The buffer where the file descriptor's
+/// attributes are stored.
+#[inline]
+pub unsafe fn fd_stat_get(fd_: fd, buf_: *mut fdstat) -> errno {
+ cloudabi_sys_fd_stat_get(fd_, buf_)
+}
+
+/// Adjusts attributes of a file descriptor.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The file descriptor whose attributes have to
+/// be adjusted.
+///
+/// **buf**:
+/// The desired values of the file descriptor
+/// attributes that are adjusted.
+///
+/// **flags**:
+/// A bitmask indicating which attributes have to
+/// be adjusted.
+#[inline]
+pub unsafe fn fd_stat_put(fd_: fd, buf_: *const fdstat, flags_: fdsflags) -> errno {
+ cloudabi_sys_fd_stat_put(fd_, buf_, flags_)
+}
+
+/// Synchronizes the data and metadata of a file to disk.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The file descriptor of the file whose data
+/// and metadata needs to be synchronized to disk.
+#[inline]
+pub unsafe fn fd_sync(fd_: fd) -> errno {
+ cloudabi_sys_fd_sync(fd_)
+}
+
+/// Writes to a file descriptor.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The file descriptor to which data should be
+/// written.
+///
+/// **iovs**:
+/// List of scatter/gather vectors where data
+/// should be retrieved.
+///
+/// **nwritten**:
+/// The number of bytes written.
+#[inline]
+pub unsafe fn fd_write(fd_: fd, iovs_: &[ciovec], nwritten_: &mut usize) -> errno {
+ cloudabi_sys_fd_write(fd_, iovs_.as_ptr(), iovs_.len(), nwritten_)
+}
+
+/// Provides file advisory information on a file descriptor.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The file descriptor for which to provide file
+/// advisory information.
+///
+/// **offset**:
+/// The offset within the file to which the
+/// advisory applies.
+///
+/// **len**:
+/// The length of the region to which the advisory
+/// applies.
+///
+/// **advice**:
+/// The advice.
+#[inline]
+pub unsafe fn file_advise(fd_: fd, offset_: filesize, len_: filesize, advice_: advice) -> errno {
+ cloudabi_sys_file_advise(fd_, offset_, len_, advice_)
+}
+
+/// Forces the allocation of space in a file.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The file in which the space should be
+/// allocated.
+///
+/// **offset**:
+/// The offset at which the allocation should
+/// start.
+///
+/// **len**:
+/// The length of the area that is allocated.
+#[inline]
+pub unsafe fn file_allocate(fd_: fd, offset_: filesize, len_: filesize) -> errno {
+ cloudabi_sys_file_allocate(fd_, offset_, len_)
+}
+
+/// Creates a file of a specified type.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The working directory at which the resolution
+/// of the file to be created starts.
+///
+/// **path**:
+/// The path at which the file should be created.
+///
+/// **type**:
+/// Possible values:
+///
+/// - [`DIRECTORY`](enum.filetype.html#variant.DIRECTORY):
+/// Creates a directory.
+#[inline]
+pub unsafe fn file_create(fd_: fd, path_: &[u8], type_: filetype) -> errno {
+ cloudabi_sys_file_create(fd_, path_.as_ptr(), path_.len(), type_)
+}
+
+/// Creates a hard link.
+///
+/// ## Parameters
+///
+/// **fd1**:
+/// The working directory at which the resolution
+/// of the source path starts.
+///
+/// **path1**:
+/// The source path of the file that should be
+/// hard linked.
+///
+/// **fd2**:
+/// The working directory at which the resolution
+/// of the destination path starts.
+///
+/// **path2**:
+/// The destination path at which the hard link
+/// should be created.
+#[inline]
+pub unsafe fn file_link(fd1_: lookup, path1_: &[u8], fd2_: fd, path2_: &[u8]) -> errno {
+ cloudabi_sys_file_link(fd1_, path1_.as_ptr(), path1_.len(), fd2_, path2_.as_ptr(), path2_.len())
+}
+
+/// Opens a file.
+///
+/// ## Parameters
+///
+/// **dirfd**:
+/// The working directory at which the resolution
+/// of the file to be opened starts.
+///
+/// **path**:
+/// The path of the file that should be opened.
+///
+/// **oflags**:
+/// The method at which the file should be opened.
+///
+/// **fds**:
+/// [`fdstat.fs_rights_base`](struct.fdstat.html#structfield.fs_rights_base) and
+/// [`fdstat.fs_rights_inheriting`](struct.fdstat.html#structfield.fs_rights_inheriting) specify the
+/// initial rights of the newly created file
+/// descriptor. The operating system is allowed to
+/// return a file descriptor with fewer rights
+/// than specified, if and only if those rights do
+/// not apply to the type of file being opened.
+///
+/// [`fdstat.fs_flags`](struct.fdstat.html#structfield.fs_flags) specifies the initial flags
+/// of the file descriptor.
+///
+/// [`fdstat.fs_filetype`](struct.fdstat.html#structfield.fs_filetype) is ignored.
+///
+/// **fd**:
+/// The file descriptor of the file that has been
+/// opened.
+#[inline]
+pub unsafe fn file_open(dirfd_: lookup, path_: &[u8], oflags_: oflags, fds_: *const fdstat, fd_: &mut fd) -> errno {
+ cloudabi_sys_file_open(dirfd_, path_.as_ptr(), path_.len(), oflags_, fds_, fd_)
+}
+
+/// Reads directory entries from a directory.
+///
+/// When successful, the contents of the output buffer consist of
+/// a sequence of directory entries. Each directory entry consists
+/// of a [`dirent`](struct.dirent.html) object, followed by [`dirent.d_namlen`](struct.dirent.html#structfield.d_namlen) bytes
+/// holding the name of the directory entry.
+///
+/// This system call fills the output buffer as much as possible,
+/// potentially truncating the last directory entry. This allows
+/// the caller to grow its read buffer size in case it's too small
+/// to fit a single large directory entry, or skip the oversized
+/// directory entry.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The directory from which to read the directory
+/// entries.
+///
+/// **buf**:
+/// The buffer where directory entries are stored.
+///
+/// **cookie**:
+/// The location within the directory to start
+/// reading.
+///
+/// **bufused**:
+/// The number of bytes stored in the read buffer.
+/// If less than the size of the read buffer, the
+/// end of the directory has been reached.
+#[inline]
+pub unsafe fn file_readdir(fd_: fd, buf_: &mut [u8], cookie_: dircookie, bufused_: &mut usize) -> errno {
+ cloudabi_sys_file_readdir(fd_, buf_.as_mut_ptr() as *mut (), buf_.len(), cookie_, bufused_)
+}
+
+/// Reads the contents of a symbolic link.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The working directory at which the resolution
+/// of the path of the symbolic starts.
+///
+/// **path**:
+/// The path of the symbolic link whose contents
+/// should be read.
+///
+/// **buf**:
+/// The buffer where the contents of the symbolic
+/// link should be stored.
+///
+/// **bufused**:
+/// The number of bytes placed in the buffer.
+#[inline]
+pub unsafe fn file_readlink(fd_: fd, path_: &[u8], buf_: &mut [u8], bufused_: &mut usize) -> errno {
+ cloudabi_sys_file_readlink(fd_, path_.as_ptr(), path_.len(), buf_.as_mut_ptr(), buf_.len(), bufused_)
+}
+
+/// Renames a file.
+///
+/// ## Parameters
+///
+/// **fd1**:
+/// The working directory at which the resolution
+/// of the source path starts.
+///
+/// **path1**:
+/// The source path of the file that should be
+/// renamed.
+///
+/// **fd2**:
+/// The working directory at which the resolution
+/// of the destination path starts.
+///
+/// **path2**:
+/// The destination path to which the file should
+/// be renamed.
+#[inline]
+pub unsafe fn file_rename(fd1_: fd, path1_: &[u8], fd2_: fd, path2_: &[u8]) -> errno {
+ cloudabi_sys_file_rename(fd1_, path1_.as_ptr(), path1_.len(), fd2_, path2_.as_ptr(), path2_.len())
+}
+
+/// Gets attributes of a file by file descriptor.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The file descriptor whose attributes have to
+/// be obtained.
+///
+/// **buf**:
+/// The buffer where the file's attributes are
+/// stored.
+#[inline]
+pub unsafe fn file_stat_fget(fd_: fd, buf_: *mut filestat) -> errno {
+ cloudabi_sys_file_stat_fget(fd_, buf_)
+}
+
+/// Adjusts attributes of a file by file descriptor.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The file descriptor whose attributes have to
+/// be adjusted.
+///
+/// **buf**:
+/// The desired values of the file attributes that
+/// are adjusted.
+///
+/// **flags**:
+/// A bitmask indicating which attributes have to
+/// be adjusted.
+#[inline]
+pub unsafe fn file_stat_fput(fd_: fd, buf_: *const filestat, flags_: fsflags) -> errno {
+ cloudabi_sys_file_stat_fput(fd_, buf_, flags_)
+}
+
+/// Gets attributes of a file by path.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The working directory at which the resolution
+/// of the path whose attributes have to be
+/// obtained starts.
+///
+/// **path**:
+/// The path of the file whose attributes have to
+/// be obtained.
+///
+/// **buf**:
+/// The buffer where the file's attributes are
+/// stored.
+#[inline]
+pub unsafe fn file_stat_get(fd_: lookup, path_: &[u8], buf_: *mut filestat) -> errno {
+ cloudabi_sys_file_stat_get(fd_, path_.as_ptr(), path_.len(), buf_)
+}
+
+/// Adjusts attributes of a file by path.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The working directory at which the resolution
+/// of the path whose attributes have to be
+/// adjusted starts.
+///
+/// **path**:
+/// The path of the file whose attributes have to
+/// be adjusted.
+///
+/// **buf**:
+/// The desired values of the file attributes that
+/// are adjusted.
+///
+/// **flags**:
+/// A bitmask indicating which attributes have to
+/// be adjusted.
+#[inline]
+pub unsafe fn file_stat_put(fd_: lookup, path_: &[u8], buf_: *const filestat, flags_: fsflags) -> errno {
+ cloudabi_sys_file_stat_put(fd_, path_.as_ptr(), path_.len(), buf_, flags_)
+}
+
+/// Creates a symbolic link.
+///
+/// ## Parameters
+///
+/// **path1**:
+/// The contents of the symbolic link.
+///
+/// **fd**:
+/// The working directory at which the resolution
+/// of the destination path starts.
+///
+/// **path2**:
+/// The destination path at which the symbolic
+/// link should be created.
+#[inline]
+pub unsafe fn file_symlink(path1_: &[u8], fd_: fd, path2_: &[u8]) -> errno {
+ cloudabi_sys_file_symlink(path1_.as_ptr(), path1_.len(), fd_, path2_.as_ptr(), path2_.len())
+}
+
+/// Unlinks a file, or removes a directory.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// The working directory at which the resolution
+/// of the path starts.
+///
+/// **path**:
+/// The path that needs to be unlinked or removed.
+///
+/// **flags**:
+/// Possible values:
+///
+/// - [`REMOVEDIR`](struct.ulflags.html#associatedconstant.REMOVEDIR):
+/// If set, attempt to remove a directory.
+/// Otherwise, unlink a file.
+#[inline]
+pub unsafe fn file_unlink(fd_: fd, path_: &[u8], flags_: ulflags) -> errno {
+ cloudabi_sys_file_unlink(fd_, path_.as_ptr(), path_.len(), flags_)
+}
+
+/// Unlocks a write-locked userspace lock.
+///
+/// If a userspace lock is unlocked while having its
+/// [`LOCK_KERNEL_MANAGED`](constant.LOCK_KERNEL_MANAGED.html) flag set, the lock cannot be unlocked in
+/// userspace directly. This system call needs to be performed
+/// instead, so that any waiting threads can be woken up.
+///
+/// To prevent spurious invocations of this system call, the lock
+/// must be locked for writing. This prevents other threads from
+/// acquiring additional read locks while the system call is in
+/// progress. If the lock is acquired for reading, it must first
+/// be upgraded to a write lock.
+///
+/// ## Parameters
+///
+/// **lock**:
+/// The userspace lock that is locked for writing
+/// by the calling thread.
+///
+/// **scope**:
+/// Whether the lock is stored in private or
+/// shared memory.
+#[inline]
+pub unsafe fn lock_unlock(lock_: *mut lock, scope_: scope) -> errno {
+ cloudabi_sys_lock_unlock(lock_, scope_)
+}
+
+/// Provides memory advisory information on a region of memory.
+///
+/// ## Parameters
+///
+/// **mapping**:
+/// The pages for which to provide memory advisory
+/// information.
+///
+/// **advice**:
+/// The advice.
+#[inline]
+pub unsafe fn mem_advise(mapping_: &mut [u8], advice_: advice) -> errno {
+ cloudabi_sys_mem_advise(mapping_.as_mut_ptr() as *mut (), mapping_.len(), advice_)
+}
+
+/// Creates a memory mapping, making the contents of a file
+/// accessible through memory.
+///
+/// ## Parameters
+///
+/// **addr**:
+/// If [`FIXED`](struct.mflags.html#associatedconstant.FIXED) is set, specifies to which
+/// address the file region is mapped. Otherwise,
+/// the mapping is performed at an unused
+/// location.
+///
+/// **len**:
+/// The length of the memory mapping to be
+/// created.
+///
+/// **prot**:
+/// Initial memory protection options for the
+/// memory mapping.
+///
+/// **flags**:
+/// Memory mapping flags.
+///
+/// **fd**:
+/// If [`ANON`](struct.mflags.html#associatedconstant.ANON) is set, this argument must be
+/// [`MAP_ANON_FD`](constant.MAP_ANON_FD.html). Otherwise, this argument
+/// specifies the file whose contents need to be
+/// mapped.
+///
+/// **off**:
+/// If [`ANON`](struct.mflags.html#associatedconstant.ANON) is set, this argument must be
+/// zero. Otherwise, this argument specifies the
+/// offset within the file at which the mapping
+/// starts.
+///
+/// **mem**:
+/// The starting address of the memory mapping.
+#[inline]
+pub unsafe fn mem_map(addr_: *mut (), len_: usize, prot_: mprot, flags_: mflags, fd_: fd, off_: filesize, mem_: &mut *mut ()) -> errno {
+ cloudabi_sys_mem_map(addr_, len_, prot_, flags_, fd_, off_, mem_)
+}
+
+/// Change the protection of a memory mapping.
+///
+/// ## Parameters
+///
+/// **mapping**:
+/// The pages that need their protection changed.
+///
+/// **prot**:
+/// New protection options.
+#[inline]
+pub unsafe fn mem_protect(mapping_: &mut [u8], prot_: mprot) -> errno {
+ cloudabi_sys_mem_protect(mapping_.as_mut_ptr() as *mut (), mapping_.len(), prot_)
+}
+
+/// Synchronize a region of memory with its physical storage.
+///
+/// ## Parameters
+///
+/// **mapping**:
+/// The pages that need to be synchronized.
+///
+/// **flags**:
+/// The method of synchronization.
+#[inline]
+pub unsafe fn mem_sync(mapping_: &mut [u8], flags_: msflags) -> errno {
+ cloudabi_sys_mem_sync(mapping_.as_mut_ptr() as *mut (), mapping_.len(), flags_)
+}
+
+/// Unmaps a region of memory.
+///
+/// ## Parameters
+///
+/// **mapping**:
+/// The pages that needs to be unmapped.
+#[inline]
+pub unsafe fn mem_unmap(mapping_: &mut [u8]) -> errno {
+ cloudabi_sys_mem_unmap(mapping_.as_mut_ptr() as *mut (), mapping_.len())
+}
+
+/// Concurrently polls for the occurrence of a set of events.
+///
+/// ## Parameters
+///
+/// **in**:
+/// The events to which to subscribe.
+///
+/// **out**:
+/// The events that have occurred.
+///
+/// **nsubscriptions**:
+/// Both the number of subscriptions and events.
+///
+/// **nevents**:
+/// The number of events stored.
+#[inline]
+pub unsafe fn poll(in_: *const subscription, out_: *mut event, nsubscriptions_: usize, nevents_: &mut usize) -> errno {
+ cloudabi_sys_poll(in_, out_, nsubscriptions_, nevents_)
+}
+
+/// Replaces the process by a new executable.
+///
+/// Process execution in CloudABI differs from POSIX in two ways:
+/// handling of arguments and inheritance of file descriptors.
+///
+/// CloudABI does not use string command line arguments. Instead,
+/// a buffer with binary data is copied into the address space of
+/// the new executable. The kernel does not enforce any specific
+/// structure to this data, although CloudABI's C library uses it
+/// to store a tree structure that is semantically identical to
+/// YAML.
+///
+/// Due to the strong focus on thread safety, file descriptors
+/// aren't inherited through close-on-exec flags. An explicit
+/// list of file descriptors that need to be retained needs to be
+/// provided. After execution, file descriptors are placed in the
+/// order in which they are stored in the array. This not only
+/// makes the execution process deterministic. It also prevents
+/// potential information disclosures about the layout of the
+/// original process.
+///
+/// ## Parameters
+///
+/// **fd**:
+/// A file descriptor of the new executable.
+///
+/// **data**:
+/// Binary argument data that is passed on to the
+/// new executable.
+///
+/// **fds**:
+/// The layout of the file descriptor table after
+/// execution.
+#[inline]
+pub unsafe fn proc_exec(fd_: fd, data_: &[u8], fds_: &[fd]) -> errno {
+ cloudabi_sys_proc_exec(fd_, data_.as_ptr() as *const (), data_.len(), fds_.as_ptr(), fds_.len())
+}
+
+/// Terminates the process normally.
+///
+/// ## Parameters
+///
+/// **rval**:
+/// The exit code returned by the process. The
+/// exit code can be obtained by other processes
+/// through [`event.union.proc_terminate.exitcode`](struct.event_proc_terminate.html#structfield.exitcode).
+#[inline]
+pub unsafe fn proc_exit(rval_: exitcode) -> ! {
+ cloudabi_sys_proc_exit(rval_)
+}
+
+/// Forks the process of the calling thread.
+///
+/// After forking, a new process shall be created, having only a
+/// copy of the calling thread. The parent process will obtain a
+/// process descriptor. When closed, the child process is
+/// automatically signaled with [`KILL`](enum.signal.html#variant.KILL).
+///
+/// ## Parameters
+///
+/// **fd**:
+/// In the parent process: the file descriptor
+/// number of the process descriptor.
+///
+/// In the child process: [`PROCESS_CHILD`](constant.PROCESS_CHILD.html).
+///
+/// **tid**:
+/// In the parent process: undefined.
+///
+/// In the child process: the thread ID of the
+/// initial thread of the child process.
+#[inline]
+pub unsafe fn proc_fork(fd_: &mut fd, tid_: &mut tid) -> errno {
+ cloudabi_sys_proc_fork(fd_, tid_)
+}
+
+/// Sends a signal to the process of the calling thread.
+///
+/// ## Parameters
+///
+/// **sig**:
+/// The signal condition that should be triggered.
+/// If the signal causes the process to terminate,
+/// its condition can be obtained by other
+/// processes through
+/// [`event.union.proc_terminate.signal`](struct.event_proc_terminate.html#structfield.signal).
+#[inline]
+pub unsafe fn proc_raise(sig_: signal) -> errno {
+ cloudabi_sys_proc_raise(sig_)
+}
+
+/// Obtains random data from the kernel random number generator.
+///
+/// As this interface is not guaranteed to be fast, it is advised
+/// that the random data obtained through this system call is used
+/// as the seed for a userspace pseudo-random number generator.
+///
+/// ## Parameters
+///
+/// **buf**:
+/// The buffer that needs to be filled with random
+/// data.
+#[inline]
+pub unsafe fn random_get(buf_: &mut [u8]) -> errno {
+ cloudabi_sys_random_get(buf_.as_mut_ptr() as *mut (), buf_.len())
+}
+
+/// Receives a message on a socket.
+///
+/// ## Parameters
+///
+/// **sock**:
+/// The socket on which a message should be
+/// received.
+///
+/// **in**:
+/// Input parameters.
+///
+/// **out**:
+/// Output parameters.
+#[inline]
+pub unsafe fn sock_recv(sock_: fd, in_: *const recv_in, out_: *mut recv_out) -> errno {
+ cloudabi_sys_sock_recv(sock_, in_, out_)
+}
+
+/// Sends a message on a socket.
+///
+/// ## Parameters
+///
+/// **sock**:
+/// The socket on which a message should be sent.
+///
+/// **in**:
+/// Input parameters.
+///
+/// **out**:
+/// Output parameters.
+#[inline]
+pub unsafe fn sock_send(sock_: fd, in_: *const send_in, out_: *mut send_out) -> errno {
+ cloudabi_sys_sock_send(sock_, in_, out_)
+}
+
+/// Shuts down socket send and receive channels.
+///
+/// ## Parameters
+///
+/// **sock**:
+/// The socket that needs its channels shut down.
+///
+/// **how**:
+/// Which channels on the socket need to be shut
+/// down.
+#[inline]
+pub unsafe fn sock_shutdown(sock_: fd, how_: sdflags) -> errno {
+ cloudabi_sys_sock_shutdown(sock_, how_)
+}
+
+/// Creates a new thread within the current process.
+///
+/// ## Parameters
+///
+/// **attr**:
+/// The desired attributes of the new thread.
+///
+/// **tid**:
+/// The thread ID of the new thread.
+#[inline]
+pub unsafe fn thread_create(attr_: *mut threadattr, tid_: &mut tid) -> errno {
+ cloudabi_sys_thread_create(attr_, tid_)
+}
+
+/// Terminates the calling thread.
+///
+/// This system call can also unlock a single userspace lock
+/// after termination, which can be used to implement thread
+/// joining.
+///
+/// ## Parameters
+///
+/// **lock**:
+/// Userspace lock that is locked for writing by
+/// the calling thread.
+///
+/// **scope**:
+/// Whether the lock is stored in private or
+/// shared memory.
+#[inline]
+pub unsafe fn thread_exit(lock_: *mut lock, scope_: scope) -> ! {
+ cloudabi_sys_thread_exit(lock_, scope_)
+}
+
+/// Temporarily yields execution of the calling thread.
+#[inline]
+pub unsafe fn thread_yield() -> errno {
+ cloudabi_sys_thread_yield()
+}
diff --git a/ctr-std/src/sys/cloudabi/abi/mod.rs b/ctr-std/src/sys/cloudabi/abi/mod.rs
new file mode 100644
index 0000000..81a4d29
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/abi/mod.rs
@@ -0,0 +1,13 @@
+// Copyright 2018 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(warnings)]
+mod cloudabi;
+pub use self::cloudabi::*;
diff --git a/ctr-std/src/sys/cloudabi/args.rs b/ctr-std/src/sys/cloudabi/args.rs
new file mode 100644
index 0000000..7b62cc6
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/args.rs
@@ -0,0 +1,17 @@
+// Copyright 2018 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 use sys::cloudabi::shims::args::*;
+
+#[allow(dead_code)]
+pub fn init(_: isize, _: *const *const u8) {}
+
+#[allow(dead_code)]
+pub fn cleanup() {}
diff --git a/ctr-std/src/sys/cloudabi/backtrace.rs b/ctr-std/src/sys/cloudabi/backtrace.rs
new file mode 100644
index 0000000..1b97018
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/backtrace.rs
@@ -0,0 +1,122 @@
+// Copyright 2014-2018 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 error::Error;
+use ffi::CStr;
+use intrinsics;
+use io;
+use libc;
+use sys_common::backtrace::Frame;
+use unwind as uw;
+
+pub struct BacktraceContext;
+
+struct Context<'a> {
+ idx: usize,
+ frames: &'a mut [Frame],
+}
+
+#[derive(Debug)]
+struct UnwindError(uw::_Unwind_Reason_Code);
+
+impl Error for UnwindError {
+ fn description(&self) -> &'static str {
+ "unexpected return value while unwinding"
+ }
+}
+
+impl ::fmt::Display for UnwindError {
+ fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
+ write!(f, "{}: {:?}", self.description(), self.0)
+ }
+}
+
+#[inline(never)] // if we know this is a function call, we can skip it when
+ // tracing
+pub fn unwind_backtrace(frames: &mut [Frame]) -> io::Result<(usize, BacktraceContext)> {
+ let mut cx = Context { idx: 0, frames };
+ let result_unwind =
+ unsafe { uw::_Unwind_Backtrace(trace_fn, &mut cx as *mut Context as *mut libc::c_void) };
+ // See libunwind:src/unwind/Backtrace.c for the return values.
+ // No, there is no doc.
+ match result_unwind {
+ // These return codes seem to be benign and need to be ignored for backtraces
+ // to show up properly on all tested platforms.
+ uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => {
+ Ok((cx.idx, BacktraceContext))
+ }
+ _ => Err(io::Error::new(
+ io::ErrorKind::Other,
+ UnwindError(result_unwind),
+ )),
+ }
+}
+
+extern "C" fn trace_fn(
+ ctx: *mut uw::_Unwind_Context,
+ arg: *mut libc::c_void,
+) -> uw::_Unwind_Reason_Code {
+ let cx = unsafe { &mut *(arg as *mut Context) };
+ let mut ip_before_insn = 0;
+ let mut ip = unsafe { uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void };
+ if !ip.is_null() && ip_before_insn == 0 {
+ // this is a non-signaling frame, so `ip` refers to the address
+ // after the calling instruction. account for that.
+ ip = (ip as usize - 1) as *mut _;
+ }
+
+ let symaddr = unsafe { uw::_Unwind_FindEnclosingFunction(ip) };
+ if cx.idx < cx.frames.len() {
+ cx.frames[cx.idx] = Frame {
+ symbol_addr: symaddr as *mut u8,
+ exact_position: ip as *mut u8,
+ inline_context: 0,
+ };
+ cx.idx += 1;
+ }
+
+ uw::_URC_NO_REASON
+}
+
+pub fn foreach_symbol_fileline<F>(_: Frame, _: F, _: &BacktraceContext) -> io::Result<bool>
+where
+ F: FnMut(&[u8], u32) -> io::Result<()>,
+{
+ // No way to obtain this information on CloudABI.
+ Ok(false)
+}
+
+pub fn resolve_symname<F>(frame: Frame, callback: F, _: &BacktraceContext) -> io::Result<()>
+where
+ F: FnOnce(Option<&str>) -> io::Result<()>,
+{
+ unsafe {
+ let mut info: Dl_info = intrinsics::init();
+ let symname =
+ if dladdr(frame.exact_position as *mut _, &mut info) == 0 || info.dli_sname.is_null() {
+ None
+ } else {
+ CStr::from_ptr(info.dli_sname).to_str().ok()
+ };
+ callback(symname)
+ }
+}
+
+#[repr(C)]
+struct Dl_info {
+ dli_fname: *const libc::c_char,
+ dli_fbase: *mut libc::c_void,
+ dli_sname: *const libc::c_char,
+ dli_saddr: *mut libc::c_void,
+}
+
+extern "C" {
+ fn dladdr(addr: *const libc::c_void, info: *mut Dl_info) -> libc::c_int;
+}
diff --git a/ctr-std/src/sys/cloudabi/condvar.rs b/ctr-std/src/sys/cloudabi/condvar.rs
new file mode 100644
index 0000000..c05c837
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/condvar.rs
@@ -0,0 +1,169 @@
+// Copyright 2018 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 mem;
+use sync::atomic::{AtomicU32, Ordering};
+use sys::cloudabi::abi;
+use sys::mutex::{self, Mutex};
+use sys::time::dur2intervals;
+use time::Duration;
+
+extern "C" {
+ #[thread_local]
+ static __pthread_thread_id: abi::tid;
+}
+
+pub struct Condvar {
+ condvar: UnsafeCell<AtomicU32>,
+}
+
+unsafe impl Send for Condvar {}
+unsafe impl Sync for Condvar {}
+
+impl Condvar {
+ pub const fn new() -> Condvar {
+ Condvar {
+ condvar: UnsafeCell::new(AtomicU32::new(abi::CONDVAR_HAS_NO_WAITERS.0)),
+ }
+ }
+
+ pub unsafe fn init(&mut self) {}
+
+ pub unsafe fn notify_one(&self) {
+ let condvar = self.condvar.get();
+ if (*condvar).load(Ordering::Relaxed) != abi::CONDVAR_HAS_NO_WAITERS.0 {
+ let ret = abi::condvar_signal(condvar as *mut abi::condvar, abi::scope::PRIVATE, 1);
+ assert_eq!(
+ ret,
+ abi::errno::SUCCESS,
+ "Failed to signal on condition variable"
+ );
+ }
+ }
+
+ pub unsafe fn notify_all(&self) {
+ let condvar = self.condvar.get();
+ if (*condvar).load(Ordering::Relaxed) != abi::CONDVAR_HAS_NO_WAITERS.0 {
+ let ret = abi::condvar_signal(
+ condvar as *mut abi::condvar,
+ abi::scope::PRIVATE,
+ abi::nthreads::max_value(),
+ );
+ assert_eq!(
+ ret,
+ abi::errno::SUCCESS,
+ "Failed to broadcast on condition variable"
+ );
+ }
+ }
+
+ pub unsafe fn wait(&self, mutex: &Mutex) {
+ let mutex = mutex::raw(mutex);
+ assert_eq!(
+ (*mutex).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
+ "This lock is not write-locked by this thread"
+ );
+
+ // Call into the kernel to wait on the condition variable.
+ let condvar = self.condvar.get();
+ let subscription = abi::subscription {
+ type_: abi::eventtype::CONDVAR,
+ union: abi::subscription_union {
+ condvar: abi::subscription_condvar {
+ condvar: condvar as *mut abi::condvar,
+ condvar_scope: abi::scope::PRIVATE,
+ lock: mutex as *mut abi::lock,
+ lock_scope: abi::scope::PRIVATE,
+ },
+ },
+ ..mem::zeroed()
+ };
+ let mut event: abi::event = mem::uninitialized();
+ let mut nevents: usize = mem::uninitialized();
+ let ret = abi::poll(&subscription, &mut event, 1, &mut nevents);
+ assert_eq!(
+ ret,
+ abi::errno::SUCCESS,
+ "Failed to wait on condition variable"
+ );
+ assert_eq!(
+ event.error,
+ abi::errno::SUCCESS,
+ "Failed to wait on condition variable"
+ );
+ }
+
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+ let mutex = mutex::raw(mutex);
+ assert_eq!(
+ (*mutex).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
+ "This lock is not write-locked by this thread"
+ );
+
+ // Call into the kernel to wait on the condition variable.
+ let condvar = self.condvar.get();
+ let subscriptions = [
+ abi::subscription {
+ type_: abi::eventtype::CONDVAR,
+ union: abi::subscription_union {
+ condvar: abi::subscription_condvar {
+ condvar: condvar as *mut abi::condvar,
+ condvar_scope: abi::scope::PRIVATE,
+ lock: mutex as *mut abi::lock,
+ lock_scope: abi::scope::PRIVATE,
+ },
+ },
+ ..mem::zeroed()
+ },
+ abi::subscription {
+ type_: abi::eventtype::CLOCK,
+ union: abi::subscription_union {
+ clock: abi::subscription_clock {
+ clock_id: abi::clockid::MONOTONIC,
+ timeout: dur2intervals(&dur),
+ ..mem::zeroed()
+ },
+ },
+ ..mem::zeroed()
+ },
+ ];
+ let mut events: [abi::event; 2] = mem::uninitialized();
+ let mut nevents: usize = mem::uninitialized();
+ let ret = abi::poll(subscriptions.as_ptr(), events.as_mut_ptr(), 2, &mut nevents);
+ assert_eq!(
+ ret,
+ abi::errno::SUCCESS,
+ "Failed to wait on condition variable"
+ );
+ for i in 0..nevents {
+ assert_eq!(
+ events[i].error,
+ abi::errno::SUCCESS,
+ "Failed to wait on condition variable"
+ );
+ if events[i].type_ == abi::eventtype::CONDVAR {
+ return true;
+ }
+ }
+ false
+ }
+
+ pub unsafe fn destroy(&self) {
+ let condvar = self.condvar.get();
+ assert_eq!(
+ (*condvar).load(Ordering::Relaxed),
+ abi::CONDVAR_HAS_NO_WAITERS.0,
+ "Attempted to destroy a condition variable with blocked threads"
+ );
+ }
+}
diff --git a/ctr-std/src/sys/cloudabi/mod.rs b/ctr-std/src/sys/cloudabi/mod.rs
new file mode 100644
index 0000000..9e943c1
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/mod.rs
@@ -0,0 +1,76 @@
+// Copyright 2018 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 libc;
+use mem;
+
+pub mod args;
+#[cfg(feature = "backtrace")]
+pub mod backtrace;
+#[path = "../unix/cmath.rs"]
+pub mod cmath;
+pub mod condvar;
+#[path = "../unix/memchr.rs"]
+pub mod memchr;
+pub mod mutex;
+pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
+pub mod rwlock;
+pub mod stack_overflow;
+pub mod stdio;
+pub mod thread;
+#[path = "../unix/thread_local.rs"]
+pub mod thread_local;
+pub mod time;
+
+mod abi;
+
+mod shims;
+pub use self::shims::*;
+
+#[allow(dead_code)]
+pub fn init() {}
+
+pub fn decode_error_kind(errno: i32) -> io::ErrorKind {
+ match errno {
+ x if x == abi::errno::ACCES as i32 => io::ErrorKind::PermissionDenied,
+ x if x == abi::errno::ADDRINUSE as i32 => io::ErrorKind::AddrInUse,
+ x if x == abi::errno::ADDRNOTAVAIL as i32 => io::ErrorKind::AddrNotAvailable,
+ x if x == abi::errno::AGAIN as i32 => io::ErrorKind::WouldBlock,
+ x if x == abi::errno::CONNABORTED as i32 => io::ErrorKind::ConnectionAborted,
+ x if x == abi::errno::CONNREFUSED as i32 => io::ErrorKind::ConnectionRefused,
+ x if x == abi::errno::CONNRESET as i32 => io::ErrorKind::ConnectionReset,
+ x if x == abi::errno::EXIST as i32 => io::ErrorKind::AlreadyExists,
+ x if x == abi::errno::INTR as i32 => io::ErrorKind::Interrupted,
+ x if x == abi::errno::INVAL as i32 => io::ErrorKind::InvalidInput,
+ x if x == abi::errno::NOENT as i32 => io::ErrorKind::NotFound,
+ x if x == abi::errno::NOTCONN as i32 => io::ErrorKind::NotConnected,
+ x if x == abi::errno::PERM as i32 => io::ErrorKind::PermissionDenied,
+ x if x == abi::errno::PIPE as i32 => io::ErrorKind::BrokenPipe,
+ x if x == abi::errno::TIMEDOUT as i32 => io::ErrorKind::TimedOut,
+ _ => io::ErrorKind::Other,
+ }
+}
+
+pub unsafe fn abort_internal() -> ! {
+ ::core::intrinsics::abort();
+}
+
+pub use libc::strlen;
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+ unsafe {
+ let mut v = mem::uninitialized();
+ libc::arc4random_buf(&mut v as *mut _ as *mut libc::c_void, mem::size_of_val(&v));
+ v
+ }
+}
diff --git a/ctr-std/src/sys/cloudabi/mutex.rs b/ctr-std/src/sys/cloudabi/mutex.rs
new file mode 100644
index 0000000..d4ba6bc
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/mutex.rs
@@ -0,0 +1,158 @@
+// Copyright 2018 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 mem;
+use sync::atomic::{AtomicU32, Ordering};
+use sys::cloudabi::abi;
+use sys::rwlock::{self, RWLock};
+
+extern "C" {
+ #[thread_local]
+ static __pthread_thread_id: abi::tid;
+}
+
+// Implement Mutex using an RWLock. This doesn't introduce any
+// performance overhead in this environment, as the operations would be
+// implemented identically.
+pub struct Mutex(RWLock);
+
+pub unsafe fn raw(m: &Mutex) -> *mut AtomicU32 {
+ rwlock::raw(&m.0)
+}
+
+impl Mutex {
+ pub const fn new() -> Mutex {
+ Mutex(RWLock::new())
+ }
+
+ pub unsafe fn init(&mut self) {
+ // This function should normally reinitialize the mutex after
+ // moving it to a different memory address. This implementation
+ // does not require adjustments after moving.
+ }
+
+ pub unsafe fn try_lock(&self) -> bool {
+ self.0.try_write()
+ }
+
+ pub unsafe fn lock(&self) {
+ self.0.write()
+ }
+
+ pub unsafe fn unlock(&self) {
+ self.0.write_unlock()
+ }
+
+ pub unsafe fn destroy(&self) {
+ self.0.destroy()
+ }
+}
+
+pub struct ReentrantMutex {
+ lock: UnsafeCell<AtomicU32>,
+ recursion: UnsafeCell<u32>,
+}
+
+impl ReentrantMutex {
+ pub unsafe fn uninitialized() -> ReentrantMutex {
+ mem::uninitialized()
+ }
+
+ pub unsafe fn init(&mut self) {
+ self.lock = UnsafeCell::new(AtomicU32::new(abi::LOCK_UNLOCKED.0));
+ self.recursion = UnsafeCell::new(0);
+ }
+
+ pub unsafe fn try_lock(&self) -> bool {
+ // Attempt to acquire the lock.
+ let lock = self.lock.get();
+ let recursion = self.recursion.get();
+ if let Err(old) = (*lock).compare_exchange(
+ abi::LOCK_UNLOCKED.0,
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
+ Ordering::Acquire,
+ Ordering::Relaxed,
+ ) {
+ // If we fail to acquire the lock, it may be the case
+ // that we've already acquired it and may need to recurse.
+ if old & !abi::LOCK_KERNEL_MANAGED.0 == __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0 {
+ *recursion += 1;
+ true
+ } else {
+ false
+ }
+ } else {
+ // Success.
+ assert_eq!(*recursion, 0, "Mutex has invalid recursion count");
+ true
+ }
+ }
+
+ pub unsafe fn lock(&self) {
+ if !self.try_lock() {
+ // Call into the kernel to acquire a write lock.
+ let lock = self.lock.get();
+ let subscription = abi::subscription {
+ type_: abi::eventtype::LOCK_WRLOCK,
+ union: abi::subscription_union {
+ lock: abi::subscription_lock {
+ lock: lock as *mut abi::lock,
+ lock_scope: abi::scope::PRIVATE,
+ },
+ },
+ ..mem::zeroed()
+ };
+ let mut event: abi::event = mem::uninitialized();
+ let mut nevents: usize = mem::uninitialized();
+ let ret = abi::poll(&subscription, &mut event, 1, &mut nevents);
+ assert_eq!(ret, abi::errno::SUCCESS, "Failed to acquire mutex");
+ assert_eq!(event.error, abi::errno::SUCCESS, "Failed to acquire mutex");
+ }
+ }
+
+ pub unsafe fn unlock(&self) {
+ let lock = self.lock.get();
+ let recursion = self.recursion.get();
+ assert_eq!(
+ (*lock).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
+ "This mutex is locked by a different thread"
+ );
+
+ if *recursion > 0 {
+ *recursion -= 1;
+ } else if !(*lock)
+ .compare_exchange(
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
+ abi::LOCK_UNLOCKED.0,
+ Ordering::Release,
+ Ordering::Relaxed,
+ )
+ .is_ok()
+ {
+ // Lock is managed by kernelspace. Call into the kernel
+ // to unblock waiting threads.
+ let ret = abi::lock_unlock(lock as *mut abi::lock, abi::scope::PRIVATE);
+ assert_eq!(ret, abi::errno::SUCCESS, "Failed to unlock a mutex");
+ }
+ }
+
+ pub unsafe fn destroy(&self) {
+ let lock = self.lock.get();
+ let recursion = self.recursion.get();
+ assert_eq!(
+ (*lock).load(Ordering::Relaxed),
+ abi::LOCK_UNLOCKED.0,
+ "Attempted to destroy locked mutex"
+ );
+ assert_eq!(*recursion, 0, "Recursion counter invalid");
+ }
+}
diff --git a/ctr-std/src/sys/cloudabi/os.rs b/ctr-std/src/sys/cloudabi/os.rs
new file mode 100644
index 0000000..7e506b8
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/os.rs
@@ -0,0 +1,37 @@
+// Copyright 2018 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 libc::{self, c_int};
+use str;
+
+pub use sys::cloudabi::shims::os::*;
+
+pub fn errno() -> i32 {
+ extern "C" {
+ #[thread_local]
+ static errno: c_int;
+ }
+
+ unsafe { errno as i32 }
+}
+
+/// Gets a detailed string description for the given error number.
+pub fn error_string(errno: i32) -> String {
+ // cloudlibc's strerror() is guaranteed to be thread-safe. There is
+ // thus no need to use strerror_r().
+ str::from_utf8(unsafe { CStr::from_ptr(libc::strerror(errno)) }.to_bytes())
+ .unwrap()
+ .to_owned()
+}
+
+pub fn exit(code: i32) -> ! {
+ unsafe { libc::exit(code as c_int) }
+}
diff --git a/ctr-std/src/sys/cloudabi/rwlock.rs b/ctr-std/src/sys/cloudabi/rwlock.rs
new file mode 100644
index 0000000..8539aec
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/rwlock.rs
@@ -0,0 +1,237 @@
+// Copyright 2018 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 mem;
+use sync::atomic::{AtomicU32, Ordering};
+use sys::cloudabi::abi;
+
+extern "C" {
+ #[thread_local]
+ static __pthread_thread_id: abi::tid;
+}
+
+#[thread_local]
+static mut RDLOCKS_ACQUIRED: u32 = 0;
+
+pub struct RWLock {
+ lock: UnsafeCell<AtomicU32>,
+}
+
+pub unsafe fn raw(r: &RWLock) -> *mut AtomicU32 {
+ r.lock.get()
+}
+
+unsafe impl Send for RWLock {}
+unsafe impl Sync for RWLock {}
+
+impl RWLock {
+ pub const fn new() -> RWLock {
+ RWLock {
+ lock: UnsafeCell::new(AtomicU32::new(abi::LOCK_UNLOCKED.0)),
+ }
+ }
+
+ pub unsafe fn try_read(&self) -> bool {
+ let lock = self.lock.get();
+ let mut old = abi::LOCK_UNLOCKED.0;
+ while let Err(cur) =
+ (*lock).compare_exchange_weak(old, old + 1, Ordering::Acquire, Ordering::Relaxed)
+ {
+ if (cur & abi::LOCK_WRLOCKED.0) != 0 {
+ // Another thread already has a write lock.
+ assert_ne!(
+ old & !abi::LOCK_KERNEL_MANAGED.0,
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
+ "Attempted to acquire a read lock while holding a write lock"
+ );
+ return false;
+ } else if (old & abi::LOCK_KERNEL_MANAGED.0) != 0 && RDLOCKS_ACQUIRED == 0 {
+ // Lock has threads waiting for the lock. Only acquire
+ // the lock if we have already acquired read locks. In
+ // that case, it is justified to acquire this lock to
+ // prevent a deadlock.
+ return false;
+ }
+ old = cur;
+ }
+
+ RDLOCKS_ACQUIRED += 1;
+ true
+ }
+
+ pub unsafe fn read(&self) {
+ if !self.try_read() {
+ // Call into the kernel to acquire a read lock.
+ let lock = self.lock.get();
+ let subscription = abi::subscription {
+ type_: abi::eventtype::LOCK_RDLOCK,
+ union: abi::subscription_union {
+ lock: abi::subscription_lock {
+ lock: lock as *mut abi::lock,
+ lock_scope: abi::scope::PRIVATE,
+ },
+ },
+ ..mem::zeroed()
+ };
+ let mut event: abi::event = mem::uninitialized();
+ let mut nevents: usize = mem::uninitialized();
+ let ret = abi::poll(&subscription, &mut event, 1, &mut nevents);
+ assert_eq!(ret, abi::errno::SUCCESS, "Failed to acquire read lock");
+ assert_eq!(
+ event.error,
+ abi::errno::SUCCESS,
+ "Failed to acquire read lock"
+ );
+
+ RDLOCKS_ACQUIRED += 1;
+ }
+ }
+
+ pub unsafe fn read_unlock(&self) {
+ // Perform a read unlock. We can do this in userspace, except when
+ // other threads are blocked and we are performing the last unlock.
+ // In that case, call into the kernel.
+ //
+ // Other threads may attempt to increment the read lock count,
+ // meaning that the call into the kernel could be spurious. To
+ // prevent this from happening, upgrade to a write lock first. This
+ // allows us to call into the kernel, having the guarantee that the
+ // lock value will not change in the meantime.
+ assert!(RDLOCKS_ACQUIRED > 0, "Bad lock count");
+ let mut old = 1;
+ loop {
+ let lock = self.lock.get();
+ if old == 1 | abi::LOCK_KERNEL_MANAGED.0 {
+ // Last read lock while threads are waiting. Attempt to upgrade
+ // to a write lock before calling into the kernel to unlock.
+ if let Err(cur) = (*lock).compare_exchange_weak(
+ old,
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0 | abi::LOCK_KERNEL_MANAGED.0,
+ Ordering::Acquire,
+ Ordering::Relaxed,
+ ) {
+ old = cur;
+ } else {
+ // Call into the kernel to unlock.
+ let ret = abi::lock_unlock(lock as *mut abi::lock, abi::scope::PRIVATE);
+ assert_eq!(ret, abi::errno::SUCCESS, "Failed to write unlock a rwlock");
+ break;
+ }
+ } else {
+ // No threads waiting or not the last read lock. Just decrement
+ // the read lock count.
+ assert_ne!(
+ old & !abi::LOCK_KERNEL_MANAGED.0,
+ 0,
+ "This rwlock is not locked"
+ );
+ assert_eq!(
+ old & abi::LOCK_WRLOCKED.0,
+ 0,
+ "Attempted to read-unlock a write-locked rwlock"
+ );
+ if let Err(cur) = (*lock).compare_exchange_weak(
+ old,
+ old - 1,
+ Ordering::Acquire,
+ Ordering::Relaxed,
+ ) {
+ old = cur;
+ } else {
+ break;
+ }
+ }
+ }
+
+ RDLOCKS_ACQUIRED -= 1;
+ }
+
+ pub unsafe fn try_write(&self) -> bool {
+ // Attempt to acquire the lock.
+ let lock = self.lock.get();
+ if let Err(old) = (*lock).compare_exchange(
+ abi::LOCK_UNLOCKED.0,
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
+ Ordering::Acquire,
+ Ordering::Relaxed,
+ ) {
+ // Failure. Crash upon recursive acquisition.
+ assert_ne!(
+ old & !abi::LOCK_KERNEL_MANAGED.0,
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
+ "Attempted to recursive write-lock a rwlock",
+ );
+ false
+ } else {
+ // Success.
+ true
+ }
+ }
+
+ pub unsafe fn write(&self) {
+ if !self.try_write() {
+ // Call into the kernel to acquire a write lock.
+ let lock = self.lock.get();
+ let subscription = abi::subscription {
+ type_: abi::eventtype::LOCK_WRLOCK,
+ union: abi::subscription_union {
+ lock: abi::subscription_lock {
+ lock: lock as *mut abi::lock,
+ lock_scope: abi::scope::PRIVATE,
+ },
+ },
+ ..mem::zeroed()
+ };
+ let mut event: abi::event = mem::uninitialized();
+ let mut nevents: usize = mem::uninitialized();
+ let ret = abi::poll(&subscription, &mut event, 1, &mut nevents);
+ assert_eq!(ret, abi::errno::SUCCESS, "Failed to acquire write lock");
+ assert_eq!(
+ event.error,
+ abi::errno::SUCCESS,
+ "Failed to acquire write lock"
+ );
+ }
+ }
+
+ pub unsafe fn write_unlock(&self) {
+ let lock = self.lock.get();
+ assert_eq!(
+ (*lock).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
+ "This rwlock is not write-locked by this thread"
+ );
+
+ if !(*lock)
+ .compare_exchange(
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
+ abi::LOCK_UNLOCKED.0,
+ Ordering::Release,
+ Ordering::Relaxed,
+ )
+ .is_ok()
+ {
+ // Lock is managed by kernelspace. Call into the kernel
+ // to unblock waiting threads.
+ let ret = abi::lock_unlock(lock as *mut abi::lock, abi::scope::PRIVATE);
+ assert_eq!(ret, abi::errno::SUCCESS, "Failed to write unlock a rwlock");
+ }
+ }
+
+ pub unsafe fn destroy(&self) {
+ let lock = self.lock.get();
+ assert_eq!(
+ (*lock).load(Ordering::Relaxed),
+ abi::LOCK_UNLOCKED.0,
+ "Attempted to destroy locked rwlock"
+ );
+ }
+}
diff --git a/ctr-std/src/sys/cloudabi/shims/args.rs b/ctr-std/src/sys/cloudabi/shims/args.rs
new file mode 100644
index 0000000..1b5785a
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/shims/args.rs
@@ -0,0 +1,45 @@
+// Copyright 2018 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::OsString;
+
+pub struct Args(());
+
+impl Args {
+ pub fn inner_debug(&self) -> &[OsString] {
+ &[]
+ }
+}
+
+impl Iterator for Args {
+ type Item = OsString;
+ fn next(&mut self) -> Option<OsString> {
+ None
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ (0, Some(0))
+ }
+}
+
+impl ExactSizeIterator for Args {
+ fn len(&self) -> usize {
+ 0
+ }
+}
+
+impl DoubleEndedIterator for Args {
+ fn next_back(&mut self) -> Option<OsString> {
+ None
+ }
+}
+
+pub fn args() -> Args {
+ Args(())
+}
diff --git a/ctr-std/src/sys/cloudabi/shims/env.rs b/ctr-std/src/sys/cloudabi/shims/env.rs
new file mode 100644
index 0000000..31777aa
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/shims/env.rs
@@ -0,0 +1,19 @@
+// Copyright 2018 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 = "cloudabi";
+ pub const OS: &'static str = "cloudabi";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
diff --git a/ctr-std/src/sys/cloudabi/shims/fs.rs b/ctr-std/src/sys/cloudabi/shims/fs.rs
new file mode 100644
index 0000000..d3da0fb
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/shims/fs.rs
@@ -0,0 +1,302 @@
+// 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.
+
+use ffi::OsString;
+use fmt;
+use hash::{Hash, Hasher};
+use io::{self, SeekFrom};
+use path::{Path, PathBuf};
+use sys::time::SystemTime;
+use sys::{unsupported, Void};
+
+pub struct File(Void);
+
+pub struct FileAttr(Void);
+
+pub struct ReadDir(Void);
+
+pub struct DirEntry(Void);
+
+#[derive(Clone, Debug)]
+pub struct OpenOptions {}
+
+pub struct FilePermissions(Void);
+
+pub struct FileType(Void);
+
+#[derive(Debug)]
+pub struct DirBuilder {}
+
+impl FileAttr {
+ pub fn size(&self) -> u64 {
+ match self.0 {}
+ }
+
+ pub fn perm(&self) -> FilePermissions {
+ match self.0 {}
+ }
+
+ pub fn file_type(&self) -> FileType {
+ match self.0 {}
+ }
+
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ match self.0 {}
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ match self.0 {}
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ match self.0 {}
+ }
+}
+
+impl Clone for FileAttr {
+ fn clone(&self) -> FileAttr {
+ match self.0 {}
+ }
+}
+
+impl FilePermissions {
+ pub fn readonly(&self) -> bool {
+ match self.0 {}
+ }
+
+ pub fn set_readonly(&mut self, _readonly: bool) {
+ match self.0 {}
+ }
+}
+
+impl Clone for FilePermissions {
+ fn clone(&self) -> FilePermissions {
+ match self.0 {}
+ }
+}
+
+impl PartialEq for FilePermissions {
+ fn eq(&self, _other: &FilePermissions) -> bool {
+ match self.0 {}
+ }
+}
+
+impl Eq for FilePermissions {}
+
+impl fmt::Debug for FilePermissions {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+impl FileType {
+ pub fn is_dir(&self) -> bool {
+ match self.0 {}
+ }
+
+ pub fn is_file(&self) -> bool {
+ match self.0 {}
+ }
+
+ pub fn is_symlink(&self) -> bool {
+ match self.0 {}
+ }
+}
+
+impl Clone for FileType {
+ fn clone(&self) -> FileType {
+ match self.0 {}
+ }
+}
+
+impl Copy for FileType {}
+
+impl PartialEq for FileType {
+ fn eq(&self, _other: &FileType) -> bool {
+ match self.0 {}
+ }
+}
+
+impl Eq for FileType {}
+
+impl Hash for FileType {
+ fn hash<H: Hasher>(&self, _h: &mut H) {
+ match self.0 {}
+ }
+}
+
+impl fmt::Debug for FileType {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+impl fmt::Debug for ReadDir {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+impl Iterator for ReadDir {
+ type Item = io::Result<DirEntry>;
+
+ fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ match self.0 {}
+ }
+}
+
+impl DirEntry {
+ pub fn path(&self) -> PathBuf {
+ match self.0 {}
+ }
+
+ pub fn file_name(&self) -> OsString {
+ match self.0 {}
+ }
+
+ pub fn metadata(&self) -> io::Result<FileAttr> {
+ match self.0 {}
+ }
+
+ pub fn file_type(&self) -> io::Result<FileType> {
+ match self.0 {}
+ }
+}
+
+impl OpenOptions {
+ pub fn new() -> OpenOptions {
+ OpenOptions {}
+ }
+
+ pub fn read(&mut self, _read: bool) {}
+ pub fn write(&mut self, _write: bool) {}
+ pub fn append(&mut self, _append: bool) {}
+ pub fn truncate(&mut self, _truncate: bool) {}
+ pub fn create(&mut self, _create: bool) {}
+ pub fn create_new(&mut self, _create_new: bool) {}
+}
+
+impl File {
+ pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result<File> {
+ unsupported()
+ }
+
+ pub fn file_attr(&self) -> io::Result<FileAttr> {
+ match self.0 {}
+ }
+
+ pub fn fsync(&self) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn datasync(&self) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn truncate(&self, _size: u64) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
+ match self.0 {}
+ }
+
+ pub fn duplicate(&self) -> io::Result<File> {
+ match self.0 {}
+ }
+
+ pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn diverge(&self) -> ! {
+ match self.0 {}
+ }
+}
+
+impl DirBuilder {
+ pub fn new() -> DirBuilder {
+ DirBuilder {}
+ }
+
+ pub fn mkdir(&self, _p: &Path) -> io::Result<()> {
+ unsupported()
+ }
+}
+
+impl fmt::Debug for File {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
+ unsupported()
+}
+
+pub fn unlink(_p: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
+ match perm.0 {}
+}
+
+pub fn rmdir(_p: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn symlink(_src: &Path, _dst: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn stat(_p: &Path) -> io::Result<FileAttr> {
+ unsupported()
+}
+
+pub fn lstat(_p: &Path) -> io::Result<FileAttr> {
+ unsupported()
+}
+
+pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn copy(_from: &Path, _to: &Path) -> io::Result<u64> {
+ unsupported()
+}
diff --git a/ctr-std/src/sys/cloudabi/shims/mod.rs b/ctr-std/src/sys/cloudabi/shims/mod.rs
new file mode 100644
index 0000000..407c2b9
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/shims/mod.rs
@@ -0,0 +1,32 @@
+// Copyright 2018 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;
+
+pub mod args;
+pub mod env;
+pub mod fs;
+pub mod net;
+#[path = "../../unix/path.rs"]
+pub mod path;
+pub mod pipe;
+pub mod process;
+pub mod os;
+
+// This enum is used as the storage for a bunch of types which can't actually exist.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub enum Void {}
+
+pub fn unsupported<T>() -> io::Result<T> {
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ "This function is not available on CloudABI.",
+ ))
+}
diff --git a/ctr-std/src/sys/cloudabi/shims/net.rs b/ctr-std/src/sys/cloudabi/shims/net.rs
new file mode 100644
index 0000000..93eaf6a
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/shims/net.rs
@@ -0,0 +1,296 @@
+// 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.
+
+use fmt;
+use io;
+use net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
+use time::Duration;
+use sys::{unsupported, Void};
+
+pub extern crate libc as netc;
+
+pub struct TcpStream(Void);
+
+impl TcpStream {
+ pub fn connect(_: &SocketAddr) -> io::Result<TcpStream> {
+ unsupported()
+ }
+
+ pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
+ unsupported()
+ }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ match self.0 {}
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ match self.0 {}
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn write(&self, _: &[u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ match self.0 {}
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ match self.0 {}
+ }
+
+ pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpStream> {
+ match self.0 {}
+ }
+
+ pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ match self.0 {}
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ match self.0 {}
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ match self.0 {}
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+}
+
+impl fmt::Debug for TcpStream {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+pub struct TcpListener(Void);
+
+impl TcpListener {
+ pub fn bind(_: &SocketAddr) -> io::Result<TcpListener> {
+ unsupported()
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ match self.0 {}
+ }
+
+ pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+ match self.0 {}
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpListener> {
+ match self.0 {}
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ match self.0 {}
+ }
+
+ pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn only_v6(&self) -> io::Result<bool> {
+ match self.0 {}
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ match self.0 {}
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+}
+
+impl fmt::Debug for TcpListener {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+pub struct UdpSocket(Void);
+
+impl UdpSocket {
+ pub fn bind(_: &SocketAddr) -> io::Result<UdpSocket> {
+ unsupported()
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ match self.0 {}
+ }
+
+ pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ match self.0 {}
+ }
+
+ pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ match self.0 {}
+ }
+
+ pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn duplicate(&self) -> io::Result<UdpSocket> {
+ match self.0 {}
+ }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ match self.0 {}
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ match self.0 {}
+ }
+
+ pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn broadcast(&self) -> io::Result<bool> {
+ match self.0 {}
+ }
+
+ pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn multicast_loop_v4(&self) -> io::Result<bool> {
+ match self.0 {}
+ }
+
+ pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
+ match self.0 {}
+ }
+
+ pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn multicast_loop_v6(&self) -> io::Result<bool> {
+ match self.0 {}
+ }
+
+ pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ match self.0 {}
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ match self.0 {}
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn send(&self, _: &[u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn connect(&self, _: &SocketAddr) -> io::Result<()> {
+ match self.0 {}
+ }
+}
+
+impl fmt::Debug for UdpSocket {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+pub struct LookupHost(Void);
+
+impl Iterator for LookupHost {
+ type Item = SocketAddr;
+ fn next(&mut self) -> Option<SocketAddr> {
+ match self.0 {}
+ }
+}
+
+pub fn lookup_host(_: &str) -> io::Result<LookupHost> {
+ unsupported()
+}
diff --git a/ctr-std/src/sys/cloudabi/shims/os.rs b/ctr-std/src/sys/cloudabi/shims/os.rs
new file mode 100644
index 0000000..1e355d9
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/shims/os.rs
@@ -0,0 +1,95 @@
+// Copyright 2018 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 error::Error as StdError;
+use ffi::{OsStr, OsString};
+use fmt;
+use io;
+use iter;
+use path::{self, PathBuf};
+use sys::{unsupported, Void};
+
+pub fn getcwd() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn chdir(_: &path::Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub type Env = iter::Empty<(OsString, OsString)>;
+
+pub fn env() -> Env {
+ iter::empty()
+}
+
+pub fn getenv(_: &OsStr) -> io::Result<Option<OsString>> {
+ Ok(None)
+}
+
+pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn unsetenv(_: &OsStr) -> io::Result<()> {
+ unsupported()
+}
+
+pub struct SplitPaths<'a>(&'a Void);
+
+pub fn split_paths(_unparsed: &OsStr) -> SplitPaths {
+ panic!("unsupported")
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+ type Item = PathBuf;
+ fn next(&mut self) -> Option<PathBuf> {
+ match *self.0 {}
+ }
+}
+
+#[derive(Debug)]
+pub struct JoinPathsError;
+
+pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
+where
+ I: Iterator<Item = T>,
+ T: AsRef<OsStr>,
+{
+ Err(JoinPathsError)
+}
+
+impl fmt::Display for JoinPathsError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ "not supported on CloudABI yet".fmt(f)
+ }
+}
+
+impl StdError for JoinPathsError {
+ fn description(&self) -> &str {
+ "not supported on CloudABI yet"
+ }
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+ None
+}
+
+pub fn temp_dir() -> PathBuf {
+ PathBuf::from("/tmp")
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn getpid() -> u32 {
+ 1
+}
diff --git a/ctr-std/src/sys/cloudabi/shims/pipe.rs b/ctr-std/src/sys/cloudabi/shims/pipe.rs
new file mode 100644
index 0000000..77a9cd6
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/shims/pipe.rs
@@ -0,0 +1,32 @@
+// 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.
+
+use io;
+use sys::Void;
+
+pub struct AnonPipe(Void);
+
+impl AnonPipe {
+ pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn diverge(&self) -> ! {
+ match self.0 {}
+ }
+}
+
+pub fn read2(p1: AnonPipe, _v1: &mut Vec<u8>, _p2: AnonPipe, _v2: &mut Vec<u8>) -> io::Result<()> {
+ match p1.0 {}
+}
diff --git a/ctr-std/src/sys/cloudabi/shims/process.rs b/ctr-std/src/sys/cloudabi/shims/process.rs
new file mode 100644
index 0000000..fcd40c1
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/shims/process.rs
@@ -0,0 +1,159 @@
+// 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.
+
+use ffi::OsStr;
+use fmt;
+use io;
+use sys::fs::File;
+use sys::pipe::AnonPipe;
+use sys::{unsupported, Void};
+use sys_common::process::{CommandEnv, DefaultEnvKey};
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct Command {
+ env: CommandEnv<DefaultEnvKey>,
+}
+
+// passed back to std::process with the pipes connected to the child, if any
+// were requested
+pub struct StdioPipes {
+ pub stdin: Option<AnonPipe>,
+ pub stdout: Option<AnonPipe>,
+ pub stderr: Option<AnonPipe>,
+}
+
+pub enum Stdio {
+ Inherit,
+ Null,
+ MakePipe,
+}
+
+impl Command {
+ pub fn new(_program: &OsStr) -> Command {
+ Command {
+ env: Default::default(),
+ }
+ }
+
+ pub fn arg(&mut self, _arg: &OsStr) {}
+
+ pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> {
+ &mut self.env
+ }
+
+ pub fn cwd(&mut self, _dir: &OsStr) {}
+
+ pub fn stdin(&mut self, _stdin: Stdio) {}
+
+ pub fn stdout(&mut self, _stdout: Stdio) {}
+
+ pub fn stderr(&mut self, _stderr: Stdio) {}
+
+ pub fn spawn(
+ &mut self,
+ _default: Stdio,
+ _needs_stdin: bool,
+ ) -> io::Result<(Process, StdioPipes)> {
+ unsupported()
+ }
+}
+
+impl From<AnonPipe> for Stdio {
+ fn from(pipe: AnonPipe) -> Stdio {
+ pipe.diverge()
+ }
+}
+
+impl From<File> for Stdio {
+ fn from(file: File) -> Stdio {
+ file.diverge()
+ }
+}
+
+impl fmt::Debug for Command {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ Ok(())
+ }
+}
+
+pub struct ExitStatus(Void);
+
+impl ExitStatus {
+ pub fn success(&self) -> bool {
+ match self.0 {}
+ }
+
+ pub fn code(&self) -> Option<i32> {
+ match self.0 {}
+ }
+}
+
+impl Clone for ExitStatus {
+ fn clone(&self) -> ExitStatus {
+ match self.0 {}
+ }
+}
+
+impl Copy for ExitStatus {}
+
+impl PartialEq for ExitStatus {
+ fn eq(&self, _other: &ExitStatus) -> bool {
+ match self.0 {}
+ }
+}
+
+impl Eq for ExitStatus {}
+
+impl fmt::Debug for ExitStatus {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+impl fmt::Display for ExitStatus {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitCode(bool);
+
+impl ExitCode {
+ pub const SUCCESS: ExitCode = ExitCode(false);
+ pub const FAILURE: ExitCode = ExitCode(true);
+
+ pub fn as_i32(&self) -> i32 {
+ self.0 as i32
+ }
+}
+
+pub struct Process(Void);
+
+impl Process {
+ pub fn id(&self) -> u32 {
+ match self.0 {}
+ }
+
+ pub fn kill(&mut self) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ match self.0 {}
+ }
+
+ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+ match self.0 {}
+ }
+}
diff --git a/ctr-std/src/sys/cloudabi/stack_overflow.rs b/ctr-std/src/sys/cloudabi/stack_overflow.rs
new file mode 100644
index 0000000..5c0b1e5
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/stack_overflow.rs
@@ -0,0 +1,23 @@
+// 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.
+
+#![cfg_attr(test, allow(dead_code))]
+
+pub struct Handler;
+
+impl Handler {
+ pub unsafe fn new() -> Handler {
+ Handler
+ }
+}
+
+pub unsafe fn init() {}
+
+pub unsafe fn cleanup() {}
diff --git a/ctr-std/src/sys/cloudabi/stdio.rs b/ctr-std/src/sys/cloudabi/stdio.rs
new file mode 100644
index 0000000..1d7344f
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/stdio.rs
@@ -0,0 +1,83 @@
+// Copyright 2018 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::cloudabi::abi;
+
+pub struct Stdin(());
+pub struct Stdout(());
+pub struct Stderr(());
+
+impl Stdin {
+ pub fn new() -> io::Result<Stdin> {
+ Ok(Stdin(()))
+ }
+
+ pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
+ Ok(0)
+ }
+}
+
+impl Stdout {
+ pub fn new() -> io::Result<Stdout> {
+ Ok(Stdout(()))
+ }
+
+ pub fn write(&self, _: &[u8]) -> io::Result<usize> {
+ Err(io::Error::new(
+ io::ErrorKind::BrokenPipe,
+ "Stdout is not connected to any output in this environment",
+ ))
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl Stderr {
+ pub fn new() -> io::Result<Stderr> {
+ Ok(Stderr(()))
+ }
+
+ pub fn write(&self, _: &[u8]) -> io::Result<usize> {
+ Err(io::Error::new(
+ io::ErrorKind::BrokenPipe,
+ "Stderr is not connected to any output in this environment",
+ ))
+ }
+
+ 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)
+ }
+}
+
+pub fn is_ebadf(err: &io::Error) -> bool {
+ err.raw_os_error() == Some(abi::errno::BADF as i32)
+}
+
+pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE;
+
+pub fn stderr_prints_nothing() -> bool {
+ false
+}
diff --git a/ctr-std/src/sys/cloudabi/thread.rs b/ctr-std/src/sys/cloudabi/thread.rs
new file mode 100644
index 0000000..5d66936
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/thread.rs
@@ -0,0 +1,126 @@
+// Copyright 2014-2018 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 cmp;
+use ffi::CStr;
+use io;
+use libc;
+use mem;
+use ptr;
+use sys::cloudabi::abi;
+use sys::time::dur2intervals;
+use sys_common::thread::*;
+use time::Duration;
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
+
+pub struct Thread {
+ id: libc::pthread_t,
+}
+
+// CloudABI has pthread_t as a pointer in which case we still want
+// a thread to be Send/Sync
+unsafe impl Send for Thread {}
+unsafe impl Sync for Thread {}
+
+impl Thread {
+ pub unsafe fn new<'a>(stack: usize, p: Box<FnBox() + 'a>) -> io::Result<Thread> {
+ let p = box p;
+ let mut native: libc::pthread_t = mem::zeroed();
+ let mut attr: libc::pthread_attr_t = mem::zeroed();
+ assert_eq!(libc::pthread_attr_init(&mut attr), 0);
+
+ let stack_size = cmp::max(stack, min_stack_size(&attr));
+ assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0);
+
+ let ret = libc::pthread_create(&mut native, &attr, thread_start, &*p as *const _ as *mut _);
+ assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+
+ return if ret != 0 {
+ Err(io::Error::from_raw_os_error(ret))
+ } else {
+ mem::forget(p); // ownership passed to pthread_create
+ Ok(Thread { id: native })
+ };
+
+ extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
+ unsafe {
+ start_thread(main as *mut u8);
+ }
+ ptr::null_mut()
+ }
+ }
+
+ pub fn yield_now() {
+ let ret = unsafe { abi::thread_yield() };
+ debug_assert_eq!(ret, abi::errno::SUCCESS);
+ }
+
+ pub fn set_name(_name: &CStr) {
+ // CloudABI has no way to set a thread name.
+ }
+
+ pub fn sleep(dur: Duration) {
+ unsafe {
+ let subscription = abi::subscription {
+ type_: abi::eventtype::CLOCK,
+ union: abi::subscription_union {
+ clock: abi::subscription_clock {
+ clock_id: abi::clockid::MONOTONIC,
+ timeout: dur2intervals(&dur),
+ ..mem::zeroed()
+ },
+ },
+ ..mem::zeroed()
+ };
+ let mut event: abi::event = mem::uninitialized();
+ let mut nevents: usize = mem::uninitialized();
+ let ret = abi::poll(&subscription, &mut event, 1, &mut nevents);
+ assert_eq!(ret, abi::errno::SUCCESS);
+ assert_eq!(event.error, abi::errno::SUCCESS);
+ }
+ }
+
+ pub fn join(self) {
+ unsafe {
+ let ret = libc::pthread_join(self.id, ptr::null_mut());
+ mem::forget(self);
+ assert!(
+ ret == 0,
+ "failed to join thread: {}",
+ io::Error::from_raw_os_error(ret)
+ );
+ }
+ }
+}
+
+impl Drop for Thread {
+ fn drop(&mut self) {
+ let ret = unsafe { libc::pthread_detach(self.id) };
+ debug_assert_eq!(ret, 0);
+ }
+}
+
+#[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() {}
+}
+
+fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
+ libc::PTHREAD_STACK_MIN
+}
diff --git a/ctr-std/src/sys/cloudabi/time.rs b/ctr-std/src/sys/cloudabi/time.rs
new file mode 100644
index 0000000..ee12731
--- /dev/null
+++ b/ctr-std/src/sys/cloudabi/time.rs
@@ -0,0 +1,111 @@
+// Copyright 2018 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 sys::cloudabi::abi;
+use time::Duration;
+
+const NSEC_PER_SEC: abi::timestamp = 1_000_000_000;
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct Instant {
+ t: abi::timestamp,
+}
+
+pub fn dur2intervals(dur: &Duration) -> abi::timestamp {
+ dur.as_secs()
+ .checked_mul(NSEC_PER_SEC)
+ .and_then(|nanos| nanos.checked_add(dur.subsec_nanos() as abi::timestamp))
+ .expect("overflow converting duration to nanoseconds")
+}
+
+impl Instant {
+ pub fn now() -> Instant {
+ unsafe {
+ let mut t = mem::uninitialized();
+ let ret = abi::clock_time_get(abi::clockid::MONOTONIC, 0, &mut t);
+ assert_eq!(ret, abi::errno::SUCCESS);
+ Instant { t: t }
+ }
+ }
+
+ pub fn sub_instant(&self, other: &Instant) -> Duration {
+ let diff = self.t
+ .checked_sub(other.t)
+ .expect("second instant is later than self");
+ Duration::new(diff / NSEC_PER_SEC, (diff % NSEC_PER_SEC) as u32)
+ }
+
+ pub fn add_duration(&self, other: &Duration) -> Instant {
+ Instant {
+ t: self.t
+ .checked_add(dur2intervals(other))
+ .expect("overflow when adding duration to instant"),
+ }
+ }
+
+ pub fn sub_duration(&self, other: &Duration) -> Instant {
+ Instant {
+ t: self.t
+ .checked_sub(dur2intervals(other))
+ .expect("overflow when subtracting duration from instant"),
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct SystemTime {
+ t: abi::timestamp,
+}
+
+impl SystemTime {
+ pub fn now() -> SystemTime {
+ unsafe {
+ let mut t = mem::uninitialized();
+ let ret = abi::clock_time_get(abi::clockid::REALTIME, 0, &mut t);
+ assert_eq!(ret, abi::errno::SUCCESS);
+ SystemTime { t: t }
+ }
+ }
+
+ pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
+ if self.t >= other.t {
+ let diff = self.t - other.t;
+ Ok(Duration::new(
+ diff / NSEC_PER_SEC,
+ (diff % NSEC_PER_SEC) as u32,
+ ))
+ } else {
+ let diff = other.t - self.t;
+ Err(Duration::new(
+ diff / NSEC_PER_SEC,
+ (diff % NSEC_PER_SEC) as u32,
+ ))
+ }
+ }
+
+ pub fn add_duration(&self, other: &Duration) -> SystemTime {
+ SystemTime {
+ t: self.t
+ .checked_add(dur2intervals(other))
+ .expect("overflow when adding duration to instant"),
+ }
+ }
+
+ pub fn sub_duration(&self, other: &Duration) -> SystemTime {
+ SystemTime {
+ t: self.t
+ .checked_sub(dur2intervals(other))
+ .expect("overflow when subtracting duration from instant"),
+ }
+ }
+}
+
+pub const UNIX_EPOCH: SystemTime = SystemTime { t: 0 };
diff --git a/ctr-std/src/sys/horizon/android.rs b/ctr-std/src/sys/horizon/android.rs
new file mode 100644
index 0000000..1043672
--- /dev/null
+++ b/ctr-std/src/sys/horizon/android.rs
@@ -0,0 +1,170 @@
+// 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.
+
+//! Android ABI-compatibility module
+//!
+//! The ABI of Android has changed quite a bit over time, and libstd attempts to
+//! be both forwards and backwards compatible as much as possible. We want to
+//! always work with the most recent version of Android, but we also want to
+//! work with older versions of Android for whenever projects need to.
+//!
+//! Our current minimum supported Android version is `android-9`, e.g. Android
+//! with API level 9. We then in theory want to work on that and all future
+//! versions of Android!
+//!
+//! Some of the detection here is done at runtime via `dlopen` and
+//! introspection. Other times no detection is performed at all and we just
+//! provide a fallback implementation as some versions of Android we support
+//! don't have the function.
+//!
+//! You'll find more details below about why each compatibility shim is needed.
+
+#![cfg(target_os = "android")]
+
+use libc::{c_int, c_void, sighandler_t, size_t, ssize_t};
+use libc::{ftruncate, pread, pwrite};
+
+use io;
+use super::{cvt, cvt_r};
+
+// The `log2` and `log2f` functions apparently appeared in android-18, or at
+// least you can see they're not present in the android-17 header [1] and they
+// are present in android-18 [2].
+//
+// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
+// /android-17/arch-arm/usr/include/math.h
+// [2]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
+// /android-18/arch-arm/usr/include/math.h
+//
+// Note that these shims are likely less precise than directly calling `log2`,
+// but hopefully that should be enough for now...
+//
+// Note that mathematically, for any arbitrary `y`:
+//
+// log_2(x) = log_y(x) / log_y(2)
+// = log_y(x) / (1 / log_2(y))
+// = log_y(x) * log_2(y)
+//
+// Hence because `ln` (log_e) is available on all Android we just choose `y = e`
+// and get:
+//
+// log_2(x) = ln(x) * log_2(e)
+
+#[cfg(not(test))]
+pub fn log2f32(f: f32) -> f32 {
+ f.ln() * ::f32::consts::LOG2_E
+}
+
+#[cfg(not(test))]
+pub fn log2f64(f: f64) -> f64 {
+ f.ln() * ::f64::consts::LOG2_E
+}
+
+// Back in the day [1] the `signal` function was just an inline wrapper
+// around `bsd_signal`, but starting in API level android-20 the `signal`
+// symbols was introduced [2]. Finally, in android-21 the API `bsd_signal` was
+// removed [3].
+//
+// Basically this means that if we want to be binary compatible with multiple
+// Android releases (oldest being 9 and newest being 21) then we need to check
+// for both symbols and not actually link against either.
+//
+// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
+// /android-18/arch-arm/usr/include/signal.h
+// [2]: https://chromium.googlesource.com/android_tools/+/fbd420/ndk_experimental
+// /platforms/android-20/arch-arm
+// /usr/include/signal.h
+// [3]: https://chromium.googlesource.com/android_tools/+/20ee6d/ndk/platforms
+// /android-21/arch-arm/usr/include/signal.h
+pub unsafe fn signal(signum: c_int, handler: sighandler_t) -> sighandler_t {
+ weak!(fn signal(c_int, sighandler_t) -> sighandler_t);
+ weak!(fn bsd_signal(c_int, sighandler_t) -> sighandler_t);
+
+ let f = signal.get().or_else(|| bsd_signal.get());
+ let f = f.expect("neither `signal` nor `bsd_signal` symbols found");
+ f(signum, handler)
+}
+
+// The `ftruncate64` symbol apparently appeared in android-12, so we do some
+// dynamic detection to see if we can figure out whether `ftruncate64` exists.
+//
+// If it doesn't we just fall back to `ftruncate`, generating an error for
+// too-large values.
+#[cfg(target_pointer_width = "32")]
+pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> {
+ weak!(fn ftruncate64(c_int, i64) -> c_int);
+
+ unsafe {
+ match ftruncate64.get() {
+ Some(f) => cvt_r(|| f(fd, size as i64)).map(|_| ()),
+ None => {
+ if size > i32::max_value() as u64 {
+ Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot truncate >2GB"))
+ } else {
+ cvt_r(|| ftruncate(fd, size as i32)).map(|_| ())
+ }
+ }
+ }
+ }
+}
+
+#[cfg(target_pointer_width = "64")]
+pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> {
+ unsafe {
+ cvt_r(|| ftruncate(fd, size as i64)).map(|_| ())
+ }
+}
+
+#[cfg(target_pointer_width = "32")]
+pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i64)
+ -> io::Result<ssize_t>
+{
+ use convert::TryInto;
+ weak!(fn pread64(c_int, *mut c_void, size_t, i64) -> ssize_t);
+ pread64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| {
+ if let Ok(o) = offset.try_into() {
+ cvt(pread(fd, buf, count, o))
+ } else {
+ Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot pread >2GB"))
+ }
+ })
+}
+
+#[cfg(target_pointer_width = "32")]
+pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: i64)
+ -> io::Result<ssize_t>
+{
+ use convert::TryInto;
+ weak!(fn pwrite64(c_int, *const c_void, size_t, i64) -> ssize_t);
+ pwrite64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| {
+ if let Ok(o) = offset.try_into() {
+ cvt(pwrite(fd, buf, count, o))
+ } else {
+ Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot pwrite >2GB"))
+ }
+ })
+}
+
+#[cfg(target_pointer_width = "64")]
+pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i64)
+ -> io::Result<ssize_t>
+{
+ cvt(pread(fd, buf, count, offset))
+}
+
+#[cfg(target_pointer_width = "64")]
+pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: i64)
+ -> io::Result<ssize_t>
+{
+ cvt(pwrite(fd, buf, count, offset))
+}
diff --git a/ctr-std/src/sys/horizon/args.rs b/ctr-std/src/sys/horizon/args.rs
new file mode 100644
index 0000000..84d0c85
--- /dev/null
+++ b/ctr-std/src/sys/horizon/args.rs
@@ -0,0 +1,60 @@
+// 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.
+
+use ffi::OsString;
+use marker::PhantomData;
+use vec;
+
+pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
+ // Currently null because we haven't implemented args yet
+}
+
+pub unsafe fn cleanup() {
+}
+
+pub fn args() -> Args {
+ return Args {
+ iter: Vec::new().into_iter(),
+ _dont_send_or_sync_me: PhantomData,
+ }
+}
+
+pub struct Args {
+ iter: vec::IntoIter<OsString>,
+ _dont_send_or_sync_me: PhantomData<*mut ()>,
+}
+
+impl Args {
+ pub fn inner_debug(&self) -> &[OsString] {
+ self.iter.as_slice()
+ }
+}
+
+impl Iterator for Args {
+ type Item = OsString;
+ fn next(&mut self) -> Option<OsString> {
+ self.iter.next()
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+impl ExactSizeIterator for Args {
+ fn len(&self) -> usize {
+ self.iter.len()
+ }
+}
+
+impl DoubleEndedIterator for Args {
+ fn next_back(&mut self) -> Option<OsString> {
+ self.iter.next_back()
+ }
+}
diff --git a/ctr-std/src/sys/horizon/backtrace/mod.rs b/ctr-std/src/sys/horizon/backtrace/mod.rs
new file mode 100644
index 0000000..b5bf20c
--- /dev/null
+++ b/ctr-std/src/sys/horizon/backtrace/mod.rs
@@ -0,0 +1,119 @@
+// 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.
+
+/// Backtrace support built on libgcc with some extra OS-specific support
+///
+/// Some methods of getting a backtrace:
+///
+/// * The backtrace() functions on unix. It turns out this doesn't work very
+/// well for green threads on macOS, and the address to symbol portion of it
+/// suffers problems that are described below.
+///
+/// * Using libunwind. This is more difficult than it sounds because libunwind
+/// isn't installed everywhere by default. It's also a bit of a hefty library,
+/// so possibly not the best option. When testing, libunwind was excellent at
+/// getting both accurate backtraces and accurate symbols across platforms.
+/// This route was not chosen in favor of the next option, however.
+///
+/// * We're already using libgcc_s for exceptions in rust (triggering thread
+/// unwinding and running destructors on the stack), and it turns out that it
+/// conveniently comes with a function that also gives us a backtrace. All of
+/// these functions look like _Unwind_*, but it's not quite the full
+/// repertoire of the libunwind API. Due to it already being in use, this was
+/// the chosen route of getting a backtrace.
+///
+/// After choosing libgcc_s for backtraces, the sad part is that it will only
+/// give us a stack trace of instruction pointers. Thankfully these instruction
+/// pointers are accurate (they work for green and native threads), but it's
+/// then up to us again to figure out how to translate these addresses to
+/// symbols. As with before, we have a few options. Before, that, a little bit
+/// of an interlude about symbols. This is my very limited knowledge about
+/// symbol tables, and this information is likely slightly wrong, but the
+/// general idea should be correct.
+///
+/// When talking about symbols, it's helpful to know a few things about where
+/// symbols are located. Some symbols are located in the dynamic symbol table
+/// of the executable which in theory means that they're available for dynamic
+/// linking and lookup. Other symbols end up only in the local symbol table of
+/// the file. This loosely corresponds to pub and priv functions in Rust.
+///
+/// Armed with this knowledge, we know that our solution for address to symbol
+/// translation will need to consult both the local and dynamic symbol tables.
+/// With that in mind, here's our options of translating an address to
+/// a symbol.
+///
+/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr()
+/// behind the scenes to translate, and this is why backtrace() was not used.
+/// Conveniently, this method works fantastically on macOS. It appears dladdr()
+/// uses magic to consult the local symbol table, or we're putting everything
+/// in the dynamic symbol table anyway. Regardless, for macOS, this is the
+/// method used for translation. It's provided by the system and easy to do.o
+///
+/// Sadly, all other systems have a dladdr() implementation that does not
+/// consult the local symbol table. This means that most functions are blank
+/// because they don't have symbols. This means that we need another solution.
+///
+/// * Use unw_get_proc_name(). This is part of the libunwind api (not the
+/// libgcc_s version of the libunwind api), but involves taking a dependency
+/// to libunwind. We may pursue this route in the future if we bundle
+/// libunwind, but libunwind was unwieldy enough that it was not chosen at
+/// this time to provide this functionality.
+///
+/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a
+/// semi-reasonable solution. The stdlib already knows how to spawn processes,
+/// so in theory it could invoke readelf, parse the output, and consult the
+/// local/dynamic symbol tables from there. This ended up not getting chosen
+/// due to the craziness of the idea plus the advent of the next option.
+///
+/// * Use `libbacktrace`. It turns out that this is a small library bundled in
+/// the gcc repository which provides backtrace and symbol translation
+/// functionality. All we really need from it is the backtrace functionality,
+/// and we only really need this on everything that's not macOS, so this is the
+/// chosen route for now.
+///
+/// In summary, the current situation uses libgcc_s to get a trace of stack
+/// pointers, and we use dladdr() or libbacktrace to translate these addresses
+/// to symbols. This is a bit of a hokey implementation as-is, but it works for
+/// all unix platforms we support right now, so it at least gets the job done.
+
+pub use self::tracing::unwind_backtrace;
+pub use self::printing::{foreach_symbol_fileline, resolve_symname};
+
+// tracing impls:
+mod tracing;
+// symbol resolvers:
+mod printing;
+
+#[cfg(not(target_os = "emscripten"))]
+pub mod gnu {
+ use io;
+ use fs;
+ use libc::c_char;
+
+ #[cfg(not(any(target_os = "macos", target_os = "ios")))]
+ pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> {
+ Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
+ }
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> {
+ use env;
+ use os::unix::ffi::OsStrExt;
+
+ let filename = env::current_exe()?;
+ let file = fs::File::open(&filename)?;
+ let mut filename_cstr: Vec<_> = filename.as_os_str().as_bytes().iter()
+ .map(|&x| x as c_char).collect();
+ filename_cstr.push(0); // Null terminate
+ Ok((filename_cstr, file))
+ }
+}
+
+pub struct BacktraceContext;
diff --git a/ctr-std/src/sys/horizon/backtrace/printing/dladdr.rs b/ctr-std/src/sys/horizon/backtrace/printing/dladdr.rs
new file mode 100644
index 0000000..bc56fd6
--- /dev/null
+++ b/ctr-std/src/sys/horizon/backtrace/printing/dladdr.rs
@@ -0,0 +1,45 @@
+// 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.
+
+use io;
+use intrinsics;
+use ffi::CStr;
+use libc;
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
+
+pub fn resolve_symname<F>(frame: Frame,
+ callback: F,
+ _: &BacktraceContext) -> io::Result<()>
+ where F: FnOnce(Option<&str>) -> io::Result<()>
+{
+ unsafe {
+ let mut info: Dl_info = intrinsics::init();
+ let symname = if dladdr(frame.exact_position as *mut _, &mut info) == 0 ||
+ info.dli_sname.is_null() {
+ None
+ } else {
+ CStr::from_ptr(info.dli_sname).to_str().ok()
+ };
+ callback(symname)
+ }
+}
+
+#[repr(C)]
+struct Dl_info {
+ dli_fname: *const libc::c_char,
+ dli_fbase: *mut libc::c_void,
+ dli_sname: *const libc::c_char,
+ dli_saddr: *mut libc::c_void,
+}
+
+extern {
+ fn dladdr(addr: *const libc::c_void, info: *mut Dl_info) -> libc::c_int;
+}
diff --git a/ctr-std/src/sys/horizon/backtrace/printing/mod.rs b/ctr-std/src/sys/horizon/backtrace/printing/mod.rs
new file mode 100644
index 0000000..caa6071
--- /dev/null
+++ b/ctr-std/src/sys/horizon/backtrace/printing/mod.rs
@@ -0,0 +1,43 @@
+// Copyright 2014-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.
+
+mod dladdr;
+
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
+use io;
+
+#[cfg(target_os = "emscripten")]
+pub use self::dladdr::resolve_symname;
+
+#[cfg(target_os = "emscripten")]
+pub fn foreach_symbol_fileline<F>(_: Frame, _: F, _: &BacktraceContext) -> io::Result<bool>
+where
+ F: FnMut(&[u8], u32) -> io::Result<()>
+{
+ Ok(false)
+}
+
+#[cfg(not(target_os = "emscripten"))]
+pub use sys_common::gnu::libbacktrace::foreach_symbol_fileline;
+
+#[cfg(not(target_os = "emscripten"))]
+pub fn resolve_symname<F>(frame: Frame, callback: F, bc: &BacktraceContext) -> io::Result<()>
+where
+ F: FnOnce(Option<&str>) -> io::Result<()>
+{
+ ::sys_common::gnu::libbacktrace::resolve_symname(frame, |symname| {
+ if symname.is_some() {
+ callback(symname)
+ } else {
+ dladdr::resolve_symname(frame, callback, bc)
+ }
+ }, bc)
+}
diff --git a/ctr-std/src/sys/horizon/backtrace/tracing/backtrace_fn.rs b/ctr-std/src/sys/horizon/backtrace/tracing/backtrace_fn.rs
new file mode 100644
index 0000000..6293eeb
--- /dev/null
+++ b/ctr-std/src/sys/horizon/backtrace/tracing/backtrace_fn.rs
@@ -0,0 +1,49 @@
+// 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.
+
+/// As always - iOS on arm uses SjLj exceptions and
+/// _Unwind_Backtrace is even not available there. Still,
+/// backtraces could be extracted using a backtrace function,
+/// which thanks god is public
+///
+/// As mentioned in a huge comment block in `super::super`, backtrace
+/// doesn't play well with green threads, so while it is extremely nice and
+/// simple to use it should be used only on iOS devices as the only viable
+/// option.
+
+use io;
+use libc;
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
+
+#[inline(never)] // if we know this is a function call, we can skip it when
+ // tracing
+pub fn unwind_backtrace(frames: &mut [Frame])
+ -> io::Result<(usize, BacktraceContext)>
+{
+ const FRAME_LEN: usize = 100;
+ assert!(FRAME_LEN >= frames.len());
+ let mut raw_frames = [::ptr::null_mut(); FRAME_LEN];
+ let nb_frames = unsafe {
+ backtrace(raw_frames.as_mut_ptr(), raw_frames.len() as libc::c_int)
+ } as usize;
+ for (from, to) in raw_frames.iter().zip(frames.iter_mut()).take(nb_frames) {
+ *to = Frame {
+ exact_position: *from as *mut u8,
+ symbol_addr: *from as *mut u8,
+ inline_context: 0,
+ };
+ }
+ Ok((nb_frames as usize, BacktraceContext))
+}
+
+extern {
+ fn backtrace(buf: *mut *mut libc::c_void, sz: libc::c_int) -> libc::c_int;
+}
diff --git a/ctr-std/src/sys/horizon/backtrace/tracing/gcc_s.rs b/ctr-std/src/sys/horizon/backtrace/tracing/gcc_s.rs
new file mode 100644
index 0000000..1b92fc0
--- /dev/null
+++ b/ctr-std/src/sys/horizon/backtrace/tracing/gcc_s.rs
@@ -0,0 +1,107 @@
+// 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.
+
+use error::Error;
+use io;
+use libc;
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
+
+use unwind as uw;
+
+struct Context<'a> {
+ idx: usize,
+ frames: &'a mut [Frame],
+}
+
+#[derive(Debug)]
+struct UnwindError(uw::_Unwind_Reason_Code);
+
+impl Error for UnwindError {
+ fn description(&self) -> &'static str {
+ "unexpected return value while unwinding"
+ }
+}
+
+impl ::fmt::Display for UnwindError {
+ fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
+ write!(f, "{}: {:?}", self.description(), self.0)
+ }
+}
+
+#[inline(never)] // if we know this is a function call, we can skip it when
+ // tracing
+pub fn unwind_backtrace(frames: &mut [Frame])
+ -> io::Result<(usize, BacktraceContext)>
+{
+ let mut cx = Context {
+ idx: 0,
+ frames,
+ };
+ let result_unwind = unsafe {
+ uw::_Unwind_Backtrace(trace_fn,
+ &mut cx as *mut Context
+ as *mut libc::c_void)
+ };
+ // See libunwind:src/unwind/Backtrace.c for the return values.
+ // No, there is no doc.
+ match result_unwind {
+ // These return codes seem to be benign and need to be ignored for backtraces
+ // to show up properly on all tested platforms.
+ uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => {
+ Ok((cx.idx, BacktraceContext))
+ }
+ _ => {
+ Err(io::Error::new(io::ErrorKind::Other,
+ UnwindError(result_unwind)))
+ }
+ }
+}
+
+extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
+ arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
+ let cx = unsafe { &mut *(arg as *mut Context) };
+ let mut ip_before_insn = 0;
+ let mut ip = unsafe {
+ uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void
+ };
+ if !ip.is_null() && ip_before_insn == 0 {
+ // this is a non-signaling frame, so `ip` refers to the address
+ // after the calling instruction. account for that.
+ ip = (ip as usize - 1) as *mut _;
+ }
+
+ // dladdr() on osx gets whiny when we use FindEnclosingFunction, and
+ // it appears to work fine without it, so we only use
+ // FindEnclosingFunction on non-osx platforms. In doing so, we get a
+ // slightly more accurate stack trace in the process.
+ //
+ // This is often because panic involves the last instruction of a
+ // function being "call std::rt::begin_unwind", with no ret
+ // instructions after it. This means that the return instruction
+ // pointer points *outside* of the calling function, and by
+ // unwinding it we go back to the original function.
+ let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
+ ip
+ } else {
+ unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
+ };
+
+ if cx.idx < cx.frames.len() {
+ cx.frames[cx.idx] = Frame {
+ symbol_addr: symaddr as *mut u8,
+ exact_position: ip as *mut u8,
+ inline_context: 0,
+ };
+ cx.idx += 1;
+ }
+
+ uw::_URC_NO_REASON
+}
diff --git a/ctr-std/src/sys/horizon/backtrace/tracing/mod.rs b/ctr-std/src/sys/horizon/backtrace/tracing/mod.rs
new file mode 100644
index 0000000..c9c8e26
--- /dev/null
+++ b/ctr-std/src/sys/horizon/backtrace/tracing/mod.rs
@@ -0,0 +1,18 @@
+// 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.
+
+pub use self::imp::*;
+
+#[cfg(not(all(target_os = "ios", target_arch = "arm")))]
+#[path = "gcc_s.rs"]
+mod imp;
+#[cfg(all(target_os = "ios", target_arch = "arm"))]
+#[path = "backtrace_fn.rs"]
+mod imp;
diff --git a/ctr-std/src/sys/horizon/cmath.rs b/ctr-std/src/sys/horizon/cmath.rs
new file mode 100644
index 0000000..2bc9665
--- /dev/null
+++ b/ctr-std/src/sys/horizon/cmath.rs
@@ -0,0 +1,43 @@
+// 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 acosf(n: c_float) -> c_float;
+ pub fn asin(n: c_double) -> c_double;
+ pub fn asinf(n: c_float) -> c_float;
+ pub fn atan(n: c_double) -> c_double;
+ pub fn atan2(a: c_double, b: c_double) -> c_double;
+ pub fn atan2f(a: c_float, b: c_float) -> c_float;
+ pub fn atanf(n: c_float) -> c_float;
+ 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 coshf(n: c_float) -> c_float;
+ 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;
+ pub fn hypot(x: c_double, y: c_double) -> c_double;
+ 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 sinhf(n: c_float) -> c_float;
+ pub fn tan(n: c_double) -> c_double;
+ pub fn tanf(n: c_float) -> c_float;
+ pub fn tanh(n: c_double) -> c_double;
+ pub fn tanhf(n: c_float) -> c_float;
+}
diff --git a/ctr-std/src/sys/horizon/condvar.rs b/ctr-std/src/sys/horizon/condvar.rs
new file mode 100644
index 0000000..f6cb49d
--- /dev/null
+++ b/ctr-std/src/sys/horizon/condvar.rs
@@ -0,0 +1,139 @@
+// 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.
+
+// *Implementation adapted from `/sys/redox/condvar.rs`
+
+use cell::UnsafeCell;
+use intrinsics::atomic_cxchg;
+use ptr;
+use time::Duration;
+
+use sys::mutex::{self, Mutex};
+
+pub struct Condvar {
+ lock: UnsafeCell<*mut ::libctru::LightLock>,
+}
+
+unsafe impl Send for Condvar {}
+unsafe impl Sync for Condvar {}
+
+impl Condvar {
+ pub const fn new() -> Condvar {
+ Condvar {
+ lock: UnsafeCell::new(ptr::null_mut()),
+ }
+ }
+
+ #[inline]
+ pub unsafe fn init(&self) {
+ *self.lock.get() = ptr::null_mut();
+ }
+
+ #[inline]
+ pub fn notify_one(&self) {
+ unsafe {
+ let arbiter = ::libctru::__sync_get_arbiter();
+
+ ::libctru::svcArbitrateAddress(arbiter,
+ *self.lock.get() as u32,
+ ::libctru::ARBITRATION_SIGNAL,
+ 1,
+ 0);
+ }
+ }
+
+ #[inline]
+ pub fn notify_all(&self) {
+ unsafe {
+ let lock = self.lock.get();
+
+ if *lock == ptr::null_mut() {
+ return;
+ }
+
+ let arbiter = ::libctru::__sync_get_arbiter();
+
+ ::libctru::svcArbitrateAddress(arbiter,
+ *self.lock.get() as u32,
+ ::libctru::ARBITRATION_SIGNAL,
+ -1,
+ 0);
+ }
+ }
+
+ #[inline]
+ pub fn wait(&self, mutex: &Mutex) {
+ unsafe {
+ let lock = self.lock.get();
+
+ if *lock != mutex::raw(mutex) as *mut i32 {
+ if *lock != ptr::null_mut() {
+ panic!("Condvar used with more than one Mutex");
+ }
+
+ atomic_cxchg(lock as *mut usize, 0, mutex::raw(mutex) as usize);
+ }
+
+ mutex.unlock();
+
+ let arbiter = ::libctru::__sync_get_arbiter();
+
+ ::libctru::svcArbitrateAddress(arbiter,
+ *self.lock.get() as u32,
+ ::libctru::ARBITRATION_WAIT_IF_LESS_THAN,
+ 2,
+ 0);
+
+ mutex.lock();
+ }
+ }
+
+ #[inline]
+ pub fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+ use time::Instant;
+
+ unsafe {
+ let lock = self.lock.get();
+
+ if *lock != mutex::raw(mutex) as *mut i32 {
+ if *lock != ptr::null_mut() {
+ panic!("Condvar used with more than one Mutex");
+ }
+
+ atomic_cxchg(lock as *mut usize, 0, mutex::raw(mutex) as usize);
+ }
+
+ let now = Instant::now();
+
+ let nanos = dur.as_secs()
+ .saturating_mul(1_000_000_000)
+ .saturating_add(dur.subsec_nanos() as u64);
+
+ mutex.unlock();
+
+ let arbiter = ::libctru::__sync_get_arbiter();
+
+ ::libctru::svcArbitrateAddress(arbiter,
+ *self.lock.get() as u32,
+ ::libctru::ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT,
+ 2,
+ nanos as i64);
+
+ mutex.lock();
+
+ now.elapsed() < dur
+ }
+ }
+
+ #[inline]
+ pub unsafe fn destroy(&self) {
+ *self.lock.get() = ptr::null_mut();
+ }
+} \ No newline at end of file
diff --git a/ctr-std/src/sys/horizon/env.rs b/ctr-std/src/sys/horizon/env.rs
new file mode 100644
index 0000000..5e2fe38
--- /dev/null
+++ b/ctr-std/src/sys/horizon/env.rs
@@ -0,0 +1,185 @@
+// 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.
+
+#[cfg(target_os = "horizon")]
+pub mod os {
+ pub const FAMILY: &'static str = "nintendo";
+ pub const OS: &'static str = "horizon";
+ pub const DLL_PREFIX: &'static str = "";
+ pub const DLL_SUFFIX: &'static str = ".cro";
+ pub const DLL_EXTENSION: &'static str = ".cro";
+ pub const EXE_SUFFIX: &'static str = ".3dsx";
+ pub const EXE_EXTENSION: &'static str = "3dsx";
+}
+
+#[cfg(target_os = "linux")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "linux";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "macos")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "macos";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".dylib";
+ pub const DLL_EXTENSION: &'static str = "dylib";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "ios")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "ios";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".dylib";
+ pub const DLL_EXTENSION: &'static str = "dylib";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "freebsd")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "freebsd";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "dragonfly")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "dragonfly";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "bitrig")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "bitrig";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "netbsd")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "netbsd";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "openbsd")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "openbsd";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "android")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "android";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "solaris")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "solaris";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "haiku")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "haiku";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(all(target_os = "emscripten", target_arch = "asmjs"))]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "emscripten";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = ".js";
+ pub const EXE_EXTENSION: &'static str = "js";
+}
+
+#[cfg(all(target_os = "emscripten", target_arch = "wasm32"))]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "emscripten";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = ".js";
+ pub const EXE_EXTENSION: &'static str = "js";
+}
+
+#[cfg(target_os = "fuchsia")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "fuchsia";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "l4re")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "l4re";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
diff --git a/ctr-std/src/sys/horizon/ext/ffi.rs b/ctr-std/src/sys/horizon/ext/ffi.rs
new file mode 100644
index 0000000..8347145
--- /dev/null
+++ b/ctr-std/src/sys/horizon/ext/ffi.rs
@@ -0,0 +1,119 @@
+// 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.
+
+//! Unix-specific extension to the primitives in the `std::ffi` module
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+use ffi::{OsStr, OsString};
+use mem;
+use sys::os_str::Buf;
+use sys_common::{FromInner, IntoInner, AsInner};
+
+/// Unix-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 byte vector.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::ffi::OsString;
+ /// use std::os::unix::ffi::OsStringExt;
+ ///
+ /// let bytes = b"foo".to_vec();
+ /// let os_string = OsString::from_vec(bytes);
+ /// assert_eq!(os_string.to_str(), Some("foo"));
+ /// ```
+ ///
+ /// [`OsString`]: ../../../ffi/struct.OsString.html
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn from_vec(vec: Vec<u8>) -> Self;
+
+ /// Yields the underlying byte vector of this [`OsString`].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::ffi::OsString;
+ /// use std::os::unix::ffi::OsStringExt;
+ ///
+ /// let mut os_string = OsString::new();
+ /// os_string.push("foo");
+ /// let bytes = os_string.into_vec();
+ /// assert_eq!(bytes, b"foo");
+ /// ```
+ ///
+ /// [`OsString`]: ../../../ffi/struct.OsString.html
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn into_vec(self) -> Vec<u8>;
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl OsStringExt for OsString {
+ fn from_vec(vec: Vec<u8>) -> OsString {
+ FromInner::from_inner(Buf { inner: vec })
+ }
+ fn into_vec(self) -> Vec<u8> {
+ self.into_inner().inner
+ }
+}
+
+/// Unix-specific extensions to [`OsStr`].
+///
+/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub trait OsStrExt {
+ #[stable(feature = "rust1", since = "1.0.0")]
+ /// Creates an [`OsStr`] from a byte slice.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::ffi::OsStr;
+ /// use std::os::unix::ffi::OsStrExt;
+ ///
+ /// let bytes = b"foo";
+ /// let os_str = OsStr::from_bytes(bytes);
+ /// assert_eq!(os_str.to_str(), Some("foo"));
+ /// ```
+ ///
+ /// [`OsStr`]: ../../../ffi/struct.OsStr.html
+ fn from_bytes(slice: &[u8]) -> &Self;
+
+ /// Gets the underlying byte view of the [`OsStr`] slice.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::ffi::OsStr;
+ /// use std::os::unix::ffi::OsStrExt;
+ ///
+ /// let mut os_str = OsStr::new("foo");
+ /// let bytes = os_str.as_bytes();
+ /// assert_eq!(bytes, b"foo");
+ /// ```
+ ///
+ /// [`OsStr`]: ../../../ffi/struct.OsStr.html
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn as_bytes(&self) -> &[u8];
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl OsStrExt for OsStr {
+ fn from_bytes(slice: &[u8]) -> &OsStr {
+ unsafe { mem::transmute(slice) }
+ }
+ fn as_bytes(&self) -> &[u8] {
+ &self.as_inner().inner
+ }
+}
diff --git a/ctr-std/src/sys/horizon/ext/fs.rs b/ctr-std/src/sys/horizon/ext/fs.rs
new file mode 100644
index 0000000..29f2542
--- /dev/null
+++ b/ctr-std/src/sys/horizon/ext/fs.rs
@@ -0,0 +1,736 @@
+// 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.
+
+//! Unix-specific extensions to primitives in the `std::fs` module.
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+use fs::{self, Permissions, OpenOptions};
+use io;
+use libc;
+use path::Path;
+use sys;
+use sys_common::{FromInner, AsInner, AsInnerMut};
+use sys::platform::fs::MetadataExt as UnixMetadataExt;
+
+/// Unix-specific extensions to [`File`].
+///
+/// [`File`]: ../../../../std/fs/struct.File.html
+#[stable(feature = "file_offset", since = "1.15.0")]
+pub trait FileExt {
+ /// Reads a number of bytes starting from a given offset.
+ ///
+ /// 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 file cursor is not affected by this function.
+ ///
+ /// Note that similar to [`File::read`], it is not an error to return with a
+ /// short read.
+ ///
+ /// [`File::read`]: ../../../../std/fs/struct.File.html#method.read
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::io;
+ /// use std::fs::File;
+ /// use std::os::unix::prelude::FileExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let mut buf = [0u8; 8];
+ /// let file = File::open("foo.txt")?;
+ ///
+ /// // We now read 8 bytes from the offset 10.
+ /// let num_bytes_read = file.read_at(&mut buf, 10)?;
+ /// println!("read {} bytes: {:?}", num_bytes_read, buf);
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_offset", since = "1.15.0")]
+ fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
+
+ /// Writes a number of bytes starting from a given offset.
+ ///
+ /// 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 file cursor is not affected by this function.
+ ///
+ /// When writing beyond the end of the file, the file is appropriately
+ /// extended and the intermediate bytes are initialized with the value 0.
+ ///
+ /// Note that similar to [`File::write`], it is not an error to return a
+ /// short write.
+ ///
+ /// [`File::write`]: ../../../../std/fs/struct.File.html#write.v
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::File;
+ /// use std::io;
+ /// use std::os::unix::prelude::FileExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let file = File::open("foo.txt")?;
+ ///
+ /// // We now write at the offset 10.
+ /// file.write_at(b"sushi", 10)?;
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_offset", since = "1.15.0")]
+ fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
+}
+
+#[stable(feature = "file_offset", since = "1.15.0")]
+impl FileExt for fs::File {
+ fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
+ self.as_inner().read_at(buf, offset)
+ }
+ fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
+ self.as_inner().write_at(buf, offset)
+ }
+}
+
+/// Unix-specific extensions to [`fs::Permissions`].
+///
+/// [`fs::Permissions`]: ../../../../std/fs/struct.Permissions.html
+#[stable(feature = "fs_ext", since = "1.1.0")]
+pub trait PermissionsExt {
+ /// Returns the underlying raw `st_mode` bits that contain the standard
+ /// Unix permissions for this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::File;
+ /// use std::os::unix::fs::PermissionsExt;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let f = File::create("foo.txt")?;
+ /// let metadata = f.metadata()?;
+ /// let permissions = metadata.permissions();
+ ///
+ /// println!("permissions: {}", permissions.mode());
+ /// Ok(()) }
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn mode(&self) -> u32;
+
+ /// Sets the underlying raw bits for this set of permissions.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::File;
+ /// use std::os::unix::fs::PermissionsExt;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let f = File::create("foo.txt")?;
+ /// let metadata = f.metadata()?;
+ /// let mut permissions = metadata.permissions();
+ ///
+ /// permissions.set_mode(0o644); // Read/write for owner and read for others.
+ /// assert_eq!(permissions.mode(), 0o644);
+ /// Ok(()) }
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn set_mode(&mut self, mode: u32);
+
+ /// Creates a new instance of `Permissions` from the given set of Unix
+ /// permission bits.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::fs::Permissions;
+ /// use std::os::unix::fs::PermissionsExt;
+ ///
+ /// // Read/write for owner and read for others.
+ /// let permissions = Permissions::from_mode(0o644);
+ /// assert_eq!(permissions.mode(), 0o644);
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn from_mode(mode: u32) -> Self;
+}
+
+#[stable(feature = "fs_ext", since = "1.1.0")]
+impl PermissionsExt for Permissions {
+ fn mode(&self) -> u32 {
+ self.as_inner().mode()
+ }
+
+ fn set_mode(&mut self, mode: u32) {
+ *self = Permissions::from_inner(FromInner::from_inner(mode));
+ }
+
+ fn from_mode(mode: u32) -> Permissions {
+ Permissions::from_inner(FromInner::from_inner(mode))
+ }
+}
+
+/// Unix-specific extensions to [`fs::OpenOptions`].
+///
+/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html
+#[stable(feature = "fs_ext", since = "1.1.0")]
+pub trait OpenOptionsExt {
+ /// Sets the mode bits that a new file will be created with.
+ ///
+ /// If a new file is created as part of a `File::open_opts` call then this
+ /// specified `mode` will be used as the permission bits for the new file.
+ /// If no `mode` is set, the default of `0o666` will be used.
+ /// The operating system masks out bits with the systems `umask`, to produce
+ /// the final permissions.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::OpenOptions;
+ /// use std::os::unix::fs::OpenOptionsExt;
+ ///
+ /// # fn main() {
+ /// let mut options = OpenOptions::new();
+ /// options.mode(0o644); // Give read/write for owner and read for others.
+ /// let file = options.open("foo.txt");
+ /// # }
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn mode(&mut self, mode: u32) -> &mut Self;
+
+ /// Pass custom flags to the `flags` argument of `open`.
+ ///
+ /// The bits that define the access mode are masked out with `O_ACCMODE`, to
+ /// ensure they do not interfere with the access mode set by Rusts options.
+ ///
+ /// Custom flags can only set flags, not remove flags set by Rusts options.
+ /// This options overwrites any previously set custom flags.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// # #![feature(libc)]
+ /// extern crate libc;
+ /// use std::fs::OpenOptions;
+ /// use std::os::unix::fs::OpenOptionsExt;
+ ///
+ /// # fn main() {
+ /// let mut options = OpenOptions::new();
+ /// options.write(true);
+ /// if cfg!(unix) {
+ /// options.custom_flags(libc::O_NOFOLLOW);
+ /// }
+ /// let file = options.open("foo.txt");
+ /// # }
+ /// ```
+ #[stable(feature = "open_options_ext", since = "1.10.0")]
+ fn custom_flags(&mut self, flags: i32) -> &mut Self;
+}
+
+#[stable(feature = "fs_ext", since = "1.1.0")]
+impl OpenOptionsExt for OpenOptions {
+ fn mode(&mut self, mode: u32) -> &mut OpenOptions {
+ self.as_inner_mut().mode(mode); self
+ }
+
+ fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions {
+ self.as_inner_mut().custom_flags(flags); self
+ }
+}
+
+/// Unix-specific extensions to [`fs::Metadata`].
+///
+/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+pub trait MetadataExt {
+ /// Returns the ID of the device containing the file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::io;
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let dev_id = meta.dev();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn dev(&self) -> u64;
+ /// Returns the inode number.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let inode = meta.ino();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn ino(&self) -> u64;
+ /// Returns the rights applied to this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let mode = meta.mode();
+ /// let user_has_write_access = mode & 0o200;
+ /// let user_has_read_write_access = mode & 0o600;
+ /// let group_has_read_access = mode & 0o040;
+ /// let others_have_exec_access = mode & 0o001;
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn mode(&self) -> u32;
+ /// Returns the number of hard links pointing to this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let nb_hard_links = meta.nlink();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn nlink(&self) -> u64;
+ /// Returns the user ID of the owner of this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let user_id = meta.uid();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn uid(&self) -> u32;
+ /// Returns the group ID of the owner of this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let group_id = meta.gid();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn gid(&self) -> u32;
+ /// Returns the device ID of this file (if it is a special one).
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let device_id = meta.rdev();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn rdev(&self) -> u64;
+ /// Returns the total size of this file in bytes.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let file_size = meta.size();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn size(&self) -> u64;
+ /// Returns the time of the last access to the file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let last_access_time = meta.atime();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn atime(&self) -> i64;
+ /// Returns the time of the last access to the file in nanoseconds.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let nano_last_access_time = meta.atime_nsec();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn atime_nsec(&self) -> i64;
+ /// Returns the time of the last modification of the file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let last_modification_time = meta.mtime();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn mtime(&self) -> i64;
+ /// Returns the time of the last modification of the file in nanoseconds.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let nano_last_modification_time = meta.mtime_nsec();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn mtime_nsec(&self) -> i64;
+ /// Returns the time of the last status change of the file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let last_status_change_time = meta.ctime();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn ctime(&self) -> i64;
+ /// Returns the time of the last status change of the file in nanoseconds.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let nano_last_status_change_time = meta.ctime_nsec();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn ctime_nsec(&self) -> i64;
+ /// Returns the blocksize for filesystem I/O.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let blocksize = meta.blksize();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn blksize(&self) -> u64;
+ /// Returns the number of blocks allocated to the file, in 512-byte units.
+ ///
+ /// Please note that this may be smaller than `st_size / 512` when the file has holes.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let blocks = meta.blocks();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn blocks(&self) -> u64;
+}
+
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+impl MetadataExt for fs::Metadata {
+ fn dev(&self) -> u64 { self.st_dev() }
+ fn ino(&self) -> u64 { self.st_ino() }
+ fn mode(&self) -> u32 { self.st_mode() }
+ fn nlink(&self) -> u64 { self.st_nlink() }
+ fn uid(&self) -> u32 { self.st_uid() }
+ fn gid(&self) -> u32 { self.st_gid() }
+ fn rdev(&self) -> u64 { self.st_rdev() }
+ fn size(&self) -> u64 { self.st_size() }
+ fn atime(&self) -> i64 { self.st_atime() }
+ fn atime_nsec(&self) -> i64 { self.st_atime_nsec() }
+ fn mtime(&self) -> i64 { self.st_mtime() }
+ fn mtime_nsec(&self) -> i64 { self.st_mtime_nsec() }
+ fn ctime(&self) -> i64 { self.st_ctime() }
+ fn ctime_nsec(&self) -> i64 { self.st_ctime_nsec() }
+ fn blksize(&self) -> u64 { self.st_blksize() }
+ fn blocks(&self) -> u64 { self.st_blocks() }
+}
+
+/// Unix-specific extensions for [`FileType`].
+///
+/// Adds support for special Unix file types such as block/character devices,
+/// pipes, and sockets.
+///
+/// [`FileType`]: ../../../../std/fs/struct.FileType.html
+#[stable(feature = "file_type_ext", since = "1.5.0")]
+pub trait FileTypeExt {
+ /// Returns whether this file type is a block device.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::FileTypeExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("block_device_file")?;
+ /// let file_type = meta.file_type();
+ /// assert!(file_type.is_block_device());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_block_device(&self) -> bool;
+ /// Returns whether this file type is a char device.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::FileTypeExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("char_device_file")?;
+ /// let file_type = meta.file_type();
+ /// assert!(file_type.is_char_device());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_char_device(&self) -> bool;
+ /// Returns whether this file type is a fifo.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::FileTypeExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("fifo_file")?;
+ /// let file_type = meta.file_type();
+ /// assert!(file_type.is_fifo());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_fifo(&self) -> bool;
+ /// Returns whether this file type is a socket.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::FileTypeExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("unix.socket")?;
+ /// let file_type = meta.file_type();
+ /// assert!(file_type.is_socket());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_socket(&self) -> bool;
+}
+
+#[stable(feature = "file_type_ext", since = "1.5.0")]
+impl FileTypeExt for fs::FileType {
+ fn is_block_device(&self) -> bool { self.as_inner().is(libc::S_IFBLK) }
+ fn is_char_device(&self) -> bool { self.as_inner().is(libc::S_IFCHR) }
+ fn is_fifo(&self) -> bool { self.as_inner().is(libc::S_IFIFO) }
+ fn is_socket(&self) -> bool { self.as_inner().is(libc::S_IFSOCK) }
+}
+
+/// Unix-specific extension methods for [`fs::DirEntry`].
+///
+/// [`fs::DirEntry`]: ../../../../std/fs/struct.DirEntry.html
+#[stable(feature = "dir_entry_ext", since = "1.1.0")]
+pub trait DirEntryExt {
+ /// Returns the underlying `d_ino` field in the contained `dirent`
+ /// structure.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::fs;
+ /// use std::os::unix::fs::DirEntryExt;
+ ///
+ /// if let Ok(entries) = fs::read_dir(".") {
+ /// for entry in entries {
+ /// if let Ok(entry) = entry {
+ /// // Here, `entry` is a `DirEntry`.
+ /// println!("{:?}: {}", entry.file_name(), entry.ino());
+ /// }
+ /// }
+ /// }
+ /// ```
+ #[stable(feature = "dir_entry_ext", since = "1.1.0")]
+ fn ino(&self) -> u64;
+}
+
+#[stable(feature = "dir_entry_ext", since = "1.1.0")]
+impl DirEntryExt for fs::DirEntry {
+ fn ino(&self) -> u64 { 0u64 }
+}
+
+/// Creates a new symbolic link on the filesystem.
+///
+/// The `dst` path will be a symbolic link pointing to the `src` path.
+///
+/// # Note
+///
+/// On Windows, you must specify whether a symbolic link points to a file
+/// or directory. Use `os::windows::fs::symlink_file` to create a
+/// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a
+/// symbolic link to a directory. Additionally, the process must have
+/// `SeCreateSymbolicLinkPrivilege` in order to be able to create a
+/// symbolic link.
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::os::unix::fs;
+///
+/// fn main() -> std::io::Result<()> {
+/// fs::symlink("a.txt", "b.txt")?;
+/// Ok(())
+/// }
+/// ```
+#[stable(feature = "symlink", since = "1.1.0")]
+pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()>
+{
+ sys::fs::symlink(src.as_ref(), dst.as_ref())
+}
+
+/// Unix-specific extensions to [`fs::DirBuilder`].
+///
+/// [`fs::DirBuilder`]: ../../../../std/fs/struct.DirBuilder.html
+#[stable(feature = "dir_builder", since = "1.6.0")]
+pub trait DirBuilderExt {
+ /// Sets the mode to create new directories with. This option defaults to
+ /// 0o777.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::DirBuilder;
+ /// use std::os::unix::fs::DirBuilderExt;
+ ///
+ /// let mut builder = DirBuilder::new();
+ /// builder.mode(0o755);
+ /// ```
+ #[stable(feature = "dir_builder", since = "1.6.0")]
+ fn mode(&mut self, mode: u32) -> &mut Self;
+}
+
+#[stable(feature = "dir_builder", since = "1.6.0")]
+impl DirBuilderExt for fs::DirBuilder {
+ fn mode(&mut self, mode: u32) -> &mut fs::DirBuilder {
+ self.as_inner_mut().set_mode(mode);
+ self
+ }
+}
diff --git a/ctr-std/src/sys/horizon/ext/io.rs b/ctr-std/src/sys/horizon/ext/io.rs
new file mode 100644
index 0000000..c9fe359
--- /dev/null
+++ b/ctr-std/src/sys/horizon/ext/io.rs
@@ -0,0 +1,108 @@
+// 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.
+
+//! Unix-specific extensions to general I/O primitives
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+use fs;
+use os::raw;
+use sys;
+use io;
+use sys_common::{AsInner, FromInner, IntoInner};
+use libc;
+
+/// Raw file descriptors.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub type RawFd = raw::c_int;
+
+/// A trait to extract the raw unix file descriptor from an underlying
+/// object.
+///
+/// This is only available on unix platforms and must be imported in order
+/// to call the method. Windows platforms have a corresponding `AsRawHandle`
+/// and `AsRawSocket` set of traits.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub trait AsRawFd {
+ /// Extracts the raw file descriptor.
+ ///
+ /// This method does **not** pass ownership of the raw file descriptor
+ /// to the caller. The descriptor is only guaranteed to be valid while
+ /// the original object has not yet been destroyed.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn as_raw_fd(&self) -> RawFd;
+}
+
+/// A trait to express the ability to construct an object from a raw file
+/// descriptor.
+#[stable(feature = "from_raw_os", since = "1.1.0")]
+pub trait FromRawFd {
+ /// Constructs a new instance of `Self` from the given raw file
+ /// descriptor.
+ ///
+ /// This function **consumes ownership** of the specified file
+ /// descriptor. The returned object will take responsibility for closing
+ /// it when the 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_fd(fd: RawFd) -> Self;
+}
+
+/// A trait to express the ability to consume an object and acquire ownership of
+/// its raw file descriptor.
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+pub trait IntoRawFd {
+ /// Consumes this object, returning the raw underlying file descriptor.
+ ///
+ /// This function **transfers ownership** of the underlying file descriptor
+ /// to the caller. Callers are then the unique owners of the file descriptor
+ /// and must close the descriptor once it's no longer needed.
+ #[stable(feature = "into_raw_os", since = "1.4.0")]
+ fn into_raw_fd(self) -> RawFd;
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl AsRawFd for fs::File {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_inner().fd().raw()
+ }
+}
+#[stable(feature = "from_raw_os", since = "1.1.0")]
+impl FromRawFd for fs::File {
+ unsafe fn from_raw_fd(fd: RawFd) -> fs::File {
+ fs::File::from_inner(sys::fs::File::from_inner(fd))
+ }
+}
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for fs::File {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_fd().into_raw()
+ }
+}
+
+#[stable(feature = "asraw_stdio", since = "1.21.0")]
+impl AsRawFd for io::Stdin {
+ fn as_raw_fd(&self) -> RawFd { libc::STDIN_FILENO }
+}
+
+#[stable(feature = "asraw_stdio", since = "1.21.0")]
+impl AsRawFd for io::Stdout {
+ fn as_raw_fd(&self) -> RawFd { libc::STDOUT_FILENO }
+}
+
+#[stable(feature = "asraw_stdio", since = "1.21.0")]
+impl AsRawFd for io::Stderr {
+ fn as_raw_fd(&self) -> RawFd { libc::STDERR_FILENO }
+}
diff --git a/ctr-std/src/sys/horizon/ext/mod.rs b/ctr-std/src/sys/horizon/ext/mod.rs
new file mode 100644
index 0000000..49d2a31
--- /dev/null
+++ b/ctr-std/src/sys/horizon/ext/mod.rs
@@ -0,0 +1,59 @@
+// 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.
+
+//! Experimental extensions to `std` for Unix platforms.
+//!
+//! Provides access to platform-level information on Unix platforms, and
+//! exposes Unix-specific functions that would otherwise be inappropriate as
+//! part of the core `std` library.
+//!
+//! It exposes more ways to deal with platform-specific strings (`OsStr`,
+//! `OsString`), allows to set permissions more granularly, extract low-level
+//! file descriptors from files and sockets, and has platform-specific helpers
+//! for spawning processes.
+//!
+//! # Examples
+//!
+//! ```no_run
+//! use std::fs::File;
+//! use std::os::unix::prelude::*;
+//!
+//! fn main() {
+//! let f = File::create("foo.txt").unwrap();
+//! let fd = f.as_raw_fd();
+//!
+//! // use fd with native unix bindings
+//! }
+//! ```
+
+#![stable(feature = "rust1", since = "1.0.0")]
+#![doc(cfg(unix))]
+
+pub mod io;
+pub mod ffi;
+pub mod fs;
+pub mod raw;
+
+/// 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::{RawFd, AsRawFd, FromRawFd, IntoRawFd};
+ #[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::{PermissionsExt, OpenOptionsExt, MetadataExt, FileTypeExt};
+ #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")]
+ pub use super::fs::DirEntryExt;
+ #[doc(no_inline)] #[stable(feature = "file_offset", since = "1.15.0")]
+ pub use super::fs::FileExt;
+}
diff --git a/ctr-std/src/sys/horizon/ext/raw.rs b/ctr-std/src/sys/horizon/ext/raw.rs
new file mode 100644
index 0000000..7972990
--- /dev/null
+++ b/ctr-std/src/sys/horizon/ext/raw.rs
@@ -0,0 +1,33 @@
+// 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.
+
+//! Unix-specific primitives available on all unix platforms
+
+#![stable(feature = "raw_ext", since = "1.1.0")]
+#![rustc_deprecated(since = "1.8.0",
+ reason = "these type aliases are no longer supported by \
+ the standard library, the `libc` crate on \
+ crates.io should be used instead for the correct \
+ definitions")]
+#![allow(deprecated)]
+
+#[stable(feature = "raw_ext", since = "1.1.0")] pub type uid_t = u32;
+#[stable(feature = "raw_ext", since = "1.1.0")] pub type gid_t = u32;
+#[stable(feature = "raw_ext", since = "1.1.0")] pub type pid_t = i32;
+
+#[doc(inline)]
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub use sys::platform::raw::pthread_t;
+#[doc(inline)]
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub use sys::platform::raw::{dev_t, ino_t, mode_t, nlink_t, off_t, blksize_t};
+#[doc(inline)]
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub use sys::platform::raw::{blkcnt_t, time_t};
diff --git a/ctr-std/src/sys/horizon/fast_thread_local.rs b/ctr-std/src/sys/horizon/fast_thread_local.rs
new file mode 100644
index 0000000..6cdbe5d
--- /dev/null
+++ b/ctr-std/src/sys/horizon/fast_thread_local.rs
@@ -0,0 +1,77 @@
+// 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.
+
+#![cfg(target_thread_local)]
+#![unstable(feature = "thread_local_internals", issue = "0")]
+
+// Since what appears to be glibc 2.18 this symbol has been shipped which
+// GCC and clang both use to invoke destructors in thread_local globals, so
+// let's do the same!
+//
+// Note, however, that we run on lots older linuxes, as well as cross
+// compiling from a newer linux to an older linux, so we also have a
+// fallback implementation to use as well.
+//
+// Due to rust-lang/rust#18804, make sure this is not generic!
+#[cfg(target_os = "linux")]
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
+ use libc;
+ use mem;
+ use sys_common::thread_local::register_dtor_fallback;
+
+ extern {
+ #[linkage = "extern_weak"]
+ static __dso_handle: *mut u8;
+ #[linkage = "extern_weak"]
+ static __cxa_thread_atexit_impl: *const libc::c_void;
+ }
+ if !__cxa_thread_atexit_impl.is_null() {
+ type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8),
+ arg: *mut u8,
+ dso_handle: *mut u8) -> libc::c_int;
+ mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)
+ (dtor, t, &__dso_handle as *const _ as *mut _);
+ return
+ }
+ register_dtor_fallback(t, dtor);
+}
+
+// macOS's analog of the above linux function is this _tlv_atexit function.
+// The disassembly of thread_local globals in C++ (at least produced by
+// clang) will have this show up in the output.
+#[cfg(target_os = "macos")]
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
+ extern {
+ fn _tlv_atexit(dtor: unsafe extern fn(*mut u8),
+ arg: *mut u8);
+ }
+ _tlv_atexit(dtor, t);
+}
+
+// Just use the thread_local fallback implementation, at least until there's
+// a more direct implementation.
+#[cfg(target_os = "fuchsia")]
+pub use sys_common::thread_local::register_dtor_fallback as register_dtor;
+
+pub fn requires_move_before_drop() -> bool {
+ // The macOS implementation of TLS apparently had an odd aspect to it
+ // where the pointer we have may be overwritten while this destructor
+ // is running. Specifically if a TLS destructor re-accesses TLS it may
+ // trigger a re-initialization of all TLS variables, paving over at
+ // least some destroyed ones with initial values.
+ //
+ // This means that if we drop a TLS value in place on macOS that we could
+ // revert the value to its original state halfway through the
+ // destructor, which would be bad!
+ //
+ // Hence, we use `ptr::read` on macOS (to move to a "safe" location)
+ // instead of drop_in_place.
+ cfg!(target_os = "macos")
+}
diff --git a/ctr-std/src/sys/horizon/fd.rs b/ctr-std/src/sys/horizon/fd.rs
new file mode 100644
index 0000000..8925e72
--- /dev/null
+++ b/ctr-std/src/sys/horizon/fd.rs
@@ -0,0 +1,265 @@
+// 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(reason = "not public", issue = "0", feature = "fd")]
+
+use cmp;
+use io::{self, Read};
+use libc::{self, c_int, c_void, ssize_t};
+use mem;
+use sync::atomic::{AtomicBool, Ordering};
+use sys::cvt;
+use sys_common::AsInner;
+
+#[derive(Debug)]
+pub struct FileDesc {
+ fd: c_int,
+}
+
+fn max_len() -> usize {
+ // The maximum read limit on most posix-like systems is `SSIZE_MAX`,
+ // with the man page quoting that if the count of bytes to read is
+ // greater than `SSIZE_MAX` the result is "unspecified".
+ //
+ // On macOS, however, apparently the 64-bit libc is either buggy or
+ // intentionally showing odd behavior by rejecting any read with a size
+ // larger than or equal to INT_MAX. To handle both of these the read
+ // size is capped on both platforms.
+ if cfg!(target_os = "macos") {
+ <c_int>::max_value() as usize - 1
+ } else {
+ <ssize_t>::max_value() as usize
+ }
+}
+
+impl FileDesc {
+ pub fn new(fd: c_int) -> FileDesc {
+ FileDesc { fd: fd }
+ }
+
+ pub fn raw(&self) -> c_int { self.fd }
+
+ /// Extracts the actual filedescriptor without closing it.
+ pub fn into_raw(self) -> c_int {
+ let fd = self.fd;
+ mem::forget(self);
+ fd
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ libc::read(self.fd,
+ buf.as_mut_ptr() as *mut c_void,
+ cmp::min(buf.len(), max_len()))
+ })?;
+ Ok(ret as usize)
+ }
+
+ 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 read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
+ #[cfg(target_os = "android")]
+ use super::android::cvt_pread64;
+
+ #[cfg(target_os = "emscripten")]
+ unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64)
+ -> io::Result<isize>
+ {
+ use convert::TryInto;
+ use libc::pread64;
+ // pread64 on emscripten actually takes a 32 bit offset
+ if let Ok(o) = offset.try_into() {
+ cvt(pread64(fd, buf, count, o))
+ } else {
+ Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot pread >2GB"))
+ }
+ }
+
+ #[cfg(not(any(target_os = "android", target_os = "emscripten")))]
+ unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64)
+ -> io::Result<isize>
+ {
+ #[cfg(target_os = "linux")]
+ use libc::pread64;
+ #[cfg(not(target_os = "linux"))]
+ use libc::pread as pread64;
+ cvt(pread64(fd, buf, count, offset))
+ }
+
+ unsafe {
+ cvt_pread64(self.fd,
+ buf.as_mut_ptr() as *mut c_void,
+ cmp::min(buf.len(), max_len()),
+ offset as i64)
+ .map(|n| n as usize)
+ }
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ libc::write(self.fd,
+ buf.as_ptr() as *const c_void,
+ cmp::min(buf.len(), max_len()))
+ })?;
+ Ok(ret as usize)
+ }
+
+ pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
+ #[cfg(target_os = "android")]
+ use super::android::cvt_pwrite64;
+
+ #[cfg(target_os = "emscripten")]
+ unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64)
+ -> io::Result<isize>
+ {
+ use convert::TryInto;
+ use libc::pwrite64;
+ // pwrite64 on emscripten actually takes a 32 bit offset
+ if let Ok(o) = offset.try_into() {
+ cvt(pwrite64(fd, buf, count, o))
+ } else {
+ Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot pwrite >2GB"))
+ }
+ }
+
+ #[cfg(not(any(target_os = "android", target_os = "emscripten")))]
+ unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64)
+ -> io::Result<isize>
+ {
+ #[cfg(target_os = "linux")]
+ use libc::pwrite64;
+ #[cfg(not(target_os = "linux"))]
+ use libc::pwrite as pwrite64;
+ cvt(pwrite64(fd, buf, count, offset))
+ }
+
+ unsafe {
+ cvt_pwrite64(self.fd,
+ buf.as_ptr() as *const c_void,
+ cmp::min(buf.len(), max_len()),
+ offset as i64)
+ .map(|n| n as usize)
+ }
+ }
+
+ // We don't have fork/exec on the 3DS, so this shouldn't need to do anything
+ #[cfg(target_os = "horizon")]
+ pub fn set_cloexec(&self) -> io::Result<()> {
+ Ok(())
+ }
+
+ #[cfg(target_os = "linux")]
+ pub fn get_cloexec(&self) -> io::Result<bool> {
+ unsafe {
+ Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0)
+ }
+ }
+
+ #[cfg(target_os = "linux")]
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ unsafe {
+ let v = nonblocking as c_int;
+ cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
+ Ok(())
+ }
+ }
+
+ #[cfg(not(target_os = "linux"))]
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ unsafe {
+ let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
+ let new = if nonblocking {
+ previous | libc::O_NONBLOCK
+ } else {
+ previous & !libc::O_NONBLOCK
+ };
+ if new != previous {
+ cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?;
+ }
+ Ok(())
+ }
+ }
+
+ pub fn duplicate(&self) -> io::Result<FileDesc> {
+ // We want to atomically duplicate this file descriptor and set the
+ // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
+ // flag, however, isn't supported on older Linux kernels (earlier than
+ // 2.6.24).
+ //
+ // To detect this and ensure that CLOEXEC is still set, we
+ // follow a strategy similar to musl [1] where if passing
+ // F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not
+ // supported (the third parameter, 0, is always valid), so we stop
+ // trying that.
+ //
+ // Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to
+ // resolve so we at least compile this.
+ //
+ // [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963
+ #[cfg(any(target_os = "android", target_os = "haiku"))]
+ use libc::F_DUPFD as F_DUPFD_CLOEXEC;
+ #[cfg(not(any(target_os = "android", target_os="haiku")))]
+ use libc::F_DUPFD_CLOEXEC;
+
+ let make_filedesc = |fd| {
+ let fd = FileDesc::new(fd);
+ fd.set_cloexec()?;
+ Ok(fd)
+ };
+ static TRY_CLOEXEC: AtomicBool =
+ AtomicBool::new(!cfg!(target_os = "android"));
+ let fd = self.raw();
+ if TRY_CLOEXEC.load(Ordering::Relaxed) {
+ match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) {
+ // We *still* call the `set_cloexec` method as apparently some
+ // linux kernel at some point stopped setting CLOEXEC even
+ // though it reported doing so on F_DUPFD_CLOEXEC.
+ Ok(fd) => {
+ return Ok(if cfg!(target_os = "linux") {
+ make_filedesc(fd)?
+ } else {
+ FileDesc::new(fd)
+ })
+ }
+ Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {
+ TRY_CLOEXEC.store(false, Ordering::Relaxed);
+ }
+ Err(e) => return Err(e),
+ }
+ }
+ cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).and_then(make_filedesc)
+ }
+}
+
+impl<'a> Read for &'a FileDesc {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ (**self).read(buf)
+ }
+}
+
+impl AsInner<c_int> for FileDesc {
+ fn as_inner(&self) -> &c_int { &self.fd }
+}
+
+impl Drop for FileDesc {
+ fn drop(&mut self) {
+ // Note that errors are ignored when closing a file descriptor. The
+ // reason for this is that if an error occurs we don't actually know if
+ // the file descriptor was closed or not, and if we retried (for
+ // something like EINTR), we might close another valid file descriptor
+ // (opened after we closed ours.
+ let _ = unsafe { libc::close(self.fd) };
+ }
+}
diff --git a/ctr-std/src/sys/horizon/fs.rs b/ctr-std/src/sys/horizon/fs.rs
new file mode 100644
index 0000000..f370d9c
--- /dev/null
+++ b/ctr-std/src/sys/horizon/fs.rs
@@ -0,0 +1,928 @@
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <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::unix::prelude::*;
+
+use ffi::{CString, CStr, OsString, OsStr};
+use fmt;
+use io::{self, Error, ErrorKind, SeekFrom};
+use libc::{self, c_int, mode_t};
+use mem;
+use path::{Path, PathBuf};
+use ptr;
+use sync::Arc;
+use sys::fd::FileDesc;
+use sys::time::SystemTime;
+use sys::{cvt, cvt_r};
+use sys_common::{AsInner, FromInner};
+
+#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
+use libc::{stat64, fstat64, lstat64, off64_t, ftruncate64, lseek64, dirent64, readdir64_r, open64};
+#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
+use libc::{fstatat, dirfd};
+#[cfg(target_os = "android")]
+use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, lseek64,
+ dirent as dirent64, open as open64};
+#[cfg(not(any(target_os = "linux",
+ target_os = "emscripten",
+ target_os = "l4re",
+ target_os = "android")))]
+use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, off_t as off64_t,
+ ftruncate as ftruncate64, lseek as lseek64, dirent as dirent64, open as open64};
+#[cfg(not(any(target_os = "linux",
+ target_os = "emscripten",
+ target_os = "solaris",
+ target_os = "l4re",
+ target_os = "fuchsia")))]
+use libc::{readdir_r as readdir64_r};
+
+pub struct File(FileDesc);
+
+#[derive(Clone)]
+pub struct FileAttr {
+ stat: stat64,
+}
+
+// all DirEntry's will have a reference to this struct
+struct InnerReadDir {
+ dirp: Dir,
+ root: PathBuf,
+}
+
+#[derive(Clone)]
+pub struct ReadDir(Arc<InnerReadDir>);
+
+struct Dir(*mut libc::DIR);
+
+unsafe impl Send for Dir {}
+unsafe impl Sync for Dir {}
+
+pub struct DirEntry {
+ entry: dirent64,
+ dir: ReadDir,
+ // We need to store an owned copy of the entry name
+ // on Solaris and Fuchsia because a) it uses a zero-length
+ // array to store the name, b) its lifetime between readdir
+ // calls is not guaranteed.
+ #[cfg(any(target_os = "solaris", target_os = "fuchsia"))]
+ name: Box<[u8]>
+}
+
+#[derive(Clone, Debug)]
+pub struct OpenOptions {
+ // generic
+ read: bool,
+ write: bool,
+ append: bool,
+ truncate: bool,
+ create: bool,
+ create_new: bool,
+ // system-specific
+ custom_flags: i32,
+ mode: mode_t,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct FilePermissions { mode: mode_t }
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub struct FileType { mode: mode_t }
+
+#[derive(Debug)]
+pub struct DirBuilder { mode: mode_t }
+
+impl FileAttr {
+ pub fn size(&self) -> u64 { self.stat.st_size as u64 }
+ pub fn perm(&self) -> FilePermissions {
+ FilePermissions { mode: (self.stat.st_mode as mode_t) }
+ }
+
+ pub fn file_type(&self) -> FileType {
+ FileType { mode: self.stat.st_mode as mode_t }
+ }
+}
+
+#[cfg(target_os = "netbsd")]
+impl FileAttr {
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_mtime as libc::time_t,
+ tv_nsec: self.stat.st_mtimensec as libc::c_long,
+ }))
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_atime as libc::time_t,
+ tv_nsec: self.stat.st_atimensec as libc::c_long,
+ }))
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_birthtime as libc::time_t,
+ tv_nsec: self.stat.st_birthtimensec as libc::c_long,
+ }))
+ }
+}
+
+#[cfg(not(target_os = "netbsd"))]
+impl FileAttr {
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_mtime as libc::time_t,
+ tv_nsec: 0 as _,
+ }))
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_atime as libc::time_t,
+ tv_nsec: 0 as _,
+ }))
+ }
+
+ #[cfg(any(target_os = "bitrig",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "macos",
+ target_os = "ios"))]
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_birthtime as libc::time_t,
+ tv_nsec: self.stat.st_birthtime_nsec as libc::c_long,
+ }))
+ }
+
+ #[cfg(not(any(target_os = "bitrig",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "macos",
+ target_os = "ios")))]
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Err(io::Error::new(io::ErrorKind::Other,
+ "creation time is not available on this platform \
+ currently"))
+ }
+}
+
+impl AsInner<stat64> for FileAttr {
+ fn as_inner(&self) -> &stat64 { &self.stat }
+}
+
+impl FilePermissions {
+ pub fn readonly(&self) -> bool {
+ // check if any class (owner, group, others) has write permission
+ self.mode & 0o222 == 0
+ }
+
+ pub fn set_readonly(&mut self, readonly: bool) {
+ if readonly {
+ // remove write permission for all classes; equivalent to `chmod a-w <file>`
+ self.mode &= !0o222;
+ } else {
+ // add write permission for all classes; equivalent to `chmod a+w <file>`
+ self.mode |= 0o222;
+ }
+ }
+ pub fn mode(&self) -> u32 { self.mode as u32 }
+}
+
+impl FileType {
+ pub fn is_dir(&self) -> bool { self.is(libc::S_IFDIR) }
+ pub fn is_file(&self) -> bool { self.is(libc::S_IFREG) }
+ pub fn is_symlink(&self) -> bool { self.is(libc::S_IFLNK) }
+
+ pub fn is(&self, mode: mode_t) -> bool { self.mode & libc::S_IFMT == mode }
+}
+
+impl FromInner<u32> for FilePermissions {
+ fn from_inner(mode: u32) -> FilePermissions {
+ FilePermissions { mode: mode as mode_t }
+ }
+}
+
+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("/home")'
+ fmt::Debug::fmt(&*self.0.root, f)
+ }
+}
+
+impl Iterator for ReadDir {
+ type Item = io::Result<DirEntry>;
+
+ #[cfg(any(target_os = "solaris", target_os = "fuchsia"))]
+ fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ unsafe {
+ loop {
+ // Although readdir_r(3) would be a correct function to use here because
+ // of the thread safety, on Illumos and Fuchsia the readdir(3C) function
+ // is safe to use in threaded applications and it is generally preferred
+ // over the readdir_r(3C) function.
+ super::os::set_errno(0);
+ let entry_ptr = libc::readdir(self.0.dirp.0);
+ if entry_ptr.is_null() {
+ // NULL can mean either the end is reached or an error occurred.
+ // So we had to clear errno beforehand to check for an error now.
+ return match super::os::errno() {
+ 0 => None,
+ e => Some(Err(Error::from_raw_os_error(e))),
+ }
+ }
+
+ let name = (*entry_ptr).d_name.as_ptr() as *const u8;
+ let namelen = libc::strlen(name) as usize;
+
+ let ret = DirEntry {
+ entry: *entry_ptr,
+ name: ::slice::from_raw_parts(name as *const u8,
+ namelen as usize).to_owned().into_boxed_slice(),
+ dir: self.clone()
+ };
+ if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
+ return Some(Ok(ret))
+ }
+ }
+ }
+ }
+
+ #[cfg(not(any(target_os = "solaris", target_os = "fuchsia")))]
+ fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ unsafe {
+ let mut ret = DirEntry {
+ entry: mem::zeroed(),
+ dir: self.clone(),
+ };
+ let mut entry_ptr = ptr::null_mut();
+ loop {
+ if readdir64_r(self.0.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 {
+ return Some(Err(Error::last_os_error()))
+ }
+ if entry_ptr.is_null() {
+ return None
+ }
+ if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
+ return Some(Ok(ret))
+ }
+ }
+ }
+ }
+}
+
+impl Drop for Dir {
+ fn drop(&mut self) {
+ let r = unsafe { libc::closedir(self.0) };
+ debug_assert_eq!(r, 0);
+ }
+}
+
+impl DirEntry {
+ pub fn path(&self) -> PathBuf {
+ self.dir.0.root.join(OsStr::from_bytes(self.name_bytes()))
+ }
+
+ pub fn file_name(&self) -> OsString {
+ OsStr::from_bytes(self.name_bytes()).to_os_string()
+ }
+
+ #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
+ pub fn metadata(&self) -> io::Result<FileAttr> {
+ let fd = cvt(unsafe {dirfd(self.dir.0.dirp.0)})?;
+ let mut stat: stat64 = unsafe { mem::zeroed() };
+ cvt(unsafe {
+ fstatat(fd,
+ self.entry.d_name.as_ptr() as *const u8,
+ &mut stat as *mut _ as *mut _,
+ libc::AT_SYMLINK_NOFOLLOW)
+ })?;
+ Ok(FileAttr { stat: stat })
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))]
+ pub fn metadata(&self) -> io::Result<FileAttr> {
+ lstat(&self.path())
+ }
+
+ #[cfg(any(target_os = "solaris", target_os = "haiku"))]
+ pub fn file_type(&self) -> io::Result<FileType> {
+ lstat(&self.path()).map(|m| m.file_type())
+ }
+
+ #[cfg(not(any(target_os = "solaris", target_os = "haiku")))]
+ pub fn file_type(&self) -> io::Result<FileType> {
+ match self.entry.d_type {
+ libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }),
+ libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }),
+ libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }),
+ libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }),
+ libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }),
+ libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }),
+ libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }),
+ _ => lstat(&self.path()).map(|m| m.file_type()),
+ }
+ }
+
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "android",
+ target_os = "solaris",
+ target_os = "haiku",
+ target_os = "l4re",
+ target_os = "fuchsia"))]
+ pub fn ino(&self) -> u64 {
+ self.entry.d_ino as u64
+ }
+
+ #[cfg(any(target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "bitrig",
+ target_os = "netbsd",
+ target_os = "dragonfly"))]
+ pub fn ino(&self) -> u64 {
+ self.entry.d_fileno as u64
+ }
+
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "bitrig"))]
+ fn name_bytes(&self) -> &[u8] {
+ unsafe {
+ ::slice::from_raw_parts(self.entry.d_name.as_ptr() as *const u8 as *const u8,
+ self.entry.d_namlen as usize)
+ }
+ }
+ #[cfg(any(target_os = "android",
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "l4re",
+ target_os = "haiku",
+ target_os = "horizon"))]
+ fn name_bytes(&self) -> &[u8] {
+ unsafe {
+ CStr::from_ptr(self.entry.d_name.as_ptr() as *const i8).to_bytes()
+ }
+ }
+ #[cfg(any(target_os = "solaris",
+ target_os = "fuchsia"))]
+ fn name_bytes(&self) -> &[u8] {
+ &*self.name
+ }
+}
+
+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,
+ mode: 0o666,
+ }
+ }
+
+ pub fn read(&mut self, read: bool) { self.read = read; }
+ pub fn write(&mut self, write: bool) { self.write = write; }
+ pub fn append(&mut self, append: bool) { self.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: i32) { self.custom_flags = flags; }
+ pub fn mode(&mut self, mode: u32) { self.mode = mode as mode_t; }
+
+ fn get_access_mode(&self) -> io::Result<c_int> {
+ match (self.read, self.write, self.append) {
+ (true, false, false) => Ok(libc::O_RDONLY),
+ (false, true, false) => Ok(libc::O_WRONLY),
+ (true, true, false) => Ok(libc::O_RDWR),
+ (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND),
+ (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND),
+ (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)),
+ }
+ }
+
+ fn get_creation_mode(&self) -> io::Result<c_int> {
+ match (self.write, self.append) {
+ (true, false) => {}
+ (false, false) =>
+ if self.truncate || self.create || self.create_new {
+ return Err(Error::from_raw_os_error(libc::EINVAL));
+ },
+ (_, true) =>
+ if self.truncate && !self.create_new {
+ return Err(Error::from_raw_os_error(libc::EINVAL));
+ },
+ }
+
+ Ok(match (self.create, self.truncate, self.create_new) {
+ (false, false, false) => 0,
+ (true, false, false) => libc::O_CREAT,
+ (false, true, false) => libc::O_TRUNC,
+ (true, true, false) => libc::O_CREAT | libc::O_TRUNC,
+ (_, _, true) => libc::O_CREAT | libc::O_EXCL,
+ })
+ }
+}
+
+impl File {
+ pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+ let path = cstr(path)?;
+ File::open_c(&path, opts)
+ }
+
+ pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
+ let flags = libc::O_CLOEXEC |
+ opts.get_access_mode()? |
+ opts.get_creation_mode()? |
+ (opts.custom_flags as c_int & !libc::O_ACCMODE);
+ let fd = cvt_r(|| unsafe {
+ open64(path.as_ptr() as *const u8, flags, opts.mode as c_int)
+ })?;
+ let fd = FileDesc::new(fd);
+
+ // Currently the standard library supports Linux 2.6.18 which did not
+ // have the O_CLOEXEC flag (passed above). If we're running on an older
+ // Linux kernel then the flag is just ignored by the OS. After we open
+ // the first file, we check whether it has CLOEXEC set. If it doesn't,
+ // we will explicitly ask for a CLOEXEC fd for every further file we
+ // open, if it does, we will skip that step.
+ //
+ // The CLOEXEC flag, however, is supported on versions of macOS/BSD/etc
+ // that we support, so we only do this on Linux currently.
+ #[cfg(target_os = "linux")]
+ fn ensure_cloexec(fd: &FileDesc) -> io::Result<()> {
+ use sync::atomic::{AtomicUsize, Ordering};
+
+ const OPEN_CLOEXEC_UNKNOWN: usize = 0;
+ const OPEN_CLOEXEC_SUPPORTED: usize = 1;
+ const OPEN_CLOEXEC_NOTSUPPORTED: usize = 2;
+ static OPEN_CLOEXEC: AtomicUsize = AtomicUsize::new(OPEN_CLOEXEC_UNKNOWN);
+
+ let need_to_set;
+ match OPEN_CLOEXEC.load(Ordering::Relaxed) {
+ OPEN_CLOEXEC_UNKNOWN => {
+ need_to_set = !fd.get_cloexec()?;
+ OPEN_CLOEXEC.store(if need_to_set {
+ OPEN_CLOEXEC_NOTSUPPORTED
+ } else {
+ OPEN_CLOEXEC_SUPPORTED
+ }, Ordering::Relaxed);
+ },
+ OPEN_CLOEXEC_SUPPORTED => need_to_set = false,
+ OPEN_CLOEXEC_NOTSUPPORTED => need_to_set = true,
+ _ => unreachable!(),
+ }
+ if need_to_set {
+ fd.set_cloexec()?;
+ }
+ Ok(())
+ }
+
+ #[cfg(not(target_os = "linux"))]
+ fn ensure_cloexec(_: &FileDesc) -> io::Result<()> {
+ Ok(())
+ }
+
+ ensure_cloexec(&fd)?;
+ Ok(File(fd))
+ }
+
+ pub fn file_attr(&self) -> io::Result<FileAttr> {
+ let mut stat: stat64 = unsafe { mem::zeroed() };
+ cvt(unsafe {
+ fstat64(self.0.raw(), &mut stat)
+ })?;
+ Ok(FileAttr { stat: stat })
+ }
+
+ pub fn fsync(&self) -> io::Result<()> {
+ cvt_r(|| unsafe { libc::fsync(self.0.raw()) })?;
+ Ok(())
+ }
+
+ pub fn datasync(&self) -> io::Result<()> {
+ cvt_r(|| unsafe { os_datasync(self.0.raw()) })?;
+ return Ok(());
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ unsafe fn os_datasync(fd: c_int) -> c_int {
+ libc::fcntl(fd, libc::F_FULLFSYNC)
+ }
+ #[cfg(target_os = "linux")]
+ unsafe fn os_datasync(fd: c_int) -> c_int { libc::fdatasync(fd) }
+ #[cfg(not(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "linux")))]
+ unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) }
+ }
+
+ pub fn truncate(&self, size: u64) -> io::Result<()> {
+ #[cfg(target_os = "android")]
+ return ::sys::android::ftruncate64(self.0.raw(), size);
+
+ #[cfg(not(target_os = "android"))]
+ return cvt_r(|| unsafe {
+ ftruncate64(self.0.raw(), size as off64_t)
+ }).map(|_| ());
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+
+ pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
+ self.0.read_at(buf, offset)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
+ self.0.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, too large values will end up as
+ // negative which will cause an error in `lseek64`.
+ SeekFrom::Start(off) => (libc::SEEK_SET, off as i64),
+ SeekFrom::End(off) => (libc::SEEK_END, off),
+ SeekFrom::Current(off) => (libc::SEEK_CUR, off),
+ };
+ #[cfg(target_os = "emscripten")]
+ let pos = pos as i32;
+ let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?;
+ Ok(n as u64)
+ }
+
+ pub fn duplicate(&self) -> io::Result<File> {
+ self.0.duplicate().map(File)
+ }
+
+ pub fn fd(&self) -> &FileDesc { &self.0 }
+
+ pub fn into_fd(self) -> FileDesc { self.0 }
+
+ pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
+ cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?;
+ Ok(())
+ }
+}
+
+impl DirBuilder {
+ pub fn new() -> DirBuilder {
+ DirBuilder { mode: 0o777 }
+ }
+
+ pub fn mkdir(&self, p: &Path) -> io::Result<()> {
+ let p = cstr(p)?;
+ cvt(unsafe { libc::mkdir(p.as_ptr() as *const u8, self.mode) })?;
+ Ok(())
+ }
+
+ pub fn set_mode(&mut self, mode: u32) {
+ self.mode = mode as mode_t;
+ }
+}
+
+fn cstr(path: &Path) -> io::Result<CString> {
+ Ok(CString::new(path.as_os_str().as_bytes())?)
+}
+
+impl FromInner<c_int> for File {
+ fn from_inner(fd: c_int) -> File {
+ File(FileDesc::new(fd))
+ }
+}
+
+impl fmt::Debug for File {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ #[cfg(target_os = "linux")]
+ fn get_path(fd: c_int) -> Option<PathBuf> {
+ let mut p = PathBuf::from("/proc/self/fd");
+ p.push(&fd.to_string());
+ readlink(&p).ok()
+ }
+
+ #[cfg(target_os = "macos")]
+ fn get_path(fd: c_int) -> Option<PathBuf> {
+ // FIXME: The use of PATH_MAX is generally not encouraged, but it
+ // is inevitable in this case because macOS defines `fcntl` with
+ // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
+ // alternatives. If a better method is invented, it should be used
+ // instead.
+ let mut buf = vec![0;libc::PATH_MAX as usize];
+ let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr() as *const u8) };
+ if n == -1 {
+ return None;
+ }
+ let l = buf.iter().position(|&c| c == 0).unwrap();
+ buf.truncate(l as usize);
+ buf.shrink_to_fit();
+ Some(PathBuf::from(OsString::from_vec(buf)))
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "macos")))]
+ fn get_path(_fd: c_int) -> Option<PathBuf> {
+ // FIXME(#24570): implement this for other Unix platforms
+ None
+ }
+
+ #[cfg(any(target_os = "linux", target_os = "macos"))]
+ fn get_mode(fd: c_int) -> Option<(bool, bool)> {
+ let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
+ if mode == -1 {
+ return None;
+ }
+ match mode & libc::O_ACCMODE {
+ libc::O_RDONLY => Some((true, false)),
+ libc::O_RDWR => Some((true, true)),
+ libc::O_WRONLY => Some((false, true)),
+ _ => None
+ }
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "macos")))]
+ fn get_mode(_fd: c_int) -> Option<(bool, bool)> {
+ // FIXME(#24570): implement this for other Unix platforms
+ None
+ }
+
+ let fd = self.0.raw();
+ let mut b = f.debug_struct("File");
+ b.field("fd", &fd);
+ if let Some(path) = get_path(fd) {
+ b.field("path", &path);
+ }
+ if let Some((read, write)) = get_mode(fd) {
+ b.field("read", &read).field("write", &write);
+ }
+ b.finish()
+ }
+}
+
+pub fn readdir(p: &Path) -> io::Result<ReadDir> {
+ let root = p.to_path_buf();
+ let p = cstr(p)?;
+ unsafe {
+ let ptr = libc::opendir(p.as_ptr() as *const u8);
+ if ptr.is_null() {
+ Err(Error::last_os_error())
+ } else {
+ let inner = InnerReadDir { dirp: Dir(ptr), root };
+ Ok(ReadDir(Arc::new(inner)))
+ }
+ }
+}
+
+pub fn unlink(p: &Path) -> io::Result<()> {
+ let p = cstr(p)?;
+ cvt(unsafe { libc::unlink(p.as_ptr() as *const u8) })?;
+ Ok(())
+}
+
+pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
+ let old = cstr(old)?;
+ let new = cstr(new)?;
+ cvt(unsafe { libc::rename(old.as_ptr() as *const u8, new.as_ptr() as *const u8) })?;
+ Ok(())
+}
+
+pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
+ let p = cstr(p)?;
+ cvt_r(|| unsafe { libc::chmod(p.as_ptr() as *const u8, perm.mode) })?;
+ Ok(())
+}
+
+pub fn rmdir(p: &Path) -> io::Result<()> {
+ let p = cstr(p)?;
+ cvt(unsafe { libc::rmdir(p.as_ptr() as *const u8) })?;
+ Ok(())
+}
+
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+ let filetype = lstat(path)?.file_type();
+ if filetype.is_symlink() {
+ unlink(path)
+ } else {
+ remove_dir_all_recursive(path)
+ }
+}
+
+fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
+ for child in readdir(path)? {
+ let child = child?;
+ if child.file_type()?.is_dir() {
+ remove_dir_all_recursive(&child.path())?;
+ } else {
+ unlink(&child.path())?;
+ }
+ }
+ rmdir(path)
+}
+
+pub fn readlink(p: &Path) -> io::Result<PathBuf> {
+ let c_path = cstr(p)?;
+ let p = c_path.as_ptr() as *const u8;
+
+ let mut buf = Vec::with_capacity(256);
+
+ loop {
+ let buf_read = cvt(unsafe {
+ libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity())
+ })? as usize;
+
+ unsafe { buf.set_len(buf_read); }
+
+ if buf_read != buf.capacity() {
+ buf.shrink_to_fit();
+
+ return Ok(PathBuf::from(OsString::from_vec(buf)));
+ }
+
+ // Trigger the internal buffer resizing logic of `Vec` by requiring
+ // more space than the current capacity. The length is guaranteed to be
+ // the same as the capacity due to the if statement above.
+ buf.reserve(1);
+ }
+}
+
+pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
+ let src = cstr(src)?;
+ let dst = cstr(dst)?;
+ cvt(unsafe { libc::symlink(src.as_ptr() as *const u8, dst.as_ptr() as *const u8) })?;
+ Ok(())
+}
+
+pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
+ let src = cstr(src)?;
+ let dst = cstr(dst)?;
+ cvt(unsafe { libc::link(src.as_ptr() as *const u8, dst.as_ptr() as *const u8) })?;
+ Ok(())
+}
+
+pub fn stat(p: &Path) -> io::Result<FileAttr> {
+ let p = cstr(p)?;
+ let mut stat: stat64 = unsafe { mem::zeroed() };
+ cvt(unsafe {
+ stat64(p.as_ptr() as *const u8, &mut stat as *mut _ as *mut _)
+ })?;
+ Ok(FileAttr { stat: stat })
+}
+
+pub fn lstat(p: &Path) -> io::Result<FileAttr> {
+ let p = cstr(p)?;
+ let mut stat: stat64 = unsafe { mem::zeroed() };
+ cvt(unsafe {
+ lstat64(p.as_ptr() as *const u8, &mut stat as *mut _ as *mut _)
+ })?;
+ Ok(FileAttr { stat: stat })
+}
+
+pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
+ let path = CString::new(p.as_os_str().as_bytes())?;
+ let buf;
+ unsafe {
+ let r = libc::realpath(path.as_ptr() as *const u8, ptr::null_mut());
+ if r.is_null() {
+ return Err(io::Error::last_os_error())
+ }
+ buf = CStr::from_ptr(r as *const i8).to_bytes().to_vec();
+ libc::free(r as *mut _);
+ }
+ Ok(PathBuf::from(OsString::from_vec(buf)))
+}
+
+#[cfg(not(any(target_os = "linux", target_os = "android")))]
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+ use fs::File;
+ if !from.is_file() {
+ return Err(Error::new(ErrorKind::InvalidInput,
+ "the source path is not an existing regular file"))
+ }
+
+ let mut reader = File::open(from)?;
+ let mut writer = File::create(to)?;
+ let perm = reader.metadata()?.permissions();
+
+ let ret = io::copy(&mut reader, &mut writer)?;
+ writer.set_permissions(perm)?;
+ Ok(ret)
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+ use cmp;
+ use fs::File;
+ use sync::atomic::{AtomicBool, Ordering};
+
+ // Kernel prior to 4.5 don't have copy_file_range
+ // We store the availability in a global to avoid unneccessary syscalls
+ static HAS_COPY_FILE_RANGE: AtomicBool = AtomicBool::new(true);
+
+ unsafe fn copy_file_range(
+ fd_in: libc::c_int,
+ off_in: *mut libc::loff_t,
+ fd_out: libc::c_int,
+ off_out: *mut libc::loff_t,
+ len: libc::size_t,
+ flags: libc::c_uint,
+ ) -> libc::c_long {
+ libc::syscall(
+ libc::SYS_copy_file_range,
+ fd_in,
+ off_in,
+ fd_out,
+ off_out,
+ len,
+ flags,
+ )
+ }
+
+ if !from.is_file() {
+ return Err(Error::new(ErrorKind::InvalidInput,
+ "the source path is not an existing regular file"))
+ }
+
+ let mut reader = File::open(from)?;
+ let mut writer = File::create(to)?;
+ let (perm, len) = {
+ let metadata = reader.metadata()?;
+ (metadata.permissions(), metadata.size())
+ };
+
+ let has_copy_file_range = HAS_COPY_FILE_RANGE.load(Ordering::Relaxed);
+ let mut written = 0u64;
+ while written < len {
+ let copy_result = if has_copy_file_range {
+ let bytes_to_copy = cmp::min(len - written, usize::max_value() as u64) as usize;
+ let copy_result = unsafe {
+ // We actually don't have to adjust the offsets,
+ // because copy_file_range adjusts the file offset automatically
+ cvt(copy_file_range(reader.as_raw_fd(),
+ ptr::null_mut(),
+ writer.as_raw_fd(),
+ ptr::null_mut(),
+ bytes_to_copy,
+ 0)
+ )
+ };
+ if let Err(ref copy_err) = copy_result {
+ match copy_err.raw_os_error() {
+ Some(libc::ENOSYS) | Some(libc::EPERM) => {
+ HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed);
+ }
+ _ => {}
+ }
+ }
+ copy_result
+ } else {
+ Err(io::Error::from_raw_os_error(libc::ENOSYS))
+ };
+ match copy_result {
+ Ok(ret) => written += ret as u64,
+ Err(err) => {
+ match err.raw_os_error() {
+ Some(os_err) if os_err == libc::ENOSYS
+ || os_err == libc::EXDEV
+ || os_err == libc::EPERM => {
+ // Try fallback io::copy if either:
+ // - Kernel version is < 4.5 (ENOSYS)
+ // - Files are mounted on different fs (EXDEV)
+ // - copy_file_range is disallowed, for example by seccomp (EPERM)
+ assert_eq!(written, 0);
+ let ret = io::copy(&mut reader, &mut writer)?;
+ writer.set_permissions(perm)?;
+ return Ok(ret)
+ },
+ _ => return Err(err),
+ }
+ }
+ }
+ }
+ writer.set_permissions(perm)?;
+ Ok(written)
+}
diff --git a/ctr-std/src/sys/horizon/l4re.rs b/ctr-std/src/sys/horizon/l4re.rs
new file mode 100644
index 0000000..2121848
--- /dev/null
+++ b/ctr-std/src/sys/horizon/l4re.rs
@@ -0,0 +1,441 @@
+// Copyright 2016-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.
+
+macro_rules! unimpl {
+ () => (return Err(io::Error::new(io::ErrorKind::Other, "No networking available on L4Re."));)
+}
+
+pub mod net {
+ #![allow(warnings)]
+ use fmt;
+ use io;
+ use libc;
+ use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr};
+ use sys_common::{AsInner, FromInner, IntoInner};
+ use sys::fd::FileDesc;
+ use time::Duration;
+
+
+ pub extern crate libc as netc;
+
+ pub struct Socket(FileDesc);
+ impl Socket {
+ pub fn new(_: &SocketAddr, _: libc::c_int) -> io::Result<Socket> {
+ unimpl!();
+ }
+
+ pub fn new_raw(_: libc::c_int, _: libc::c_int) -> io::Result<Socket> {
+ unimpl!();
+ }
+
+ pub fn new_pair(_: libc::c_int, _: libc::c_int) -> io::Result<(Socket, Socket)> {
+ unimpl!();
+ }
+
+ pub fn connect_timeout(&self, _: &SocketAddr, _: Duration) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn accept(&self, _: *mut libc::sockaddr, _: *mut libc::socklen_t)
+ -> io::Result<Socket> {
+ unimpl!();
+ }
+
+ pub fn duplicate(&self) -> io::Result<Socket> {
+ unimpl!();
+ }
+
+ pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unimpl!();
+ }
+
+ pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unimpl!();
+ }
+
+ pub fn write(&self, _: &[u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn set_timeout(&self, _: Option<Duration>, _: libc::c_int) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn timeout(&self, _: libc::c_int) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
+ pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unimpl!();
+ }
+ }
+
+ impl AsInner<libc::c_int> for Socket {
+ fn as_inner(&self) -> &libc::c_int { self.0.as_inner() }
+ }
+
+ impl FromInner<libc::c_int> for Socket {
+ fn from_inner(fd: libc::c_int) -> Socket { Socket(FileDesc::new(fd)) }
+ }
+
+ impl IntoInner<libc::c_int> for Socket {
+ fn into_inner(self) -> libc::c_int { self.0.into_raw() }
+ }
+
+ pub struct TcpStream {
+ inner: Socket,
+ }
+
+ impl TcpStream {
+ pub fn connect(_: &SocketAddr) -> io::Result<TcpStream> {
+ unimpl!();
+ }
+
+ pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
+ unimpl!();
+ }
+
+ pub fn socket(&self) -> &Socket { &self.inner }
+
+ pub fn into_socket(self) -> Socket { self.inner }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn write(&self, _: &[u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ unimpl!();
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unimpl!();
+ }
+
+ pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpStream> {
+ unimpl!();
+ }
+
+ pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unimpl!();
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unimpl!();
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+ }
+
+ impl FromInner<Socket> for TcpStream {
+ fn from_inner(socket: Socket) -> TcpStream {
+ TcpStream { inner: socket }
+ }
+ }
+
+ impl fmt::Debug for TcpStream {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "No networking support available on L4Re")
+ }
+ }
+
+ pub struct TcpListener {
+ inner: Socket,
+ }
+
+ impl TcpListener {
+ pub fn bind(_: &SocketAddr) -> io::Result<TcpListener> {
+ unimpl!();
+ }
+
+ pub fn socket(&self) -> &Socket { &self.inner }
+
+ pub fn into_socket(self) -> Socket { self.inner }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unimpl!();
+ }
+
+ pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+ unimpl!();
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpListener> {
+ unimpl!();
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unimpl!();
+ }
+
+ pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn only_v6(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unimpl!();
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+ }
+
+ impl FromInner<Socket> for TcpListener {
+ fn from_inner(socket: Socket) -> TcpListener {
+ TcpListener { inner: socket }
+ }
+ }
+
+ impl fmt::Debug for TcpListener {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "No networking support available on L4Re.")
+ }
+ }
+
+ pub struct UdpSocket {
+ inner: Socket,
+ }
+
+ impl UdpSocket {
+ pub fn bind(_: &SocketAddr) -> io::Result<UdpSocket> {
+ unimpl!();
+ }
+
+ pub fn socket(&self) -> &Socket { &self.inner }
+
+ pub fn into_socket(self) -> Socket { self.inner }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unimpl!();
+ }
+
+ pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unimpl!();
+ }
+
+ pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unimpl!();
+ }
+
+ pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn duplicate(&self) -> io::Result<UdpSocket> {
+ unimpl!();
+ }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
+ pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn broadcast(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn multicast_loop_v4(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
+ unimpl!();
+ }
+
+ pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn multicast_loop_v6(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr)
+ -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32)
+ -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr)
+ -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32)
+ -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unimpl!();
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unimpl!();
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn send(&self, _: &[u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn connect(&self, _: &SocketAddr) -> io::Result<()> {
+ unimpl!();
+ }
+ }
+
+ impl FromInner<Socket> for UdpSocket {
+ fn from_inner(socket: Socket) -> UdpSocket {
+ UdpSocket { inner: socket }
+ }
+ }
+
+ impl fmt::Debug for UdpSocket {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "No networking support on L4Re available.")
+ }
+ }
+
+ pub struct LookupHost {
+ original: *mut libc::addrinfo,
+ cur: *mut libc::addrinfo,
+ }
+
+ impl Iterator for LookupHost {
+ type Item = SocketAddr;
+ fn next(&mut self) -> Option<SocketAddr> {
+ None
+ }
+ }
+
+ unsafe impl Sync for LookupHost {}
+ unsafe impl Send for LookupHost {}
+
+ pub fn lookup_host(_: &str) -> io::Result<LookupHost> {
+ unimpl!();
+ }
+}
+
diff --git a/ctr-std/src/sys/horizon/memchr.rs b/ctr-std/src/sys/horizon/memchr.rs
new file mode 100644
index 0000000..f49adc2
--- /dev/null
+++ b/ctr-std/src/sys/horizon/memchr.rs
@@ -0,0 +1,57 @@
+// 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
+
+pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
+ use libc;
+
+ let p = unsafe {
+ libc::memchr(
+ haystack.as_ptr() as *const libc::c_void,
+ needle as libc::c_int,
+ haystack.len())
+ };
+ if p.is_null() {
+ None
+ } else {
+ Some(p as usize - (haystack.as_ptr() as usize))
+ }
+}
+
+pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> {
+
+ #[cfg(target_os = "linux")]
+ fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
+ use libc;
+
+ // GNU's memrchr() will - unlike memchr() - error if haystack is empty.
+ if haystack.is_empty() {return None}
+ let p = unsafe {
+ libc::memrchr(
+ haystack.as_ptr() as *const libc::c_void,
+ needle as libc::c_int,
+ haystack.len())
+ };
+ if p.is_null() {
+ None
+ } else {
+ Some(p as usize - (haystack.as_ptr() as usize))
+ }
+ }
+
+ #[cfg(not(target_os = "linux"))]
+ fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
+ ::core::slice::memchr::memrchr(needle, haystack)
+ }
+
+ memrchr_specific(needle, haystack)
+}
diff --git a/ctr-std/src/sys/horizon/mod.rs b/ctr-std/src/sys/horizon/mod.rs
new file mode 100644
index 0000000..efbe0bb
--- /dev/null
+++ b/ctr-std/src/sys/horizon/mod.rs
@@ -0,0 +1,179 @@
+// 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 io::{self, ErrorKind};
+use libc;
+
+#[cfg(any(dox, target_os = "linux"))] pub use os::linux as platform;
+
+#[cfg(all(not(dox), target_os = "android"))] pub use os::android as platform;
+#[cfg(all(not(dox), target_os = "bitrig"))] pub use os::bitrig as platform;
+#[cfg(all(not(dox), target_os = "dragonfly"))] pub use os::dragonfly as platform;
+#[cfg(all(not(dox), target_os = "freebsd"))] pub use os::freebsd as platform;
+#[cfg(all(not(dox), target_os = "haiku"))] pub use os::haiku as platform;
+#[cfg(all(not(dox), target_os = "ios"))] pub use os::ios as platform;
+#[cfg(all(not(dox), target_os = "macos"))] pub use os::macos as platform;
+#[cfg(all(not(dox), target_os = "netbsd"))] pub use os::netbsd as platform;
+#[cfg(all(not(dox), target_os = "openbsd"))] pub use os::openbsd as platform;
+#[cfg(all(not(dox), target_os = "solaris"))] pub use os::solaris as platform;
+#[cfg(all(not(dox), target_os = "emscripten"))] pub use os::emscripten as platform;
+#[cfg(all(not(dox), target_os = "fuchsia"))] pub use os::fuchsia as platform;
+#[cfg(all(not(dox), target_os = "l4re"))] pub use os::linux as platform;
+#[cfg(all(not(dox), target_os = "horizon"))] pub use os::horizon as platform;
+
+pub use self::rand::hashmap_random_keys;
+pub use libc::strlen;
+
+#[macro_use]
+pub mod weak;
+
+pub mod args;
+pub mod android;
+#[cfg(feature = "backtrace")]
+pub mod backtrace;
+pub mod cmath;
+pub mod condvar;
+pub mod env;
+pub mod ext;
+pub mod fast_thread_local;
+pub mod fd;
+pub mod fs;
+pub mod memchr;
+pub mod mutex;
+#[cfg(not(target_os = "l4re"))]
+pub mod net;
+#[cfg(target_os = "l4re")]
+mod l4re;
+#[cfg(target_os = "l4re")]
+pub use self::l4re::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() {
+ // By default, some platforms will send a *signal* when an EPIPE error
+ // would otherwise be delivered. This runtime doesn't install a SIGPIPE
+ // handler, causing it to kill the program, which isn't exactly what we
+ // want!
+ //
+ // Hence, we set SIGPIPE to ignore when the program starts up in order
+ // to prevent this problem.
+ unsafe {
+ reset_sigpipe();
+ }
+
+ #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
+ unsafe fn reset_sigpipe() {
+ assert!(signal(0 as _, libc::SIG_IGN) != libc::SIG_ERR);
+ }
+ #[cfg(any(target_os = "emscripten", target_os = "fuchsia"))]
+ unsafe fn reset_sigpipe() {}
+}
+
+#[cfg(target_os = "android")]
+pub use sys::android::signal;
+#[cfg(not(target_os = "android"))]
+pub use libc::signal;
+
+pub fn unsupported<T>() -> io::Result<T> {
+ Err(unsupported_err())
+}
+
+pub fn unsupported_err() -> io::Error {
+ io::Error::new(io::ErrorKind::Other,
+ "operation not supported on 3DS yet")
+}
+
+#[derive(Copy,Clone)]
+pub enum Void {}
+
+pub fn decode_error_kind(errno: i32) -> ErrorKind {
+ match errno as libc::c_int {
+ libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
+ libc::ECONNRESET => ErrorKind::ConnectionReset,
+ libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied,
+ libc::EPIPE => ErrorKind::BrokenPipe,
+ libc::ENOTCONN => ErrorKind::NotConnected,
+ libc::ECONNABORTED => ErrorKind::ConnectionAborted,
+ libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
+ libc::EADDRINUSE => ErrorKind::AddrInUse,
+ libc::ENOENT => ErrorKind::NotFound,
+ libc::EINTR => ErrorKind::Interrupted,
+ libc::EINVAL => ErrorKind::InvalidInput,
+ libc::ETIMEDOUT => ErrorKind::TimedOut,
+ libc::EEXIST => ErrorKind::AlreadyExists,
+
+ // These two constants can have the same value on some systems,
+ // but different values on others, so we can't use a match
+ // clause
+ x if x == libc::EAGAIN || x == libc::EWOULDBLOCK =>
+ ErrorKind::WouldBlock,
+
+ _ => ErrorKind::Other,
+ }
+}
+
+#[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 }
+
+pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
+ if t.is_minus_one() {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(t)
+ }
+}
+
+pub fn cvt_r<T, F>(mut f: F) -> io::Result<T>
+ where T: IsMinusOne,
+ F: FnMut() -> T
+{
+ loop {
+ match cvt(f()) {
+ Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
+ other => return other,
+ }
+ }
+}
+
+// On Unix-like platforms, libc::abort will unregister signal handlers
+// including the SIGABRT handler, preventing the abort from being blocked, and
+// fclose streams, with the side effect of flushing them so libc bufferred
+// output will be printed. Additionally the shell will generally print a more
+// understandable error message like "Abort trap" rather than "Illegal
+// instruction" that intrinsics::abort would cause, as intrinsics::abort is
+// implemented as an illegal instruction.
+pub unsafe fn abort_internal() -> ! {
+ ::libc::abort()
+}
diff --git a/ctr-std/src/sys/horizon/mutex.rs b/ctr-std/src/sys/horizon/mutex.rs
new file mode 100644
index 0000000..95c74a4
--- /dev/null
+++ b/ctr-std/src/sys/horizon/mutex.rs
@@ -0,0 +1,90 @@
+// 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.
+
+use cell::UnsafeCell;
+use mem;
+
+pub struct Mutex {
+ inner: UnsafeCell<::libctru::LightLock>,
+}
+
+#[inline]
+pub unsafe fn raw(m: &Mutex) -> *mut ::libctru::LightLock {
+ m.inner.get()
+}
+
+unsafe impl Send for Mutex {}
+unsafe impl Sync for Mutex {}
+
+impl Mutex {
+ pub const fn new() -> Mutex {
+ Mutex { inner: UnsafeCell::new(0) }
+ }
+
+ #[inline]
+ pub unsafe fn init(&mut self) {
+ ::libctru::LightLock_Init(self.inner.get());
+ }
+
+ #[inline]
+ pub unsafe fn lock(&self) {
+ ::libctru::LightLock_Lock(self.inner.get());
+ }
+
+ #[inline]
+ pub unsafe fn unlock(&self) {
+ ::libctru::LightLock_Unlock(self.inner.get());
+ }
+
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ match ::libctru::LightLock_TryLock(self.inner.get()) {
+ 0 => false,
+ _ => true,
+ }
+ }
+
+ #[inline]
+ pub unsafe fn destroy(&self) {
+ }
+}
+
+pub struct ReentrantMutex { inner: UnsafeCell<::libctru::RecursiveLock> }
+
+unsafe impl Send for ReentrantMutex {}
+unsafe impl Sync for ReentrantMutex {}
+
+impl ReentrantMutex {
+ pub unsafe fn uninitialized() -> ReentrantMutex {
+ ReentrantMutex { inner: mem::uninitialized() }
+ }
+
+ pub unsafe fn init(&mut self) {
+ ::libctru::RecursiveLock_Init(self.inner.get());
+ }
+
+ pub unsafe fn lock(&self) {
+ ::libctru::RecursiveLock_Lock(self.inner.get());
+ }
+
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ match ::libctru::RecursiveLock_TryLock(self.inner.get()) {
+ 0 => false,
+ _ => true,
+ }
+ }
+
+ pub unsafe fn unlock(&self) {
+ ::libctru::RecursiveLock_Unlock(self.inner.get());
+ }
+
+ pub unsafe fn destroy(&self) {}
+}
diff --git a/ctr-std/src/sys/horizon/net.rs b/ctr-std/src/sys/horizon/net.rs
new file mode 100644
index 0000000..b6af433
--- /dev/null
+++ b/ctr-std/src/sys/horizon/net.rs
@@ -0,0 +1,428 @@
+// 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.
+
+#![allow(dead_code)]
+
+use ffi::CStr;
+use io;
+use libc::{self, c_int, c_void, size_t, sockaddr, socklen_t, MSG_PEEK};
+use mem;
+use net::{SocketAddr, Shutdown};
+use str;
+use sys::fd::FileDesc;
+use sys_common::{AsInner, FromInner, IntoInner};
+use sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr};
+use time::{Duration, Instant};
+use cmp;
+
+pub use sys::{cvt, cvt_r};
+pub extern crate libc as netc;
+
+pub type wrlen_t = size_t;
+
+// See below for the usage of SOCK_CLOEXEC, but this constant is only defined on
+// Linux currently (e.g. support doesn't exist on other platforms). In order to
+// get name resolution to work and things to compile we just define a dummy
+// SOCK_CLOEXEC here for other platforms. Note that the dummy constant isn't
+// actually ever used (the blocks below are wrapped in `if cfg!` as well.
+#[cfg(target_os = "linux")]
+use libc::SOCK_CLOEXEC;
+#[cfg(not(target_os = "linux"))]
+const SOCK_CLOEXEC: c_int = 0;
+
+// Another conditional contant for name resolution: Macos et iOS use
+// SO_NOSIGPIPE as a setsockopt flag to disable SIGPIPE emission on socket.
+// Other platforms do otherwise.
+#[cfg(target_vendor = "apple")]
+use libc::SO_NOSIGPIPE;
+#[cfg(not(target_vendor = "apple"))]
+const SO_NOSIGPIPE: c_int = 0;
+
+const EAI_SYSTEM: c_int = 0;
+
+pub struct Socket(FileDesc);
+
+pub fn init() {}
+
+pub fn cvt_gai(err: c_int) -> io::Result<()> {
+ if err == 0 {
+ return Ok(())
+ }
+
+ // We may need to trigger a glibc workaround. See on_resolver_failure() for details.
+ on_resolver_failure();
+
+ if err == EAI_SYSTEM {
+ return Err(io::Error::last_os_error())
+ }
+
+ let detail = unsafe {
+ str::from_utf8(CStr::from_ptr(libc::gai_strerror(err) as *const i8).to_bytes()).unwrap()
+ .to_owned()
+ };
+ Err(io::Error::new(io::ErrorKind::Other,
+ &format!("failed to lookup address information: {}",
+ detail)[..]))
+}
+
+impl Socket {
+ pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
+ let fam = match *addr {
+ SocketAddr::V4(..) => libc::AF_INET,
+ SocketAddr::V6(..) => libc::AF_INET6,
+ };
+ Socket::new_raw(fam, ty)
+ }
+
+ pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> {
+ unsafe {
+ // On linux we first attempt to pass the SOCK_CLOEXEC flag to
+ // atomically create the socket and set it as CLOEXEC. Support for
+ // this option, however, was added in 2.6.27, and we still support
+ // 2.6.18 as a kernel, so if the returned error is EINVAL we
+ // fallthrough to the fallback.
+ if cfg!(target_os = "linux") {
+ match cvt(libc::socket(fam, ty | SOCK_CLOEXEC, 0)) {
+ Ok(fd) => return Ok(Socket(FileDesc::new(fd))),
+ Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}
+ Err(e) => return Err(e),
+ }
+ }
+
+ let fd = cvt(libc::socket(fam, ty, 0))?;
+ let fd = FileDesc::new(fd);
+ fd.set_cloexec()?;
+ let socket = Socket(fd);
+ if cfg!(target_vendor = "apple") {
+ setsockopt(&socket, libc::SOL_SOCKET, SO_NOSIGPIPE, 1)?;
+ }
+ Ok(socket)
+ }
+ }
+
+ pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> {
+ unsafe {
+ let mut fds = [0, 0];
+
+ // Like above, see if we can set cloexec atomically
+ if cfg!(target_os = "linux") {
+ match cvt(libc::socketpair(fam, ty | SOCK_CLOEXEC, 0, fds.as_mut_ptr())) {
+ Ok(_) => {
+ return Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1]))));
+ }
+ Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {},
+ Err(e) => return Err(e),
+ }
+ }
+
+ cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?;
+ let a = FileDesc::new(fds[0]);
+ let b = FileDesc::new(fds[1]);
+ a.set_cloexec()?;
+ b.set_cloexec()?;
+ Ok((Socket(a), Socket(b)))
+ }
+ }
+
+ 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(libc::connect(self.0.raw(), addrp, len))
+ };
+ self.set_nonblocking(false)?;
+
+ match r {
+ Ok(_) => return Ok(()),
+ // there's no ErrorKind for EINPROGRESS :(
+ Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {}
+ Err(e) => return Err(e),
+ }
+
+ let mut pollfd = libc::pollfd {
+ fd: self.0.raw(),
+ events: libc::POLLOUT,
+ revents: 0,
+ };
+
+ if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
+ return Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot set a 0 duration timeout"));
+ }
+
+ let start = Instant::now();
+
+ loop {
+ let elapsed = start.elapsed();
+ if elapsed >= timeout {
+ return Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out"));
+ }
+
+ let timeout = timeout - elapsed;
+ let mut timeout = timeout.as_secs()
+ .saturating_mul(1_000)
+ .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000);
+ if timeout == 0 {
+ timeout = 1;
+ }
+
+ let timeout = cmp::min(timeout, c_int::max_value() as u64) as c_int;
+
+ match unsafe { libc::poll(&mut pollfd, 1, timeout) } {
+ -1 => {
+ let err = io::Error::last_os_error();
+ if err.kind() != io::ErrorKind::Interrupted {
+ return Err(err);
+ }
+ }
+ 0 => {}
+ _ => {
+ // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look
+ // for POLLHUP rather than read readiness
+ if pollfd.revents & libc::POLLHUP != 0 {
+ let e = self.take_error()?
+ .unwrap_or_else(|| {
+ io::Error::new(io::ErrorKind::Other, "no error set after POLLHUP")
+ });
+ return Err(e);
+ }
+
+ return Ok(());
+ }
+ }
+ }
+ }
+
+ pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t)
+ -> io::Result<Socket> {
+ // Unfortunately the only known way right now to accept a socket and
+ // atomically set the CLOEXEC flag is to use the `accept4` syscall on
+ // Linux. This was added in 2.6.28, however, and because we support
+ // 2.6.18 we must detect this support dynamically.
+ if cfg!(target_os = "linux") {
+ weak! {
+ fn accept4(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int
+ }
+ if let Some(accept) = accept4.get() {
+ let res = cvt_r(|| unsafe {
+ accept(self.0.raw(), storage, len, SOCK_CLOEXEC)
+ });
+ match res {
+ Ok(fd) => return Ok(Socket(FileDesc::new(fd))),
+ Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {}
+ Err(e) => return Err(e),
+ }
+ }
+ }
+
+ let fd = cvt_r(|| unsafe {
+ libc::accept(self.0.raw(), storage, len)
+ })?;
+ let fd = FileDesc::new(fd);
+ fd.set_cloexec()?;
+ Ok(Socket(fd))
+ }
+
+ pub fn duplicate(&self) -> io::Result<Socket> {
+ self.0.duplicate().map(Socket)
+ }
+
+ fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ libc::recv(self.0.raw(),
+ buf.as_mut_ptr() as *mut c_void,
+ buf.len(),
+ flags)
+ })?;
+ Ok(ret 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, MSG_PEEK)
+ }
+
+ fn recv_from_with_flags(&self, buf: &mut [u8], flags: c_int)
+ -> io::Result<(usize, SocketAddr)> {
+ let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
+ let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t;
+
+ let n = cvt(unsafe {
+ libc::recvfrom(self.0.raw(),
+ buf.as_mut_ptr() as *mut c_void,
+ buf.len(),
+ flags,
+ &mut storage as *mut _ as *mut _,
+ &mut addrlen)
+ })?;
+ Ok((n as usize, 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, MSG_PEEK)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ pub fn set_timeout(&self, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> {
+ let timeout = match dur {
+ Some(dur) => {
+ if dur.as_secs() == 0 && dur.subsec_nanos() == 0 {
+ return Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot set a 0 duration timeout"));
+ }
+
+ let secs = if dur.as_secs() > libc::time_t::max_value() as u64 {
+ libc::time_t::max_value()
+ } else {
+ dur.as_secs() as libc::time_t
+ };
+ let mut timeout = libc::timeval {
+ tv_sec: secs,
+ tv_usec: (dur.subsec_nanos() / 1000) as libc::suseconds_t,
+ };
+ if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
+ timeout.tv_usec = 1;
+ }
+ timeout
+ }
+ None => {
+ libc::timeval {
+ tv_sec: 0,
+ tv_usec: 0,
+ }
+ }
+ };
+ setsockopt(self, libc::SOL_SOCKET, kind, timeout)
+ }
+
+ pub fn timeout(&self, kind: libc::c_int) -> io::Result<Option<Duration>> {
+ let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?;
+ if raw.tv_sec == 0 && raw.tv_usec == 0 {
+ Ok(None)
+ } else {
+ let sec = raw.tv_sec as u64;
+ let nsec = (raw.tv_usec as u32) * 1000;
+ Ok(Some(Duration::new(sec, nsec)))
+ }
+ }
+
+ pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+ let how = match how {
+ Shutdown::Write => libc::SHUT_WR,
+ Shutdown::Read => libc::SHUT_RD,
+ Shutdown::Both => libc::SHUT_RDWR,
+ };
+ cvt(unsafe { libc::shutdown(self.0.raw(), how) })?;
+ Ok(())
+ }
+
+ pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
+ setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int)
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?;
+ Ok(raw != 0)
+ }
+
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ let mut nonblocking = nonblocking as libc::c_int;
+ cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO as u32, &mut nonblocking) }).map(|_| ())
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?;
+ if raw == 0 {
+ Ok(None)
+ } else {
+ Ok(Some(io::Error::from_raw_os_error(raw as i32)))
+ }
+ }
+}
+
+impl AsInner<c_int> for Socket {
+ fn as_inner(&self) -> &c_int { self.0.as_inner() }
+}
+
+impl FromInner<c_int> for Socket {
+ fn from_inner(fd: c_int) -> Socket { Socket(FileDesc::new(fd)) }
+}
+
+impl IntoInner<c_int> for Socket {
+ fn into_inner(self) -> c_int { self.0.into_raw() }
+}
+
+// In versions of glibc prior to 2.26, there's a bug where the DNS resolver
+// will cache the contents of /etc/resolv.conf, so changes to that file on disk
+// can be ignored by a long-running program. That can break DNS lookups on e.g.
+// laptops where the network comes and goes. See
+// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some
+// distros including Debian have patched glibc to fix this for a long time.
+//
+// A workaround for this bug is to call the res_init libc function, to clear
+// the cached configs. Unfortunately, while we believe glibc's implementation
+// of res_init is thread-safe, we know that other implementations are not
+// (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could
+// try to synchronize its res_init calls with a Mutex, but that wouldn't
+// protect programs that call into libc in other ways. So instead of calling
+// res_init unconditionally, we call it only when we detect we're linking
+// against glibc version < 2.26. (That is, when we both know its needed and
+// believe it's thread-safe).
+#[cfg(target_env = "gnu")]
+fn on_resolver_failure() {
+ use sys;
+
+ // If the version fails to parse, we treat it the same as "not glibc".
+ if let Some(version) = sys::os::glibc_version() {
+ if version < (2, 26) {
+ unsafe { libc::res_init() };
+ }
+ }
+}
+
+#[cfg(not(target_env = "gnu"))]
+fn on_resolver_failure() {}
+
+#[cfg(all(test, taget_env = "gnu"))]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_res_init() {
+ // This mostly just tests that the weak linkage doesn't panic wildly...
+ res_init_if_glibc_before_2_26().unwrap();
+ }
+
+ #[test]
+ fn test_parse_glibc_version() {
+ let cases = [
+ ("0.0", Some((0, 0))),
+ ("01.+2", Some((1, 2))),
+ ("3.4.5.six", Some((3, 4))),
+ ("1", None),
+ ("1.-2", None),
+ ("1.foo", None),
+ ("foo.1", None),
+ ];
+ for &(version_str, parsed) in cases.iter() {
+ assert_eq!(parsed, parse_glibc_version(version_str));
+ }
+ }
+}
diff --git a/ctr-std/src/sys/horizon/os.rs b/ctr-std/src/sys/horizon/os.rs
new file mode 100644
index 0000000..a26e007
--- /dev/null
+++ b/ctr-std/src/sys/horizon/os.rs
@@ -0,0 +1,191 @@
+// 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.
+
+use error::Error as StdError;
+use ffi::{CString, CStr, OsString, OsStr};
+use fmt;
+use io;
+use iter;
+use libc::{self, c_int, c_char};
+use path::{self, PathBuf};
+use slice;
+use str;
+use sys::{unsupported, Void};
+use sys::horizon::ext::ffi::{OsStrExt, OsStringExt};
+
+const TMPBUF_SZ: usize = 128;
+
+extern "C" {
+ fn __errno() -> *mut c_int;
+}
+
+/// Returns the platform-specific value of errno
+pub fn errno() -> i32 {
+ unsafe {
+ (*__errno()) as i32
+ }
+}
+
+/// Gets a detailed string description for the given error number.
+pub fn error_string(errno: i32) -> String {
+ extern {
+ #[cfg_attr(any(target_os = "linux", target_env = "newlib"),
+ link_name = "__xpg_strerror_r")]
+ fn strerror_r(errnum: c_int, buf: *mut c_char,
+ buflen: libc::size_t) -> c_int;
+ }
+
+ let mut buf = [0 as c_char; TMPBUF_SZ];
+
+ let p = buf.as_mut_ptr();
+ unsafe {
+ if strerror_r(errno as c_int, p, buf.len()) < 0 {
+ panic!("strerror_r failure");
+ }
+
+ let p = p as *const _;
+ str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned()
+ }
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+ let mut buf = Vec::with_capacity(512);
+ loop {
+ unsafe {
+ let ptr = buf.as_mut_ptr() as *mut libc::c_char;
+ if !libc::getcwd(ptr, buf.capacity()).is_null() {
+ let len = CStr::from_ptr(buf.as_ptr() as *const i8).to_bytes().len();
+ buf.set_len(len);
+ buf.shrink_to_fit();
+ return Ok(PathBuf::from(OsString::from_vec(buf)));
+ } else {
+ let error = io::Error::last_os_error();
+ if error.raw_os_error() != Some(libc::ERANGE) {
+ return Err(error);
+ }
+ }
+
+ // Trigger the internal buffer resizing logic of `Vec` by requiring
+ // more space than the current capacity.
+ let cap = buf.capacity();
+ buf.set_len(cap);
+ buf.reserve(1);
+ }
+ }
+}
+
+pub fn chdir(p: &path::Path) -> io::Result<()> {
+ let p: &OsStr = p.as_ref();
+ let p = CString::new(p.as_bytes())?;
+ unsafe {
+ match libc::chdir(p.as_ptr() as *const u8) == (0 as c_int) {
+ true => Ok(()),
+ false => Err(io::Error::last_os_error()),
+ }
+ }
+}
+
+pub struct SplitPaths<'a> {
+ iter: iter::Map<slice::Split<'a, u8, fn(&u8) -> bool>,
+ fn(&'a [u8]) -> PathBuf>,
+}
+
+pub fn split_paths(unparsed: &OsStr) -> SplitPaths {
+ fn bytes_to_path(b: &[u8]) -> PathBuf {
+ PathBuf::from(<OsStr as OsStrExt>::from_bytes(b))
+ }
+ fn is_colon(b: &u8) -> bool { *b == b':' }
+ let unparsed = unparsed.as_bytes();
+ SplitPaths {
+ iter: unparsed.split(is_colon as fn(&u8) -> bool)
+ .map(bytes_to_path as fn(&[u8]) -> PathBuf)
+ }
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+ type Item = PathBuf;
+ fn next(&mut self) -> Option<PathBuf> { self.iter.next() }
+ fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
+}
+
+#[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':';
+
+ for (i, path) in paths.enumerate() {
+ let path = path.as_ref().as_bytes();
+ if i > 0 { joined.push(sep) }
+ if path.contains(&sep) {
+ return Err(JoinPathsError)
+ }
+ joined.extend_from_slice(path);
+ }
+ Ok(OsStringExt::from_vec(joined))
+}
+
+impl fmt::Display for JoinPathsError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ "path segment contains separator `:`".fmt(f)
+ }
+}
+
+impl StdError for JoinPathsError {
+ fn description(&self) -> &str { "failed to join paths" }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub struct Env(Void);
+
+impl Iterator for Env {
+ type Item = (OsString, OsString);
+ fn next(&mut self) -> Option<(OsString, OsString)> {
+ match self.0 {}
+ }
+}
+
+pub fn env() -> Env {
+ panic!("not supported on 3DS yet")
+}
+
+pub fn getenv(_k: &OsStr) -> io::Result<Option<OsString>> {
+ return Ok(None)
+}
+
+pub fn setenv(_k: &OsStr, _v: &OsStr) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn unsetenv(_n: &OsStr) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn temp_dir() -> PathBuf {
+ PathBuf::from("/tmp")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+ None
+}
+
+pub fn exit(code: i32) -> ! {
+ unsafe { libc::exit(code as c_int) }
+}
+
+pub fn getpid() -> u32 {
+ panic!("no pids on 3DS")
+} \ No newline at end of file
diff --git a/ctr-std/src/sys/horizon/os_str.rs b/ctr-std/src/sys/horizon/os_str.rs
new file mode 100644
index 0000000..01c0fb8
--- /dev/null
+++ b/ctr-std/src/sys/horizon/os_str.rs
@@ -0,0 +1,189 @@
+// 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 Unix systems: just
+/// a `Vec<u8>`/`[u8]`.
+
+use borrow::Cow;
+use fmt;
+use str;
+use mem;
+use rc::Rc;
+use sync::Arc;
+use sys_common::{AsInner, IntoInner};
+use sys_common::bytestring::debug_fmt_bytestring;
+use core::str::lossy::Utf8Lossy;
+
+#[derive(Clone, Hash)]
+pub struct Buf {
+ pub inner: Vec<u8>
+}
+
+pub struct Slice {
+ pub inner: [u8]
+}
+
+impl fmt::Debug for Slice {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ debug_fmt_bytestring(&self.inner, formatter)
+ }
+}
+
+impl fmt::Display for Slice {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter)
+ }
+}
+
+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)
+ }
+}
+
+impl IntoInner<Vec<u8>> for Buf {
+ fn into_inner(self) -> Vec<u8> {
+ self.inner
+ }
+}
+
+impl AsInner<[u8]> for Buf {
+ fn as_inner(&self) -> &[u8] {
+ &self.inner
+ }
+}
+
+
+impl Buf {
+ pub fn from_string(s: String) -> Buf {
+ Buf { inner: s.into_bytes() }
+ }
+
+ #[inline]
+ pub fn with_capacity(capacity: usize) -> Buf {
+ Buf {
+ inner: Vec::with_capacity(capacity)
+ }
+ }
+
+ #[inline]
+ pub fn clear(&mut self) {
+ self.inner.clear()
+ }
+
+ #[inline]
+ pub fn capacity(&self) -> usize {
+ self.inner.capacity()
+ }
+
+ #[inline]
+ pub fn reserve(&mut self, additional: usize) {
+ self.inner.reserve(additional)
+ }
+
+ #[inline]
+ pub fn reserve_exact(&mut self, additional: usize) {
+ self.inner.reserve_exact(additional)
+ }
+
+ #[inline]
+ 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)
+ }
+
+ pub fn as_slice(&self) -> &Slice {
+ unsafe { mem::transmute(&*self.inner) }
+ }
+
+ pub fn into_string(self) -> Result<String, Buf> {
+ String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } )
+ }
+
+ pub fn push_slice(&mut self, s: &Slice) {
+ self.inner.extend_from_slice(&s.inner)
+ }
+
+ #[inline]
+ pub fn into_box(self) -> Box<Slice> {
+ unsafe { mem::transmute(self.inner.into_boxed_slice()) }
+ }
+
+ #[inline]
+ pub fn from_box(boxed: Box<Slice>) -> Buf {
+ let inner: Box<[u8]> = unsafe { mem::transmute(boxed) };
+ Buf { inner: inner.into_vec() }
+ }
+
+ #[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 {
+ fn from_u8_slice(s: &[u8]) -> &Slice {
+ unsafe { mem::transmute(s) }
+ }
+
+ pub fn from_str(s: &str) -> &Slice {
+ Slice::from_u8_slice(s.as_bytes())
+ }
+
+ pub fn to_str(&self) -> Option<&str> {
+ str::from_utf8(&self.inner).ok()
+ }
+
+ pub fn to_string_lossy(&self) -> Cow<str> {
+ String::from_utf8_lossy(&self.inner)
+ }
+
+ pub fn to_owned(&self) -> Buf {
+ Buf { inner: self.inner.to_vec() }
+ }
+
+ #[inline]
+ pub fn into_box(&self) -> Box<Slice> {
+ let boxed: Box<[u8]> = self.inner.into();
+ unsafe { mem::transmute(boxed) }
+ }
+
+ pub fn empty_box() -> Box<Slice> {
+ let boxed: Box<[u8]> = Default::default();
+ unsafe { mem::transmute(boxed) }
+ }
+
+ #[inline]
+ pub fn into_arc(&self) -> Arc<Slice> {
+ let arc: Arc<[u8]> = Arc::from(&self.inner);
+ unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) }
+ }
+
+ #[inline]
+ pub fn into_rc(&self) -> Rc<Slice> {
+ let rc: Rc<[u8]> = Rc::from(&self.inner);
+ unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) }
+ }
+}
diff --git a/ctr-std/src/sys/horizon/path.rs b/ctr-std/src/sys/horizon/path.rs
new file mode 100644
index 0000000..bf9af7a
--- /dev/null
+++ b/ctr-std/src/sys/horizon/path.rs
@@ -0,0 +1,29 @@
+// 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;
+
+#[inline]
+pub fn is_sep_byte(b: u8) -> bool {
+ b == b'/'
+}
+
+#[inline]
+pub fn is_verbatim_sep(b: u8) -> bool {
+ b == b'/'
+}
+
+pub fn parse_prefix(_: &OsStr) -> Option<Prefix> {
+ None
+}
+
+pub const MAIN_SEP_STR: &'static str = "/";
+pub const MAIN_SEP: char = '/';
diff --git a/ctr-std/src/sys/horizon/pipe.rs b/ctr-std/src/sys/horizon/pipe.rs
new file mode 100644
index 0000000..0c121ae
--- /dev/null
+++ b/ctr-std/src/sys/horizon/pipe.rs
@@ -0,0 +1,137 @@
+// 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.
+
+#![allow(dead_code)]
+
+use io;
+use libc::{self, c_int};
+use mem;
+use sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
+use sys::fd::FileDesc;
+use sys::{cvt, cvt_r};
+
+////////////////////////////////////////////////////////////////////////////////
+// Anonymous pipes
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct AnonPipe(FileDesc);
+
+pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
+ weak! { fn pipe2(*mut c_int, c_int) -> c_int }
+ static INVALID: AtomicBool = ATOMIC_BOOL_INIT;
+
+ let mut fds = [0; 2];
+
+ // Unfortunately the only known way right now to create atomically set the
+ // CLOEXEC flag is to use the `pipe2` syscall on Linux. This was added in
+ // 2.6.27, however, and because we support 2.6.18 we must detect this
+ // support dynamically.
+ if cfg!(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd")) &&
+ !INVALID.load(Ordering::SeqCst)
+ {
+
+ if let Some(pipe) = pipe2.get() {
+ // Note that despite calling a glibc function here we may still
+ // get ENOSYS. Glibc has `pipe2` since 2.9 and doesn't try to
+ // emulate on older kernels, so if you happen to be running on
+ // an older kernel you may see `pipe2` as a symbol but still not
+ // see the syscall.
+ match cvt(unsafe { pipe(fds.as_mut_ptr(), libc::O_CLOEXEC) }) {
+ Ok(_) => {
+ return Ok((AnonPipe(FileDesc::new(fds[0])),
+ AnonPipe(FileDesc::new(fds[1]))));
+ }
+ Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {
+ INVALID.store(true, Ordering::SeqCst);
+ }
+ Err(e) => return Err(e),
+ }
+ }
+ }
+ cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
+
+ let fd0 = FileDesc::new(fds[0]);
+ let fd1 = FileDesc::new(fds[1]);
+ fd0.set_cloexec()?;
+ fd1.set_cloexec()?;
+ Ok((AnonPipe(fd0), AnonPipe(fd1)))
+}
+
+impl AnonPipe {
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ pub fn fd(&self) -> &FileDesc { &self.0 }
+ pub fn into_fd(self) -> FileDesc { self.0 }
+
+ pub fn diverge(&self) -> ! {
+ panic!()
+ }
+}
+
+pub fn read2(p1: AnonPipe,
+ v1: &mut Vec<u8>,
+ p2: AnonPipe,
+ v2: &mut Vec<u8>) -> io::Result<()> {
+
+ // Set both pipes into nonblocking mode as we're gonna be reading from both
+ // in the `select` loop below, and we wouldn't want one to block the other!
+ let p1 = p1.into_fd();
+ let p2 = p2.into_fd();
+ p1.set_nonblocking(true)?;
+ p2.set_nonblocking(true)?;
+
+ let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
+ fds[0].fd = p1.raw();
+ fds[0].events = libc::POLLIN;
+ fds[1].fd = p2.raw();
+ fds[1].events = libc::POLLIN;
+ loop {
+ // wait for either pipe to become readable using `poll`
+ cvt_r(|| unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) })?;
+
+ if fds[0].revents != 0 && read(&p1, v1)? {
+ p2.set_nonblocking(false)?;
+ return p2.read_to_end(v2).map(|_| ());
+ }
+ if fds[1].revents != 0 && read(&p2, v2)? {
+ p1.set_nonblocking(false)?;
+ return p1.read_to_end(v1).map(|_| ());
+ }
+ }
+
+ // Read as much as we can from each pipe, ignoring EWOULDBLOCK or
+ // EAGAIN. If we hit EOF, then this will happen because the underlying
+ // reader will return Ok(0), in which case we'll see `Ok` ourselves. In
+ // this case we flip the other fd back into blocking mode and read
+ // whatever's leftover on that file descriptor.
+ fn read(fd: &FileDesc, dst: &mut Vec<u8>) -> Result<bool, io::Error> {
+ match fd.read_to_end(dst) {
+ Ok(_) => Ok(true),
+ Err(e) => {
+ if e.raw_os_error() == Some(libc::EWOULDBLOCK) ||
+ e.raw_os_error() == Some(libc::EAGAIN) {
+ Ok(false)
+ } else {
+ Err(e)
+ }
+ }
+ }
+ }
+}
diff --git a/ctr-std/src/sys/horizon/process.rs b/ctr-std/src/sys/horizon/process.rs
new file mode 100644
index 0000000..15b0e31
--- /dev/null
+++ b/ctr-std/src/sys/horizon/process.rs
@@ -0,0 +1,151 @@
+// 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.
+
+use ffi::OsStr;
+use fmt;
+use io;
+use sys::fs::File;
+use sys::pipe::AnonPipe;
+use sys::{unsupported, Void};
+use sys_common::process::{CommandEnv, DefaultEnvKey};
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct Command {
+ env: CommandEnv<DefaultEnvKey>
+}
+
+// passed back to std::process with the pipes connected to the child, if any
+// were requested
+pub struct StdioPipes {
+ pub stdin: Option<AnonPipe>,
+ pub stdout: Option<AnonPipe>,
+ pub stderr: Option<AnonPipe>,
+}
+
+pub enum Stdio {
+ Inherit,
+ Null,
+ MakePipe,
+}
+
+impl Command {
+ pub fn new(_program: &OsStr) -> Command {
+ Command {
+ env: Default::default()
+ }
+ }
+
+ pub fn arg(&mut self, _arg: &OsStr) {
+ }
+
+ pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> {
+ &mut self.env
+ }
+
+ pub fn cwd(&mut self, _dir: &OsStr) {
+ }
+
+ pub fn stdin(&mut self, _stdin: Stdio) {
+ }
+
+ pub fn stdout(&mut self, _stdout: Stdio) {
+ }
+
+ pub fn stderr(&mut self, _stderr: Stdio) {
+ }
+
+ pub fn spawn(&mut self, _default: Stdio, _needs_stdin: bool)
+ -> io::Result<(Process, StdioPipes)> {
+ unsupported()
+ }
+}
+
+impl From<AnonPipe> for Stdio {
+ fn from(pipe: AnonPipe) -> Stdio {
+ pipe.diverge()
+ }
+}
+
+impl From<File> for Stdio {
+ fn from(_file: File) -> Stdio {
+ //file.diverge()
+ unimplemented!()
+ }
+}
+
+impl fmt::Debug for Command {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ Ok(())
+ }
+}
+
+pub struct ExitStatus(Void);
+
+impl ExitStatus {
+ pub fn success(&self) -> bool {
+ match self.0 {}
+ }
+
+ pub fn code(&self) -> Option<i32> {
+ match self.0 {}
+ }
+}
+
+impl Clone for ExitStatus {
+ fn clone(&self) -> ExitStatus {
+ match self.0 {}
+ }
+}
+
+impl Copy for ExitStatus {}
+
+impl PartialEq for ExitStatus {
+ fn eq(&self, _other: &ExitStatus) -> bool {
+ match self.0 {}
+ }
+}
+
+impl Eq for ExitStatus {
+}
+
+impl fmt::Debug for ExitStatus {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+impl fmt::Display for ExitStatus {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+pub struct Process(Void);
+
+impl Process {
+ pub fn id(&self) -> u32 {
+ match self.0 {}
+ }
+
+ pub fn kill(&mut self) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ match self.0 {}
+ }
+
+ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+ match self.0 {}
+ }
+} \ No newline at end of file
diff --git a/ctr-std/src/sys/horizon/rand.rs b/ctr-std/src/sys/horizon/rand.rs
new file mode 100644
index 0000000..3ae694a
--- /dev/null
+++ b/ctr-std/src/sys/horizon/rand.rs
@@ -0,0 +1,241 @@
+// 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 mem;
+use slice;
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+ let mut v = (0, 0);
+ unsafe {
+ let view = slice::from_raw_parts_mut(&mut v as *mut _ as *mut u8,
+ mem::size_of_val(&v));
+ imp::fill_bytes(view);
+ }
+ return v
+}
+
+#[cfg(target_os = "horizon")]
+mod imp {
+ use libctru;
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ unsafe {
+ // Initializing and de-initializing the sslC subsystem every time
+ // we initialize a hashmap is pretty dumb, but I can't think of a
+ // better method at the moment.
+ //
+ // lazy_static won't work because
+ // destructors (for closing the subsystem on exit) won't run.
+ //
+ // Perhaps overriding __appInit() and __appExit() will work,
+ // but that's an experiment for another time.
+ libctru::sslcInit(0);
+ libctru::sslcGenerateRandomData(v.as_ptr() as _, v.len() as u32);
+ libctru::sslcExit();
+ }
+ }
+}
+
+#[cfg(all(unix,
+ not(target_os = "ios"),
+ not(target_os = "openbsd"),
+ not(target_os = "freebsd"),
+ not(target_os = "fuchsia"),
+ not(target_os = "horizon")
+ ))]
+mod imp {
+ use fs::File;
+ use io::Read;
+ use libc;
+ use sys::os::errno;
+
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ fn getrandom(buf: &mut [u8]) -> libc::c_long {
+ unsafe {
+ libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), libc::GRND_NONBLOCK)
+ }
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }
+
+ fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
+ let mut read = 0;
+ while read < v.len() {
+ let result = getrandom(&mut v[read..]);
+ if result == -1 {
+ let err = errno() as libc::c_int;
+ if err == libc::EINTR {
+ continue;
+ } else if err == libc::EAGAIN {
+ return false
+ } else {
+ panic!("unexpected getrandom error: {}", err);
+ }
+ } else {
+ read += result as usize;
+ }
+ }
+
+ return true
+ }
+
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ fn is_getrandom_available() -> bool {
+ use io;
+ use sync::atomic::{AtomicBool, Ordering};
+ use sync::Once;
+
+ static CHECKER: Once = Once::new();
+ static AVAILABLE: AtomicBool = AtomicBool::new(false);
+
+ CHECKER.call_once(|| {
+ let mut buf: [u8; 0] = [];
+ let result = getrandom(&mut buf);
+ let available = if result == -1 {
+ let err = io::Error::last_os_error().raw_os_error();
+ err != Some(libc::ENOSYS)
+ } else {
+ true
+ };
+ AVAILABLE.store(available, Ordering::Relaxed);
+ });
+
+ AVAILABLE.load(Ordering::Relaxed)
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ fn is_getrandom_available() -> bool { false }
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ // getrandom_fill_bytes here can fail if getrandom() returns EAGAIN,
+ // meaning it would have blocked because the non-blocking pool (urandom)
+ // has not initialized in the kernel yet due to a lack of entropy the
+ // fallback we do here is to avoid blocking applications which could
+ // depend on this call without ever knowing they do and don't have a
+ // work around. The PRNG of /dev/urandom will still be used but not
+ // over a completely full entropy pool
+ if is_getrandom_available() && getrandom_fill_bytes(v) {
+ return
+ }
+
+ let mut file = File::open("/dev/urandom")
+ .expect("failed to open /dev/urandom");
+ file.read_exact(v).expect("failed to read /dev/urandom");
+ }
+}
+
+#[cfg(target_os = "openbsd")]
+mod imp {
+ use libc;
+ use sys::os::errno;
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ // getentropy(2) permits a maximum buffer size of 256 bytes
+ for s in v.chunks_mut(256) {
+ let ret = unsafe {
+ libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len())
+ };
+ if ret == -1 {
+ panic!("unexpected getentropy error: {}", errno());
+ }
+ }
+ }
+}
+
+#[cfg(target_os = "ios")]
+mod imp {
+ use io;
+ use libc::{c_int, size_t};
+ use ptr;
+
+ enum SecRandom {}
+
+ #[allow(non_upper_case_globals)]
+ const kSecRandomDefault: *const SecRandom = ptr::null();
+
+ extern {
+ fn SecRandomCopyBytes(rnd: *const SecRandom,
+ count: size_t,
+ bytes: *mut u8) -> c_int;
+ }
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ let ret = unsafe {
+ SecRandomCopyBytes(kSecRandomDefault,
+ v.len(),
+ v.as_mut_ptr())
+ };
+ if ret == -1 {
+ panic!("couldn't generate random bytes: {}",
+ io::Error::last_os_error());
+ }
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+mod imp {
+ use libc;
+ use ptr;
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ let mib = [libc::CTL_KERN, libc::KERN_ARND];
+ // kern.arandom permits a maximum buffer size of 256 bytes
+ for s in v.chunks_mut(256) {
+ let mut s_len = s.len();
+ let ret = unsafe {
+ libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint,
+ s.as_mut_ptr() as *mut _, &mut s_len,
+ ptr::null(), 0)
+ };
+ if ret == -1 || s_len != s.len() {
+ panic!("kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})",
+ ret, s.len(), s_len);
+ }
+ }
+ }
+}
+
+#[cfg(target_os = "fuchsia")]
+mod imp {
+ #[link(name = "zircon")]
+ extern {
+ fn zx_cprng_draw(buffer: *mut u8, len: usize, actual: *mut usize) -> i32;
+ }
+
+ fn getrandom(buf: &mut [u8]) -> Result<usize, i32> {
+ unsafe {
+ let mut actual = 0;
+ let status = zx_cprng_draw(buf.as_mut_ptr(), buf.len(), &mut actual);
+ if status == 0 {
+ Ok(actual)
+ } else {
+ Err(status)
+ }
+ }
+ }
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ let mut buf = v;
+ while !buf.is_empty() {
+ let ret = getrandom(buf);
+ match ret {
+ Err(err) => {
+ panic!("kernel zx_cprng_draw call failed! (returned {}, buf.len() {})",
+ err, buf.len())
+ }
+ Ok(actual) => {
+ let move_buf = buf;
+ buf = &mut move_buf[(actual as usize)..];
+ }
+ }
+ }
+ }
+}
diff --git a/ctr-std/src/sys/horizon/rwlock.rs b/ctr-std/src/sys/horizon/rwlock.rs
new file mode 100644
index 0000000..4a802e9
--- /dev/null
+++ b/ctr-std/src/sys/horizon/rwlock.rs
@@ -0,0 +1,127 @@
+// 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 cell::UnsafeCell;
+use super::mutex::Mutex;
+use super::condvar::Condvar;
+
+// A simple read-preferring RWLock implementation that I found on wikipedia <.<
+pub struct RWLock {
+ mutex: Mutex,
+ cvar: Condvar,
+ reader_count: UnsafeCell<u32>,
+ writer_active: UnsafeCell<bool>,
+}
+
+unsafe impl Send for RWLock {}
+unsafe impl Sync for RWLock {}
+
+impl RWLock {
+ pub const fn new() -> RWLock {
+ RWLock {
+ mutex: Mutex::new(),
+ cvar: Condvar::new(),
+ reader_count: UnsafeCell::new(0),
+ writer_active: UnsafeCell::new(false),
+ }
+ }
+
+ #[inline]
+ pub unsafe fn read(&self) {
+ self.mutex.lock();
+
+ while *self.writer_active.get() {
+ self.cvar.wait(&self.mutex);
+ }
+
+ assert!(*self.reader_count.get() != u32::max_value());
+ *self.reader_count.get() += 1;
+
+ self.mutex.unlock();
+ }
+
+ #[inline]
+ pub unsafe fn try_read(&self) -> bool {
+ if !self.mutex.try_lock() {
+ return false
+ }
+
+ while *self.writer_active.get() {
+ self.cvar.wait(&self.mutex);
+ }
+
+ assert!(*self.reader_count.get() != u32::max_value());
+ *self.reader_count.get() += 1;
+
+ self.mutex.unlock();
+ true
+ }
+
+ #[inline]
+ pub unsafe fn write(&self) {
+ self.mutex.lock();
+
+ while *self.writer_active.get() || *self.reader_count.get() > 0 {
+ self.cvar.wait(&self.mutex);
+ }
+
+ *self.writer_active.get() = true;
+
+ self.mutex.unlock();
+ }
+
+ #[inline]
+ pub unsafe fn try_write(&self) -> bool {
+ if !self.mutex.try_lock() {
+ return false;
+ }
+
+ while *self.writer_active.get() || *self.reader_count.get() > 0 {
+ self.cvar.wait(&self.mutex);
+ }
+
+ *self.writer_active.get() = true;
+
+ self.mutex.unlock();
+ true
+ }
+
+ #[inline]
+ pub unsafe fn read_unlock(&self) {
+ self.mutex.lock();
+
+ *self.reader_count.get() -= 1;
+
+ if *self.reader_count.get() == 0 {
+ self.cvar.notify_one()
+ }
+
+ self.mutex.unlock();
+ }
+
+ #[inline]
+ pub unsafe fn write_unlock(&self) {
+ self.mutex.lock();
+
+ *self.writer_active.get() = false;
+
+ self.cvar.notify_all();
+
+ self.mutex.unlock();
+ }
+
+ #[inline]
+ pub unsafe fn destroy(&self) {
+ self.mutex.destroy();
+ self.cvar.destroy();
+ *self.reader_count.get() = 0;
+ *self.writer_active.get() = false;
+ }
+}
diff --git a/ctr-std/src/sys/horizon/stack_overflow.rs b/ctr-std/src/sys/horizon/stack_overflow.rs
new file mode 100644
index 0000000..40453f9
--- /dev/null
+++ b/ctr-std/src/sys/horizon/stack_overflow.rs
@@ -0,0 +1,220 @@
+// 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.
+
+#![cfg_attr(test, allow(dead_code))]
+
+use libc;
+use self::imp::{make_handler, drop_handler};
+
+pub use self::imp::cleanup;
+pub use self::imp::init;
+
+pub struct Handler {
+ _data: *mut libc::c_void
+}
+
+impl Handler {
+ pub unsafe fn new() -> Handler {
+ make_handler()
+ }
+}
+
+impl Drop for Handler {
+ fn drop(&mut self) {
+ unsafe {
+ drop_handler(self);
+ }
+ }
+}
+
+#[cfg(any(target_os = "linux",
+ target_os = "macos",
+ target_os = "bitrig",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "solaris",
+ all(target_os = "netbsd", not(target_vendor = "rumprun")),
+ target_os = "openbsd"))]
+mod imp {
+ use super::Handler;
+ use mem;
+ use ptr;
+ use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE};
+ use libc::{sigaction, SIGBUS, SIG_DFL,
+ SA_SIGINFO, SA_ONSTACK, sighandler_t};
+ use libc;
+ use libc::{mmap, munmap};
+ use libc::{SIGSEGV, PROT_READ, PROT_WRITE, MAP_PRIVATE, MAP_ANON};
+ use libc::MAP_FAILED;
+
+ use sys_common::thread_info;
+
+
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
+ #[repr(C)]
+ struct siginfo_t {
+ a: [libc::c_int; 3], // si_signo, si_errno, si_code
+ si_addr: *mut libc::c_void,
+ }
+
+ (*(info as *const siginfo_t)).si_addr as usize
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
+ (*info).si_addr as usize
+ }
+
+ // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
+ // (unmapped pages) at the end of every thread's stack, so if a thread ends
+ // up running into the guard page it'll trigger this handler. We want to
+ // detect these cases and print out a helpful error saying that the stack
+ // has overflowed. All other signals, however, should go back to what they
+ // were originally supposed to do.
+ //
+ // This handler currently exists purely to print an informative message
+ // whenever a thread overflows its stack. We then abort to exit and
+ // indicate a crash, but to avoid a misleading SIGSEGV that might lead
+ // users to believe that unsafe code has accessed an invalid pointer; the
+ // SIGSEGV encountered when overflowing the stack is expected and
+ // well-defined.
+ //
+ // If this is not a stack overflow, the handler un-registers itself and
+ // then returns (to allow the original signal to be delivered again).
+ // Returning from this kind of signal handler is technically not defined
+ // to work when reading the POSIX spec strictly, but in practice it turns
+ // out many large systems and all implementations allow returning from a
+ // signal handler to work. For a more detailed explanation see the
+ // comments on #26458.
+ unsafe extern fn signal_handler(signum: libc::c_int,
+ info: *mut libc::siginfo_t,
+ _data: *mut libc::c_void) {
+ use sys_common::util::report_overflow;
+
+ let guard = thread_info::stack_guard().unwrap_or(0..0);
+ let addr = siginfo_si_addr(info);
+
+ // If the faulting address is within the guard page, then we print a
+ // message saying so and abort.
+ if guard.start <= addr && addr < guard.end {
+ report_overflow();
+ rtabort!("stack overflow");
+ } else {
+ // Unregister ourselves by reverting back to the default behavior.
+ let mut action: sigaction = mem::zeroed();
+ action.sa_sigaction = SIG_DFL;
+ sigaction(signum, &action, ptr::null_mut());
+
+ // See comment above for why this function returns.
+ }
+ }
+
+ static mut MAIN_ALTSTACK: *mut libc::c_void = ptr::null_mut();
+
+ pub unsafe fn init() {
+ let mut action: sigaction = mem::zeroed();
+ action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+ action.sa_sigaction = signal_handler as sighandler_t;
+ sigaction(SIGSEGV, &action, ptr::null_mut());
+ sigaction(SIGBUS, &action, ptr::null_mut());
+
+ let handler = make_handler();
+ MAIN_ALTSTACK = handler._data;
+ mem::forget(handler);
+ }
+
+ pub unsafe fn cleanup() {
+ Handler { _data: MAIN_ALTSTACK };
+ }
+
+ unsafe fn get_stackp() -> *mut libc::c_void {
+ let stackp = mmap(ptr::null_mut(),
+ SIGSTKSZ,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON,
+ -1,
+ 0);
+ if stackp == MAP_FAILED {
+ panic!("failed to allocate an alternative stack");
+ }
+ stackp
+ }
+
+ #[cfg(any(target_os = "linux",
+ target_os = "macos",
+ target_os = "bitrig",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ unsafe fn get_stack() -> libc::stack_t {
+ libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ }
+ }
+
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly"))]
+ unsafe fn get_stack() -> libc::stack_t {
+ libc::stack_t { ss_sp: get_stackp() as *mut i8, ss_flags: 0, ss_size: SIGSTKSZ }
+ }
+
+ pub unsafe fn make_handler() -> Handler {
+ let mut stack = mem::zeroed();
+ sigaltstack(ptr::null(), &mut stack);
+ // Configure alternate signal stack, if one is not already set.
+ if stack.ss_flags & SS_DISABLE != 0 {
+ stack = get_stack();
+ sigaltstack(&stack, ptr::null_mut());
+ Handler { _data: stack.ss_sp as *mut libc::c_void }
+ } else {
+ Handler { _data: ptr::null_mut() }
+ }
+ }
+
+ pub unsafe fn drop_handler(handler: &mut Handler) {
+ if !handler._data.is_null() {
+ let stack = libc::stack_t {
+ ss_sp: ptr::null_mut(),
+ ss_flags: SS_DISABLE,
+ // Workaround for bug in macOS implementation of sigaltstack
+ // UNIX2003 which returns ENOMEM when disabling a stack while
+ // passing ss_size smaller than MINSIGSTKSZ. According to POSIX
+ // both ss_sp and ss_size should be ignored in this case.
+ ss_size: SIGSTKSZ,
+ };
+ sigaltstack(&stack, ptr::null_mut());
+ munmap(handler._data, SIGSTKSZ);
+ }
+ }
+}
+
+#[cfg(not(any(target_os = "linux",
+ target_os = "macos",
+ target_os = "bitrig",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "solaris",
+ all(target_os = "netbsd", not(target_vendor = "rumprun")),
+ target_os = "openbsd")))]
+mod imp {
+ use ptr;
+
+ pub unsafe fn init() {
+ }
+
+ pub unsafe fn cleanup() {
+ }
+
+ pub unsafe fn make_handler() -> super::Handler {
+ super::Handler { _data: ptr::null_mut() }
+ }
+
+ pub unsafe fn drop_handler(_handler: &mut super::Handler) {
+ }
+}
diff --git a/ctr-std/src/sys/horizon/stdio.rs b/ctr-std/src/sys/horizon/stdio.rs
new file mode 100644
index 0000000..87ba2ae
--- /dev/null
+++ b/ctr-std/src/sys/horizon/stdio.rs
@@ -0,0 +1,81 @@
+// 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 io;
+use libc;
+use sys::fd::FileDesc;
+
+pub struct Stdin(());
+pub struct Stdout(());
+pub struct Stderr(());
+
+impl Stdin {
+ pub fn new() -> io::Result<Stdin> { Ok(Stdin(())) }
+
+ pub fn read(&self, data: &mut [u8]) -> io::Result<usize> {
+ let fd = FileDesc::new(libc::STDIN_FILENO);
+ let ret = fd.read(data);
+ fd.into_raw();
+ ret
+ }
+}
+
+impl Stdout {
+ pub fn new() -> io::Result<Stdout> { Ok(Stdout(())) }
+
+ pub fn write(&self, data: &[u8]) -> io::Result<usize> {
+ let fd = FileDesc::new(libc::STDOUT_FILENO);
+ let ret = fd.write(data);
+ fd.into_raw();
+ ret
+ }
+
+ 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> {
+ let fd = FileDesc::new(libc::STDERR_FILENO);
+ let ret = fd.write(data);
+ fd.into_raw();
+ ret
+ }
+
+ 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)
+ }
+}
+
+pub fn is_ebadf(err: &io::Error) -> bool {
+ err.raw_os_error() == Some(libc::EBADF as i32)
+}
+
+pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE;
+
+pub fn stderr_prints_nothing() -> bool {
+ false
+}
diff --git a/ctr-std/src/sys/horizon/thread.rs b/ctr-std/src/sys/horizon/thread.rs
new file mode 100644
index 0000000..694f85a
--- /dev/null
+++ b/ctr-std/src/sys/horizon/thread.rs
@@ -0,0 +1,106 @@
+// 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.
+
+use alloc_crate::boxed::FnBox;
+use libc;
+use cmp;
+use ffi::CStr;
+use io;
+use mem;
+use ptr;
+use sys_common::thread::start_thread;
+use time::Duration;
+
+use libctru::Thread as ThreadHandle;
+
+pub struct Thread {
+ handle: ThreadHandle,
+}
+
+unsafe impl Send for Thread {}
+unsafe impl Sync for Thread {}
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
+
+
+impl Thread {
+ pub unsafe fn new<'a>(stack: usize, p: Box<FnBox() + 'a>) -> io::Result<Thread> {
+ let p = box p;
+ let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE);
+
+ let mut priority = 0;
+ ::libctru::svcGetThreadPriority(&mut priority, 0xFFFF8000);
+
+ let handle = ::libctru::threadCreate(Some(thread_func), &*p as *const _ as *mut _,
+ stack_size, priority, -2, false);
+
+ return if handle == ptr::null_mut() {
+ Err(io::Error::from_raw_os_error(libc::EAGAIN))
+ } else {
+ mem::forget(p); // ownership passed to the new thread
+ Ok(Thread { handle: handle })
+ };
+
+ extern "C" fn thread_func(start: *mut libc::c_void) {
+ unsafe { start_thread(start as *mut u8) }
+ }
+ }
+
+ pub fn yield_now() {
+ unsafe {
+ ::libctru::svcSleepThread(0)
+ }
+ }
+
+ pub fn set_name(_name: &CStr) {
+ // threads aren't named in libctru
+ }
+
+ pub fn sleep(dur: Duration) {
+ unsafe {
+ let nanos = dur.as_secs()
+ .saturating_mul(1_000_000_000)
+ .saturating_add(dur.subsec_nanos() as u64);
+ ::libctru::svcSleepThread(nanos as i64)
+ }
+ }
+
+ pub fn join(self) {
+ unsafe {
+ let ret = ::libctru::threadJoin(self.handle, u64::max_value());
+ ::libctru::threadFree(self.handle);
+ mem::forget(self);
+ debug_assert_eq!(ret, 0);
+ }
+ }
+
+ #[allow(dead_code)]
+ pub fn id(&self) -> ThreadHandle {
+ self.handle
+ }
+
+ #[allow(dead_code)]
+ pub fn into_id(self) -> ThreadHandle {
+ let handle = self.handle;
+ mem::forget(self);
+ handle
+ }
+}
+
+impl Drop for Thread {
+ fn drop(&mut self) {
+ unsafe { ::libctru::threadDetach(self.handle) }
+ }
+}
+
+pub mod guard {
+ pub unsafe fn current() -> Option<usize> { None }
+ pub unsafe fn init() -> Option<usize> { None }
+}
diff --git a/ctr-std/src/sys/horizon/thread_local.rs b/ctr-std/src/sys/horizon/thread_local.rs
new file mode 100644
index 0000000..7c9c1fc
--- /dev/null
+++ b/ctr-std/src/sys/horizon/thread_local.rs
@@ -0,0 +1,71 @@
+// 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.
+
+#![allow(dead_code)] // not used on all platforms
+
+use collections::BTreeMap;
+use ptr;
+use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
+
+pub type Key = usize;
+
+type Dtor = unsafe extern fn(*mut u8);
+
+static NEXT_KEY: AtomicUsize = ATOMIC_USIZE_INIT;
+
+static mut KEYS: *mut BTreeMap<Key, Option<Dtor>> = ptr::null_mut();
+
+#[thread_local]
+static mut LOCALS: *mut BTreeMap<Key, *mut u8> = ptr::null_mut();
+
+unsafe fn keys() -> &'static mut BTreeMap<Key, Option<Dtor>> {
+ if KEYS == ptr::null_mut() {
+ KEYS = Box::into_raw(Box::new(BTreeMap::new()));
+ }
+ &mut *KEYS
+}
+
+unsafe fn locals() -> &'static mut BTreeMap<Key, *mut u8> {
+ if LOCALS == ptr::null_mut() {
+ LOCALS = Box::into_raw(Box::new(BTreeMap::new()));
+ }
+ &mut *LOCALS
+}
+
+#[inline]
+pub unsafe fn create(dtor: Option<Dtor>) -> Key {
+ let key = NEXT_KEY.fetch_add(1, Ordering::SeqCst);
+ keys().insert(key, dtor);
+ key
+}
+
+#[inline]
+pub unsafe fn get(key: Key) -> *mut u8 {
+ if let Some(&entry) = locals().get(&key) {
+ entry
+ } else {
+ ptr::null_mut()
+ }
+}
+
+#[inline]
+pub unsafe fn set(key: Key, value: *mut u8) {
+ locals().insert(key, value);
+}
+
+#[inline]
+pub unsafe fn destroy(key: Key) {
+ keys().remove(&key);
+}
+
+#[inline]
+pub fn requires_synchronized_create() -> bool {
+ false
+} \ No newline at end of file
diff --git a/ctr-std/src/sys/horizon/time.rs b/ctr-std/src/sys/horizon/time.rs
new file mode 100644
index 0000000..aa66cf1
--- /dev/null
+++ b/ctr-std/src/sys/horizon/time.rs
@@ -0,0 +1,285 @@
+// 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 libc;
+use time::Duration;
+use core::hash::{Hash, Hasher};
+
+pub use self::inner::{Instant, SystemTime, UNIX_EPOCH};
+
+const NSEC_PER_SEC: u64 = 1_000_000_000;
+
+#[derive(Copy, Clone)]
+struct Timespec {
+ t: libc::timespec,
+}
+
+impl Timespec {
+ fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
+ if self >= other {
+ Ok(if self.t.tv_nsec >= other.t.tv_nsec {
+ Duration::new((self.t.tv_sec - other.t.tv_sec) as u64,
+ (self.t.tv_nsec - other.t.tv_nsec) as u32)
+ } else {
+ Duration::new((self.t.tv_sec - 1 - other.t.tv_sec) as u64,
+ self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) -
+ other.t.tv_nsec as u32)
+ })
+ } else {
+ match other.sub_timespec(self) {
+ Ok(d) => Err(d),
+ Err(d) => Ok(d),
+ }
+ }
+ }
+
+ fn add_duration(&self, other: &Duration) -> Timespec {
+ let secs = (self.t.tv_sec as i64).checked_add(other.as_secs() as i64);
+ let mut secs = secs.expect("overflow when adding duration to time");
+
+ // Nano calculations can't overflow because nanos are <1B which fit
+ // in a u32.
+ let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32;
+ if nsec >= NSEC_PER_SEC as u32 {
+ nsec -= NSEC_PER_SEC as u32;
+ secs = secs.checked_add(1).expect("overflow when adding \
+ duration to time");
+ }
+ Timespec {
+ t: libc::timespec {
+ tv_sec: secs as libc::time_t,
+ tv_nsec: nsec as libc::c_long,
+ },
+ }
+ }
+
+ fn sub_duration(&self, other: &Duration) -> Timespec {
+ let secs = (self.t.tv_sec as i64).checked_sub(other.as_secs() as i64);
+ let mut secs = secs.expect("overflow when subtracting duration \
+ from time");
+
+ // Similar to above, nanos can't overflow.
+ let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;
+ if nsec < 0 {
+ nsec += NSEC_PER_SEC as i32;
+ secs = secs.checked_sub(1).expect("overflow when subtracting \
+ duration from time");
+ }
+ Timespec {
+ t: libc::timespec {
+ tv_sec: secs as libc::time_t,
+ tv_nsec: nsec as libc::c_long,
+ },
+ }
+ }
+}
+
+impl PartialEq for Timespec {
+ fn eq(&self, other: &Timespec) -> bool {
+ self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec
+ }
+}
+
+impl Eq for Timespec {}
+
+impl PartialOrd for Timespec {
+ fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Timespec {
+ fn cmp(&self, other: &Timespec) -> Ordering {
+ let me = (self.t.tv_sec, self.t.tv_nsec);
+ let other = (other.t.tv_sec, other.t.tv_nsec);
+ me.cmp(&other)
+ }
+}
+
+impl Hash for Timespec {
+ fn hash<H : Hasher>(&self, state: &mut H) {
+ self.t.tv_sec.hash(state);
+ self.t.tv_nsec.hash(state);
+ }
+}
+
+mod inner {
+ use fmt;
+ use libc;
+ use sync::Once;
+ use sys::cvt;
+ use sys_common::mul_div_u64;
+ use time::Duration;
+
+ use super::NSEC_PER_SEC;
+ use super::Timespec;
+
+ use libctru;
+
+ #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+ pub struct Instant {
+ t: u64
+ }
+
+ #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+ pub struct SystemTime {
+ t: Timespec,
+ }
+
+ pub const UNIX_EPOCH: SystemTime = SystemTime {
+ t: Timespec {
+ t: libc::timespec {
+ tv_sec: 0,
+ tv_nsec: 0,
+ },
+ },
+ };
+
+ impl Instant {
+ pub fn now() -> Instant {
+ Instant { t: ctr_absolute_time() }
+ }
+
+ pub fn sub_instant(&self, other: &Instant) -> Duration {
+ let info = info();
+ let diff = self.t.checked_sub(other.t)
+ .expect("second instant is later than self");
+ let nanos = mul_div_u64(diff, info.numer as u64, info.denom as u64);
+ Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32)
+ }
+
+ pub fn add_duration(&self, other: &Duration) -> Instant {
+ Instant {
+ t: self.t.checked_add(dur2intervals(other))
+ .expect("overflow when adding duration to instant"),
+ }
+ }
+
+ pub fn sub_duration(&self, other: &Duration) -> Instant {
+ Instant {
+ t: self.t.checked_sub(dur2intervals(other))
+ .expect("overflow when adding duration to instant"),
+ }
+ }
+ }
+
+ // The initial system tick after which all Instants occur
+ static mut TICK: u64 = 0;
+
+ // A source of monotonic time based on ticks of the 3DS CPU. Returns the
+ // number of system ticks elapsed since an arbitrary point in the past
+ fn ctr_absolute_time() -> u64 {
+ let first_tick = get_first_tick();
+ let current_tick = get_system_tick();
+ current_tick - first_tick
+ }
+
+ // The first time this function is called, it generates and returns the
+ // initial system tick used to create Instants
+ //
+ // subsequent calls to this function return the previously generated
+ // tick value
+ fn get_first_tick() -> u64 {
+ static ONCE: Once = Once::new();
+ unsafe {
+ ONCE.call_once(|| {
+ TICK = get_system_tick();
+ });
+ TICK
+ }
+ }
+
+ // Gets the current system tick
+ #[inline]
+ fn get_system_tick() -> u64 {
+ unsafe { libctru::svcGetSystemTick() }
+ }
+
+ // A struct representing the clock speed of the 3DS
+ struct CtrClockInfo {
+ numer: u32,
+ denom: u32,
+ }
+
+ // Initializes the CtrClockInfo struct
+ //
+ // Note that svcGetSystemTick always runs at 268MHz (268,111,856Hz), even
+ // on a New 3DS running in 804MHz mode
+ //
+ // See https://www.3dbrew.org/wiki/Hardware#Common_hardware
+ fn info() -> &'static CtrClockInfo {
+ static INFO: CtrClockInfo = CtrClockInfo {
+ numer: 1_000_000_000,
+ denom: 268_111_856,
+ };
+ &INFO
+ }
+
+ fn dur2intervals(dur: &Duration) -> u64 {
+ let info = info();
+ let nanos = dur.as_secs().checked_mul(NSEC_PER_SEC).and_then(|nanos| {
+ nanos.checked_add(dur.subsec_nanos() as u64)
+ }).expect("overflow converting duration to nanoseconds");
+ mul_div_u64(nanos, info.denom as u64, info.numer as u64)
+ }
+
+ impl SystemTime {
+ pub fn now() -> SystemTime {
+ use ptr;
+
+ let mut s = libc::timeval {
+ tv_sec: 0,
+ tv_usec: 0,
+ };
+ cvt(unsafe {
+ libc::gettimeofday(&mut s, ptr::null_mut())
+ }).unwrap();
+ return SystemTime::from(s)
+ }
+
+ pub fn sub_time(&self, other: &SystemTime)
+ -> Result<Duration, Duration> {
+ self.t.sub_timespec(&other.t)
+ }
+
+ pub fn add_duration(&self, other: &Duration) -> SystemTime {
+ SystemTime { t: self.t.add_duration(other) }
+ }
+
+ pub fn sub_duration(&self, other: &Duration) -> SystemTime {
+ SystemTime { t: self.t.sub_duration(other) }
+ }
+ }
+
+ impl From<libc::timeval> for SystemTime {
+ fn from(t: libc::timeval) -> SystemTime {
+ SystemTime::from(libc::timespec {
+ tv_sec: t.tv_sec,
+ tv_nsec: (t.tv_usec * 1000) as libc::c_long,
+ })
+ }
+ }
+
+ impl From<libc::timespec> for SystemTime {
+ fn from(t: libc::timespec) -> SystemTime {
+ SystemTime { t: Timespec { t: t } }
+ }
+ }
+
+ impl fmt::Debug for SystemTime {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("SystemTime")
+ .field("tv_sec", &self.t.t.tv_sec)
+ .field("tv_nsec", &self.t.t.tv_nsec)
+ .finish()
+ }
+ }
+} \ No newline at end of file
diff --git a/ctr-std/src/sys/horizon/weak.rs b/ctr-std/src/sys/horizon/weak.rs
new file mode 100644
index 0000000..aa6394c
--- /dev/null
+++ b/ctr-std/src/sys/horizon/weak.rs
@@ -0,0 +1,79 @@
+// 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.
+
+//! Support for "weak linkage" to symbols on Unix
+//!
+//! Some I/O operations we do in libstd require newer versions of OSes but we
+//! need to maintain binary compatibility with older releases for now. In order
+//! to use the new functionality when available we use this module for
+//! detection.
+//!
+//! One option to use here is weak linkage, but that is unfortunately only
+//! really workable on Linux. Hence, use dlsym to get the symbol value at
+//! runtime. This is also done for compatibility with older versions of glibc,
+//! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that
+//! we've been dynamically linked to the library the symbol comes from, but that
+//! is currently always the case for things like libpthread/libc.
+//!
+//! A long time ago this used weak linkage for the __pthread_get_minstack
+//! symbol, but that caused Debian to detect an unnecessarily strict versioned
+//! dependency on libc6 (#23628).
+
+use libc;
+
+use ffi::CString;
+use marker;
+use mem;
+use sync::atomic::{AtomicUsize, Ordering};
+
+macro_rules! weak {
+ (fn $name:ident($($t:ty),*) -> $ret:ty) => (
+ static $name: ::sys::weak::Weak<unsafe extern fn($($t),*) -> $ret> =
+ ::sys::weak::Weak::new(stringify!($name));
+ )
+}
+
+pub struct Weak<F> {
+ name: &'static str,
+ addr: AtomicUsize,
+ _marker: marker::PhantomData<F>,
+}
+
+impl<F> Weak<F> {
+ pub const fn new(name: &'static str) -> Weak<F> {
+ Weak {
+ name,
+ addr: AtomicUsize::new(1),
+ _marker: marker::PhantomData,
+ }
+ }
+
+ pub fn get(&self) -> Option<&F> {
+ assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
+ unsafe {
+ if self.addr.load(Ordering::SeqCst) == 1 {
+ self.addr.store(fetch(self.name), Ordering::SeqCst);
+ }
+ if self.addr.load(Ordering::SeqCst) == 0 {
+ None
+ } else {
+ mem::transmute::<&AtomicUsize, Option<&F>>(&self.addr)
+ }
+ }
+ }
+}
+
+unsafe fn fetch(name: &str) -> usize {
+ let name = match CString::new(name) {
+ Ok(cstr) => cstr,
+ Err(..) => return 0,
+ };
+ libc::dlsym(0 as _, name.as_ptr() as *const u8) as usize
+}
diff --git a/ctr-std/src/sys/mod.rs b/ctr-std/src/sys/mod.rs
index 569336d..d0ed80e 100644
--- a/ctr-std/src/sys/mod.rs
+++ b/ctr-std/src/sys/mod.rs
@@ -33,9 +33,24 @@
#![allow(missing_debug_implementations)]
cfg_if! {
- if #[cfg(unix)] {
+ if #[cfg(target_os = "horizon")] {
+ mod horizon;
+ pub use self::horizon::*;
+ } else if #[cfg(unix)] {
mod unix;
pub use self::unix::*;
+ } else if #[cfg(windows)] {
+ mod windows;
+ pub use self::windows::*;
+ } else if #[cfg(target_os = "cloudabi")] {
+ mod cloudabi;
+ pub use self::cloudabi::*;
+ } else if #[cfg(target_os = "redox")] {
+ mod redox;
+ pub use self::redox::*;
+ } else if #[cfg(target_arch = "wasm32")] {
+ mod wasm;
+ pub use self::wasm::*;
} else {
compile_error!("libstd doesn't compile for this platform yet");
}
diff --git a/ctr-std/src/sys/redox/args.rs b/ctr-std/src/sys/redox/args.rs
new file mode 100644
index 0000000..59ae2a7
--- /dev/null
+++ b/ctr-std/src/sys/redox/args.rs
@@ -0,0 +1,111 @@
+// 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.
+
+//! Global initialization and retrieval of command line arguments.
+//!
+//! On some platforms these are stored during runtime startup,
+//! and on some they are retrieved from the system on demand.
+
+#![allow(dead_code)] // runtime init functions not used during testing
+
+use ffi::OsString;
+use marker::PhantomData;
+use vec;
+
+/// One-time global initialization.
+pub unsafe fn init(argc: isize, argv: *const *const u8) { imp::init(argc, argv) }
+
+/// One-time global cleanup.
+pub unsafe fn cleanup() { imp::cleanup() }
+
+/// Returns the command line arguments
+pub fn args() -> Args {
+ imp::args()
+}
+
+pub struct Args {
+ iter: vec::IntoIter<OsString>,
+ _dont_send_or_sync_me: PhantomData<*mut ()>,
+}
+
+impl Args {
+ pub fn inner_debug(&self) -> &[OsString] {
+ self.iter.as_slice()
+ }
+}
+
+impl Iterator for Args {
+ type Item = OsString;
+ fn next(&mut self) -> Option<OsString> { self.iter.next() }
+ fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
+}
+
+impl ExactSizeIterator for Args {
+ fn len(&self) -> usize { self.iter.len() }
+}
+
+impl DoubleEndedIterator for Args {
+ fn next_back(&mut self) -> Option<OsString> { self.iter.next_back() }
+}
+
+mod imp {
+ use os::unix::prelude::*;
+ use mem;
+ use ffi::{CStr, OsString};
+ use marker::PhantomData;
+ use libc;
+ use super::Args;
+
+ use sys_common::mutex::Mutex;
+
+ static mut GLOBAL_ARGS_PTR: usize = 0;
+ static LOCK: Mutex = Mutex::new();
+
+ pub unsafe fn init(argc: isize, argv: *const *const u8) {
+ let args = (0..argc).map(|i| {
+ CStr::from_ptr(*argv.offset(i) as *const libc::c_char).to_bytes().to_vec()
+ }).collect();
+
+ LOCK.lock();
+ let ptr = get_global_ptr();
+ assert!((*ptr).is_none());
+ (*ptr) = Some(box args);
+ LOCK.unlock();
+ }
+
+ pub unsafe fn cleanup() {
+ LOCK.lock();
+ *get_global_ptr() = None;
+ LOCK.unlock();
+ }
+
+ pub fn args() -> Args {
+ let bytes = clone().unwrap_or(Vec::new());
+ let v: Vec<OsString> = bytes.into_iter().map(|v| {
+ OsStringExt::from_vec(v)
+ }).collect();
+ Args { iter: v.into_iter(), _dont_send_or_sync_me: PhantomData }
+ }
+
+ fn clone() -> Option<Vec<Vec<u8>>> {
+ unsafe {
+ LOCK.lock();
+ let ptr = get_global_ptr();
+ let ret = (*ptr).as_ref().map(|s| (**s).clone());
+ LOCK.unlock();
+ return ret
+ }
+ }
+
+ fn get_global_ptr() -> *mut Option<Box<Vec<Vec<u8>>>> {
+ unsafe { mem::transmute(&GLOBAL_ARGS_PTR) }
+ }
+
+}
diff --git a/ctr-std/src/sys/redox/backtrace/mod.rs b/ctr-std/src/sys/redox/backtrace/mod.rs
new file mode 100644
index 0000000..40b957d
--- /dev/null
+++ b/ctr-std/src/sys/redox/backtrace/mod.rs
@@ -0,0 +1,42 @@
+// 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.
+
+/// See sys/unix/backtrace/mod.rs for an explanation of the method used here.
+
+pub use self::tracing::unwind_backtrace;
+pub use self::printing::{foreach_symbol_fileline, resolve_symname};
+
+// tracing impls:
+mod tracing;
+// symbol resolvers:
+mod printing;
+
+pub mod gnu {
+ use io;
+ use fs;
+ use libc::c_char;
+ use vec::Vec;
+ use ffi::OsStr;
+ use os::unix::ffi::OsStrExt;
+ use io::Read;
+
+ pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> {
+ let mut exefile = fs::File::open("sys:exe")?;
+ let mut exename = Vec::new();
+ exefile.read_to_end(&mut exename)?;
+ if exename.last() == Some(&b'\n') {
+ exename.pop();
+ }
+ let file = fs::File::open(OsStr::from_bytes(&exename))?;
+ Ok((exename.into_iter().map(|c| c as c_char).collect(), file))
+ }
+}
+
+pub struct BacktraceContext;
diff --git a/ctr-std/src/sys/redox/backtrace/printing.rs b/ctr-std/src/sys/redox/backtrace/printing.rs
new file mode 100644
index 0000000..3e937db
--- /dev/null
+++ b/ctr-std/src/sys/redox/backtrace/printing.rs
@@ -0,0 +1,11 @@
+// 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.
+
+pub use sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname};
diff --git a/ctr-std/src/sys/redox/backtrace/tracing.rs b/ctr-std/src/sys/redox/backtrace/tracing.rs
new file mode 100644
index 0000000..bb70ca3
--- /dev/null
+++ b/ctr-std/src/sys/redox/backtrace/tracing.rs
@@ -0,0 +1,107 @@
+// 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.
+
+use error::Error;
+use io;
+use libc;
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
+
+use unwind as uw;
+
+struct Context<'a> {
+ idx: usize,
+ frames: &'a mut [Frame],
+}
+
+#[derive(Debug)]
+struct UnwindError(uw::_Unwind_Reason_Code);
+
+impl Error for UnwindError {
+ fn description(&self) -> &'static str {
+ "unexpected return value while unwinding"
+ }
+}
+
+impl ::fmt::Display for UnwindError {
+ fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
+ write!(f, "{}: {:?}", self.description(), self.0)
+ }
+}
+
+#[inline(never)] // if we know this is a function call, we can skip it when
+ // tracing
+pub fn unwind_backtrace(frames: &mut [Frame])
+ -> io::Result<(usize, BacktraceContext)>
+{
+ let mut cx = Context {
+ idx: 0,
+ frames: frames,
+ };
+ let result_unwind = unsafe {
+ uw::_Unwind_Backtrace(trace_fn,
+ &mut cx as *mut Context
+ as *mut libc::c_void)
+ };
+ // See libunwind:src/unwind/Backtrace.c for the return values.
+ // No, there is no doc.
+ match result_unwind {
+ // These return codes seem to be benign and need to be ignored for backtraces
+ // to show up properly on all tested platforms.
+ uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => {
+ Ok((cx.idx, BacktraceContext))
+ }
+ _ => {
+ Err(io::Error::new(io::ErrorKind::Other,
+ UnwindError(result_unwind)))
+ }
+ }
+}
+
+extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
+ arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
+ let cx = unsafe { &mut *(arg as *mut Context) };
+ let mut ip_before_insn = 0;
+ let mut ip = unsafe {
+ uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void
+ };
+ if !ip.is_null() && ip_before_insn == 0 {
+ // this is a non-signaling frame, so `ip` refers to the address
+ // after the calling instruction. account for that.
+ ip = (ip as usize - 1) as *mut _;
+ }
+
+ // dladdr() on osx gets whiny when we use FindEnclosingFunction, and
+ // it appears to work fine without it, so we only use
+ // FindEnclosingFunction on non-osx platforms. In doing so, we get a
+ // slightly more accurate stack trace in the process.
+ //
+ // This is often because panic involves the last instruction of a
+ // function being "call std::rt::begin_unwind", with no ret
+ // instructions after it. This means that the return instruction
+ // pointer points *outside* of the calling function, and by
+ // unwinding it we go back to the original function.
+ let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
+ ip
+ } else {
+ unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
+ };
+
+ if cx.idx < cx.frames.len() {
+ cx.frames[cx.idx] = Frame {
+ symbol_addr: symaddr as *mut u8,
+ exact_position: ip as *mut u8,
+ inline_context: 0,
+ };
+ cx.idx += 1;
+ }
+
+ uw::_URC_NO_REASON
+}
diff --git a/ctr-std/src/sys/redox/cmath.rs b/ctr-std/src/sys/redox/cmath.rs
new file mode 100644
index 0000000..2bc9665
--- /dev/null
+++ b/ctr-std/src/sys/redox/cmath.rs
@@ -0,0 +1,43 @@
+// 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 acosf(n: c_float) -> c_float;
+ pub fn asin(n: c_double) -> c_double;
+ pub fn asinf(n: c_float) -> c_float;
+ pub fn atan(n: c_double) -> c_double;
+ pub fn atan2(a: c_double, b: c_double) -> c_double;
+ pub fn atan2f(a: c_float, b: c_float) -> c_float;
+ pub fn atanf(n: c_float) -> c_float;
+ 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 coshf(n: c_float) -> c_float;
+ 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;
+ pub fn hypot(x: c_double, y: c_double) -> c_double;
+ 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 sinhf(n: c_float) -> c_float;
+ pub fn tan(n: c_double) -> c_double;
+ pub fn tanf(n: c_float) -> c_float;
+ pub fn tanh(n: c_double) -> c_double;
+ pub fn tanhf(n: c_float) -> c_float;
+}
diff --git a/ctr-std/src/sys/redox/condvar.rs b/ctr-std/src/sys/redox/condvar.rs
new file mode 100644
index 0000000..2a611ed
--- /dev/null
+++ b/ctr-std/src/sys/redox/condvar.rs
@@ -0,0 +1,121 @@
+// 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 cell::UnsafeCell;
+use intrinsics::{atomic_cxchg, atomic_load, atomic_xadd, atomic_xchg};
+use ptr;
+use time::Duration;
+
+use sys::mutex::{mutex_unlock, Mutex};
+use sys::syscall::{futex, TimeSpec, FUTEX_WAIT, FUTEX_WAKE, FUTEX_REQUEUE};
+
+pub struct Condvar {
+ lock: UnsafeCell<*mut i32>,
+ seq: UnsafeCell<i32>
+}
+
+impl Condvar {
+ pub const fn new() -> Condvar {
+ Condvar {
+ lock: UnsafeCell::new(ptr::null_mut()),
+ seq: UnsafeCell::new(0)
+ }
+ }
+
+ #[inline]
+ pub unsafe fn init(&self) {
+ *self.lock.get() = ptr::null_mut();
+ *self.seq.get() = 0;
+ }
+
+ #[inline]
+ pub fn notify_one(&self) {
+ unsafe {
+ let seq = self.seq.get();
+
+ atomic_xadd(seq, 1);
+
+ let _ = futex(seq, FUTEX_WAKE, 1, 0, ptr::null_mut());
+ }
+ }
+
+ #[inline]
+ pub fn notify_all(&self) {
+ unsafe {
+ let lock = self.lock.get();
+ let seq = self.seq.get();
+
+ if *lock == ptr::null_mut() {
+ return;
+ }
+
+ atomic_xadd(seq, 1);
+
+ let _ = futex(seq, FUTEX_REQUEUE, 1, ::usize::MAX, *lock);
+ }
+ }
+
+ #[inline]
+ unsafe fn wait_inner(&self, mutex: &Mutex, timeout_ptr: *const TimeSpec) -> bool {
+ let lock = self.lock.get();
+ let seq = self.seq.get();
+
+ if *lock != mutex.lock.get() {
+ if *lock != ptr::null_mut() {
+ panic!("Condvar used with more than one Mutex");
+ }
+
+ atomic_cxchg(lock as *mut usize, 0, mutex.lock.get() as usize);
+ }
+
+ mutex_unlock(*lock);
+
+ let seq_before = atomic_load(seq);
+
+ let _ = futex(seq, FUTEX_WAIT, seq_before, timeout_ptr as usize, ptr::null_mut());
+
+ let seq_after = atomic_load(seq);
+
+ while atomic_xchg(*lock, 2) != 0 {
+ let _ = futex(*lock, FUTEX_WAIT, 2, 0, ptr::null_mut());
+ }
+
+ seq_before != seq_after
+ }
+
+ #[inline]
+ pub fn wait(&self, mutex: &Mutex) {
+ unsafe {
+ assert!(self.wait_inner(mutex, ptr::null()));
+ }
+ }
+
+ #[inline]
+ pub fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+ unsafe {
+ let timeout = TimeSpec {
+ tv_sec: dur.as_secs() as i64,
+ tv_nsec: dur.subsec_nanos() as i32
+ };
+
+ self.wait_inner(mutex, &timeout as *const TimeSpec)
+ }
+ }
+
+ #[inline]
+ pub unsafe fn destroy(&self) {
+ *self.lock.get() = ptr::null_mut();
+ *self.seq.get() = 0;
+ }
+}
+
+unsafe impl Send for Condvar {}
+
+unsafe impl Sync for Condvar {}
diff --git a/ctr-std/src/sys/redox/env.rs b/ctr-std/src/sys/redox/env.rs
new file mode 100644
index 0000000..669b752
--- /dev/null
+++ b/ctr-std/src/sys/redox/env.rs
@@ -0,0 +1,19 @@
+// 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.
+
+pub mod os {
+ pub const FAMILY: &'static str = "redox";
+ pub const OS: &'static str = "redox";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
diff --git a/ctr-std/src/sys/redox/ext/ffi.rs b/ctr-std/src/sys/redox/ext/ffi.rs
new file mode 100644
index 0000000..cd88c8f
--- /dev/null
+++ b/ctr-std/src/sys/redox/ext/ffi.rs
@@ -0,0 +1,65 @@
+// 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.
+
+//! Redox-specific extension to the primitives in the `std::ffi` module.
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+use ffi::{OsStr, OsString};
+use mem;
+use sys::os_str::Buf;
+use sys_common::{FromInner, IntoInner, AsInner};
+
+/// Redox-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 byte vector.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn from_vec(vec: Vec<u8>) -> Self;
+
+ /// Yields the underlying byte vector of this `OsString`.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn into_vec(self) -> Vec<u8>;
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl OsStringExt for OsString {
+ fn from_vec(vec: Vec<u8>) -> OsString {
+ FromInner::from_inner(Buf { inner: vec })
+ }
+ fn into_vec(self) -> Vec<u8> {
+ self.into_inner().inner
+ }
+}
+
+/// Redox-specific extensions to [`OsStr`].
+///
+/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub trait OsStrExt {
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn from_bytes(slice: &[u8]) -> &Self;
+
+ /// Gets the underlying byte view of the `OsStr` slice.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn as_bytes(&self) -> &[u8];
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl OsStrExt for OsStr {
+ fn from_bytes(slice: &[u8]) -> &OsStr {
+ unsafe { mem::transmute(slice) }
+ }
+ fn as_bytes(&self) -> &[u8] {
+ &self.as_inner().inner
+ }
+}
diff --git a/ctr-std/src/sys/redox/ext/fs.rs b/ctr-std/src/sys/redox/ext/fs.rs
new file mode 100644
index 0000000..c1dba6e
--- /dev/null
+++ b/ctr-std/src/sys/redox/ext/fs.rs
@@ -0,0 +1,349 @@
+// 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.
+
+//! Redox-specific extensions to primitives in the `std::fs` module.
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+use fs::{self, Permissions, OpenOptions};
+use io;
+use path::Path;
+use sys;
+use sys_common::{FromInner, AsInner, AsInnerMut};
+
+/// Redox-specific extensions to [`fs::Permissions`].
+///
+/// [`fs::Permissions`]: ../../../../std/fs/struct.Permissions.html
+#[stable(feature = "fs_ext", since = "1.1.0")]
+pub trait PermissionsExt {
+ /// Returns the underlying raw `mode_t` bits that are the standard Redox
+ /// permissions for this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::File;
+ /// use std::os::redox::fs::PermissionsExt;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let f = File::create("foo.txt")?;
+ /// let metadata = f.metadata()?;
+ /// let permissions = metadata.permissions();
+ ///
+ /// println!("permissions: {}", permissions.mode());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn mode(&self) -> u32;
+
+ /// Sets the underlying raw bits for this set of permissions.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::File;
+ /// use std::os::redox::fs::PermissionsExt;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let f = File::create("foo.txt")?;
+ /// let metadata = f.metadata()?;
+ /// let mut permissions = metadata.permissions();
+ ///
+ /// permissions.set_mode(0o644); // Read/write for owner and read for others.
+ /// assert_eq!(permissions.mode(), 0o644);
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn set_mode(&mut self, mode: u32);
+
+ /// Creates a new instance of `Permissions` from the given set of Redox
+ /// permission bits.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::fs::Permissions;
+ /// use std::os::redox::fs::PermissionsExt;
+ ///
+ /// // Read/write for owner and read for others.
+ /// let permissions = Permissions::from_mode(0o644);
+ /// assert_eq!(permissions.mode(), 0o644);
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn from_mode(mode: u32) -> Self;
+}
+
+#[stable(feature = "fs_ext", since = "1.1.0")]
+impl PermissionsExt for Permissions {
+ fn mode(&self) -> u32 {
+ self.as_inner().mode()
+ }
+
+ fn set_mode(&mut self, mode: u32) {
+ *self = Permissions::from_inner(FromInner::from_inner(mode));
+ }
+
+ fn from_mode(mode: u32) -> Permissions {
+ Permissions::from_inner(FromInner::from_inner(mode))
+ }
+}
+
+/// Redox-specific extensions to [`fs::OpenOptions`].
+///
+/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html
+#[stable(feature = "fs_ext", since = "1.1.0")]
+pub trait OpenOptionsExt {
+ /// Sets the mode bits that a new file will be created with.
+ ///
+ /// If a new file is created as part of a `File::open_opts` call then this
+ /// specified `mode` will be used as the permission bits for the new file.
+ /// If no `mode` is set, the default of `0o666` will be used.
+ /// The operating system masks out bits with the systems `umask`, to produce
+ /// the final permissions.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// # #![feature(libc)]
+ /// extern crate libc;
+ /// use std::fs::OpenOptions;
+ /// use std::os::redox::fs::OpenOptionsExt;
+ ///
+ /// # fn main() {
+ /// let mut options = OpenOptions::new();
+ /// options.mode(0o644); // Give read/write for owner and read for others.
+ /// let file = options.open("foo.txt");
+ /// # }
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn mode(&mut self, mode: u32) -> &mut Self;
+
+ /// Pass custom flags to the `flags` argument of `open`.
+ ///
+ /// The bits that define the access mode are masked out with `O_ACCMODE`, to
+ /// ensure they do not interfere with the access mode set by Rusts options.
+ ///
+ /// Custom flags can only set flags, not remove flags set by Rusts options.
+ /// This options overwrites any previously set custom flags.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// # #![feature(libc)]
+ /// extern crate libc;
+ /// use std::fs::OpenOptions;
+ /// use std::os::redox::fs::OpenOptionsExt;
+ ///
+ /// # fn main() {
+ /// let mut options = OpenOptions::new();
+ /// options.write(true);
+ /// if cfg!(target_os = "redox") {
+ /// options.custom_flags(libc::O_NOFOLLOW);
+ /// }
+ /// let file = options.open("foo.txt");
+ /// # }
+ /// ```
+ #[stable(feature = "open_options_ext", since = "1.10.0")]
+ fn custom_flags(&mut self, flags: i32) -> &mut Self;
+}
+
+#[stable(feature = "fs_ext", since = "1.1.0")]
+impl OpenOptionsExt for OpenOptions {
+ fn mode(&mut self, mode: u32) -> &mut OpenOptions {
+ self.as_inner_mut().mode(mode); self
+ }
+
+ fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions {
+ self.as_inner_mut().custom_flags(flags); self
+ }
+}
+
+/// Redox-specific extensions to [`fs::Metadata`].
+///
+/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+pub trait MetadataExt {
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn dev(&self) -> u64;
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn ino(&self) -> u64;
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn mode(&self) -> u32;
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn nlink(&self) -> u64;
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn uid(&self) -> u32;
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn gid(&self) -> u32;
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn size(&self) -> u64;
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn atime(&self) -> i64;
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn atime_nsec(&self) -> i64;
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn mtime(&self) -> i64;
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn mtime_nsec(&self) -> i64;
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn ctime(&self) -> i64;
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn ctime_nsec(&self) -> i64;
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn blksize(&self) -> u64;
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn blocks(&self) -> u64;
+}
+
+// Hm, why are there casts here to the returned type, shouldn't the types always
+// be the same? Right you are! Turns out, however, on android at least the types
+// in the raw `stat` structure are not the same as the types being returned. Who
+// knew!
+//
+// As a result to make sure this compiles for all platforms we do the manual
+// casts and rely on manual lowering to `stat` if the raw type is desired.
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+impl MetadataExt for fs::Metadata {
+ fn dev(&self) -> u64 {
+ self.as_inner().as_inner().st_dev as u64
+ }
+ fn ino(&self) -> u64 {
+ self.as_inner().as_inner().st_ino as u64
+ }
+ fn mode(&self) -> u32 {
+ self.as_inner().as_inner().st_mode as u32
+ }
+ fn nlink(&self) -> u64 {
+ self.as_inner().as_inner().st_nlink as u64
+ }
+ fn uid(&self) -> u32 {
+ self.as_inner().as_inner().st_uid as u32
+ }
+ fn gid(&self) -> u32 {
+ self.as_inner().as_inner().st_gid as u32
+ }
+ fn size(&self) -> u64 {
+ self.as_inner().as_inner().st_size as u64
+ }
+ fn atime(&self) -> i64 {
+ self.as_inner().as_inner().st_atime as i64
+ }
+ fn atime_nsec(&self) -> i64 {
+ self.as_inner().as_inner().st_atime_nsec as i64
+ }
+ fn mtime(&self) -> i64 {
+ self.as_inner().as_inner().st_mtime as i64
+ }
+ fn mtime_nsec(&self) -> i64 {
+ self.as_inner().as_inner().st_mtime_nsec as i64
+ }
+ fn ctime(&self) -> i64 {
+ self.as_inner().as_inner().st_ctime as i64
+ }
+ fn ctime_nsec(&self) -> i64 {
+ self.as_inner().as_inner().st_ctime_nsec as i64
+ }
+ fn blksize(&self) -> u64 {
+ self.as_inner().as_inner().st_blksize as u64
+ }
+ fn blocks(&self) -> u64 {
+ self.as_inner().as_inner().st_blocks as u64
+ }
+}
+
+/// Redox-specific extensions for [`FileType`].
+///
+/// Adds support for special Unix file types such as block/character devices,
+/// pipes, and sockets.
+///
+/// [`FileType`]: ../../../../std/fs/struct.FileType.html
+#[stable(feature = "file_type_ext", since = "1.5.0")]
+pub trait FileTypeExt {
+ /// Returns whether this file type is a block device.
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_block_device(&self) -> bool;
+ /// Returns whether this file type is a char device.
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_char_device(&self) -> bool;
+ /// Returns whether this file type is a fifo.
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_fifo(&self) -> bool;
+ /// Returns whether this file type is a socket.
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_socket(&self) -> bool;
+}
+
+#[stable(feature = "file_type_ext", since = "1.5.0")]
+impl FileTypeExt for fs::FileType {
+ fn is_block_device(&self) -> bool { false /*FIXME: Implement block device mode*/ }
+ fn is_char_device(&self) -> bool { false /*FIXME: Implement char device mode*/ }
+ fn is_fifo(&self) -> bool { false /*FIXME: Implement fifo mode*/ }
+ fn is_socket(&self) -> bool { false /*FIXME: Implement socket mode*/ }
+}
+
+/// Creates a new symbolic link on the filesystem.
+///
+/// The `dst` path will be a symbolic link pointing to the `src` path.
+///
+/// # Note
+///
+/// On Windows, you must specify whether a symbolic link points to a file
+/// or directory. Use `os::windows::fs::symlink_file` to create a
+/// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a
+/// symbolic link to a directory. Additionally, the process must have
+/// `SeCreateSymbolicLinkPrivilege` in order to be able to create a
+/// symbolic link.
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::os::redox::fs;
+///
+/// fn main() -> std::io::Result<()> {
+/// fs::symlink("a.txt", "b.txt")?;
+/// Ok(())
+/// }
+/// ```
+#[stable(feature = "symlink", since = "1.1.0")]
+pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()>
+{
+ sys::fs::symlink(src.as_ref(), dst.as_ref())
+}
+
+/// Redox-specific extensions to [`fs::DirBuilder`].
+///
+/// [`fs::DirBuilder`]: ../../../../std/fs/struct.DirBuilder.html
+#[stable(feature = "dir_builder", since = "1.6.0")]
+pub trait DirBuilderExt {
+ /// Sets the mode to create new directories with. This option defaults to
+ /// 0o777.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::DirBuilder;
+ /// use std::os::redox::fs::DirBuilderExt;
+ ///
+ /// let mut builder = DirBuilder::new();
+ /// builder.mode(0o755);
+ /// ```
+ #[stable(feature = "dir_builder", since = "1.6.0")]
+ fn mode(&mut self, mode: u32) -> &mut Self;
+}
+
+#[stable(feature = "dir_builder", since = "1.6.0")]
+impl DirBuilderExt for fs::DirBuilder {
+ fn mode(&mut self, mode: u32) -> &mut fs::DirBuilder {
+ self.as_inner_mut().set_mode(mode);
+ self
+ }
+}
diff --git a/ctr-std/src/sys/redox/ext/io.rs b/ctr-std/src/sys/redox/ext/io.rs
new file mode 100644
index 0000000..c4d9956
--- /dev/null
+++ b/ctr-std/src/sys/redox/ext/io.rs
@@ -0,0 +1,167 @@
+// 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.
+
+//! Unix-specific extensions to general I/O primitives
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+use fs;
+use net;
+use sys;
+use io;
+use sys_common::{self, AsInner, FromInner, IntoInner};
+
+/// Raw file descriptors.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub type RawFd = usize;
+
+/// A trait to extract the raw unix file descriptor from an underlying
+/// object.
+///
+/// This is only available on unix platforms and must be imported in order
+/// to call the method. Windows platforms have a corresponding `AsRawHandle`
+/// and `AsRawSocket` set of traits.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub trait AsRawFd {
+ /// Extracts the raw file descriptor.
+ ///
+ /// This method does **not** pass ownership of the raw file descriptor
+ /// to the caller. The descriptor is only guaranteed to be valid while
+ /// the original object has not yet been destroyed.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn as_raw_fd(&self) -> RawFd;
+}
+
+/// A trait to express the ability to construct an object from a raw file
+/// descriptor.
+#[stable(feature = "from_raw_os", since = "1.1.0")]
+pub trait FromRawFd {
+ /// Constructs a new instances of `Self` from the given raw file
+ /// descriptor.
+ ///
+ /// This function **consumes ownership** of the specified file
+ /// descriptor. The returned object will take responsibility for closing
+ /// it when the 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_fd(fd: RawFd) -> Self;
+}
+
+/// A trait to express the ability to consume an object and acquire ownership of
+/// its raw file descriptor.
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+pub trait IntoRawFd {
+ /// Consumes this object, returning the raw underlying file descriptor.
+ ///
+ /// This function **transfers ownership** of the underlying file descriptor
+ /// to the caller. Callers are then the unique owners of the file descriptor
+ /// and must close the descriptor once it's no longer needed.
+ #[stable(feature = "into_raw_os", since = "1.4.0")]
+ fn into_raw_fd(self) -> RawFd;
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl AsRawFd for fs::File {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_inner().fd().raw()
+ }
+}
+#[stable(feature = "from_raw_os", since = "1.1.0")]
+impl FromRawFd for fs::File {
+ unsafe fn from_raw_fd(fd: RawFd) -> fs::File {
+ fs::File::from_inner(sys::fs::File::from_inner(fd))
+ }
+}
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for fs::File {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_fd().into_raw()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl AsRawFd for net::TcpStream {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_inner().as_inner().fd().raw()
+ }
+}
+#[stable(feature = "rust1", since = "1.0.0")]
+impl AsRawFd for net::TcpListener {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_inner().as_inner().fd().raw()
+ }
+}
+#[stable(feature = "rust1", since = "1.0.0")]
+impl AsRawFd for net::UdpSocket {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_inner().as_inner().fd().raw()
+ }
+}
+
+#[stable(feature = "asraw_stdio", since = "1.21.0")]
+impl AsRawFd for io::Stdin {
+ fn as_raw_fd(&self) -> RawFd { 0 }
+}
+
+#[stable(feature = "asraw_stdio", since = "1.21.0")]
+impl AsRawFd for io::Stdout {
+ fn as_raw_fd(&self) -> RawFd { 1 }
+}
+
+#[stable(feature = "asraw_stdio", since = "1.21.0")]
+impl AsRawFd for io::Stderr {
+ fn as_raw_fd(&self) -> RawFd { 2 }
+}
+
+#[stable(feature = "from_raw_os", since = "1.1.0")]
+impl FromRawFd for net::TcpStream {
+ unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream {
+ let file = sys::fs::File::from_inner(fd);
+ net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(file))
+ }
+}
+#[stable(feature = "from_raw_os", since = "1.1.0")]
+impl FromRawFd for net::TcpListener {
+ unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener {
+ let file = sys::fs::File::from_inner(fd);
+ net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(file))
+ }
+}
+#[stable(feature = "from_raw_os", since = "1.1.0")]
+impl FromRawFd for net::UdpSocket {
+ unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket {
+ let file = sys::fs::File::from_inner(fd);
+ net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(file))
+ }
+}
+
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for net::TcpStream {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_inner().into_fd().into_raw()
+ }
+}
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for net::TcpListener {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_inner().into_fd().into_raw()
+ }
+}
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for net::UdpSocket {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_inner().into_fd().into_raw()
+ }
+}
diff --git a/ctr-std/src/sys/redox/ext/mod.rs b/ctr-std/src/sys/redox/ext/mod.rs
new file mode 100644
index 0000000..9fd8d6c
--- /dev/null
+++ b/ctr-std/src/sys/redox/ext/mod.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.
+
+//! Experimental extensions to `std` for Unix platforms.
+//!
+//! For now, this module is limited to extracting file descriptors,
+//! but its functionality will grow over time.
+//!
+//! # Examples
+//!
+//! ```no_run
+//! use std::fs::File;
+//! use std::os::unix::prelude::*;
+//!
+//! fn main() {
+//! let f = File::create("foo.txt").unwrap();
+//! let fd = f.as_raw_fd();
+//!
+//! // use fd with native unix bindings
+//! }
+//! ```
+
+#![stable(feature = "rust1", since = "1.0.0")]
+#![doc(cfg(target_os = "redox"))]
+
+pub mod ffi;
+pub mod fs;
+pub mod io;
+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::{RawFd, AsRawFd, FromRawFd, IntoRawFd};
+ #[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::{FileTypeExt, PermissionsExt, OpenOptionsExt, MetadataExt};
+ #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")]
+ pub use super::thread::JoinHandleExt;
+ #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")]
+ pub use super::process::{CommandExt, ExitStatusExt};
+}
diff --git a/ctr-std/src/sys/redox/ext/process.rs b/ctr-std/src/sys/redox/ext/process.rs
new file mode 100644
index 0000000..cfb6d5f
--- /dev/null
+++ b/ctr-std/src/sys/redox/ext/process.rs
@@ -0,0 +1,187 @@
+// 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.
+
+//! Redox-specific extensions to primitives in the `std::process` module.
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+use io;
+use os::unix::io::{FromRawFd, RawFd, AsRawFd, IntoRawFd};
+use process;
+use sys;
+use sys_common::{AsInnerMut, AsInner, FromInner, IntoInner};
+
+/// Redox-specific extensions to the [`process::Command`] builder,
+///
+/// [`process::Command`]: ../../../../std/process/struct.Command.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub trait CommandExt {
+ /// Sets the child process's user id. This translates to a
+ /// `setuid` call in the child process. Failure in the `setuid`
+ /// call will cause the spawn to fail.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn uid(&mut self, id: u32) -> &mut process::Command;
+
+ /// Similar to `uid`, but sets the group id of the child process. This has
+ /// the same semantics as the `uid` field.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn gid(&mut self, id: u32) -> &mut process::Command;
+
+ /// Schedules a closure to be run just before the `exec` function is
+ /// invoked.
+ ///
+ /// The closure is allowed to return an I/O error whose OS error code will
+ /// be communicated back to the parent and returned as an error from when
+ /// the spawn was requested.
+ ///
+ /// Multiple closures can be registered and they will be called in order of
+ /// their registration. If a closure returns `Err` then no further closures
+ /// will be called and the spawn operation will immediately return with a
+ /// failure.
+ ///
+ /// # Notes
+ ///
+ /// This closure will be run in the context of the child process after a
+ /// `fork`. This primarily means that any modifications made to memory on
+ /// behalf of this closure will **not** be visible to the parent process.
+ /// This is often a very constrained environment where normal operations
+ /// like `malloc` or acquiring a mutex are not guaranteed to work (due to
+ /// other threads perhaps still running when the `fork` was run).
+ ///
+ /// When this closure is run, aspects such as the stdio file descriptors and
+ /// working directory have successfully been changed, so output to these
+ /// locations may not appear where intended.
+ #[stable(feature = "process_exec", since = "1.15.0")]
+ fn before_exec<F>(&mut self, f: F) -> &mut process::Command
+ where F: FnMut() -> io::Result<()> + Send + Sync + 'static;
+
+ /// Performs all the required setup by this `Command`, followed by calling
+ /// the `execvp` syscall.
+ ///
+ /// On success this function will not return, and otherwise it will return
+ /// an error indicating why the exec (or another part of the setup of the
+ /// `Command`) failed.
+ ///
+ /// This function, unlike `spawn`, will **not** `fork` the process to create
+ /// a new child. Like spawn, however, the default behavior for the stdio
+ /// descriptors will be to inherited from the current process.
+ ///
+ /// # Notes
+ ///
+ /// The process may be in a "broken state" if this function returns in
+ /// error. For example the working directory, environment variables, signal
+ /// handling settings, various user/group information, or aspects of stdio
+ /// file descriptors may have changed. If a "transactional spawn" is
+ /// required to gracefully handle errors it is recommended to use the
+ /// cross-platform `spawn` instead.
+ #[stable(feature = "process_exec2", since = "1.9.0")]
+ fn exec(&mut self) -> io::Error;
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl CommandExt for process::Command {
+ fn uid(&mut self, id: u32) -> &mut process::Command {
+ self.as_inner_mut().uid(id);
+ self
+ }
+
+ fn gid(&mut self, id: u32) -> &mut process::Command {
+ self.as_inner_mut().gid(id);
+ self
+ }
+
+ fn before_exec<F>(&mut self, f: F) -> &mut process::Command
+ where F: FnMut() -> io::Result<()> + Send + Sync + 'static
+ {
+ self.as_inner_mut().before_exec(Box::new(f));
+ self
+ }
+
+ fn exec(&mut self) -> io::Error {
+ self.as_inner_mut().exec(sys::process::Stdio::Inherit)
+ }
+}
+
+/// Redox-specific extensions to [`process::ExitStatus`].
+///
+/// [`process::ExitStatus`]: ../../../../std/process/struct.ExitStatus.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub trait ExitStatusExt {
+ /// Creates a new `ExitStatus` from the raw underlying `i32` return value of
+ /// a process.
+ #[stable(feature = "exit_status_from", since = "1.12.0")]
+ fn from_raw(raw: i32) -> Self;
+
+ /// If the process was terminated by a signal, returns that signal.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn signal(&self) -> Option<i32>;
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl ExitStatusExt for process::ExitStatus {
+ fn from_raw(raw: i32) -> Self {
+ process::ExitStatus::from_inner(From::from(raw))
+ }
+
+ fn signal(&self) -> Option<i32> {
+ self.as_inner().signal()
+ }
+}
+
+#[stable(feature = "process_extensions", since = "1.2.0")]
+impl FromRawFd for process::Stdio {
+ unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
+ let fd = sys::fd::FileDesc::new(fd);
+ let io = sys::process::Stdio::Fd(fd);
+ process::Stdio::from_inner(io)
+ }
+}
+
+#[stable(feature = "process_extensions", since = "1.2.0")]
+impl AsRawFd for process::ChildStdin {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_inner().fd().raw()
+ }
+}
+
+#[stable(feature = "process_extensions", since = "1.2.0")]
+impl AsRawFd for process::ChildStdout {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_inner().fd().raw()
+ }
+}
+
+#[stable(feature = "process_extensions", since = "1.2.0")]
+impl AsRawFd for process::ChildStderr {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_inner().fd().raw()
+ }
+}
+
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for process::ChildStdin {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_fd().into_raw()
+ }
+}
+
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for process::ChildStdout {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_fd().into_raw()
+ }
+}
+
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for process::ChildStderr {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_fd().into_raw()
+ }
+}
diff --git a/ctr-std/src/sys/redox/ext/thread.rs b/ctr-std/src/sys/redox/ext/thread.rs
new file mode 100644
index 0000000..71ff0d4
--- /dev/null
+++ b/ctr-std/src/sys/redox/ext/thread.rs
@@ -0,0 +1,49 @@
+// 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.
+
+//! Redox-specific extensions to primitives in the `std::thread` module.
+
+#![stable(feature = "thread_extensions", since = "1.9.0")]
+
+use sys_common::{AsInner, IntoInner};
+use thread::JoinHandle;
+
+#[stable(feature = "thread_extensions", since = "1.9.0")]
+#[allow(deprecated)]
+pub type RawPthread = usize;
+
+/// Redox-specific extensions to [`thread::JoinHandle`].
+///
+/// [`thread::JoinHandle`]: ../../../../std/thread/struct.JoinHandle.html
+#[stable(feature = "thread_extensions", since = "1.9.0")]
+pub trait JoinHandleExt {
+ /// Extracts the raw pthread_t without taking ownership
+ #[stable(feature = "thread_extensions", since = "1.9.0")]
+ fn as_pthread_t(&self) -> RawPthread;
+
+ /// Consumes the thread, returning the raw pthread_t
+ ///
+ /// This function **transfers ownership** of the underlying pthread_t to
+ /// the caller. Callers are then the unique owners of the pthread_t and
+ /// must either detach or join the pthread_t once it's no longer needed.
+ #[stable(feature = "thread_extensions", since = "1.9.0")]
+ fn into_pthread_t(self) -> RawPthread;
+}
+
+#[stable(feature = "thread_extensions", since = "1.9.0")]
+impl<T> JoinHandleExt for JoinHandle<T> {
+ fn as_pthread_t(&self) -> RawPthread {
+ self.as_inner().id() as RawPthread
+ }
+
+ fn into_pthread_t(self) -> RawPthread {
+ self.into_inner().into_id() as RawPthread
+ }
+}
diff --git a/ctr-std/src/sys/redox/fast_thread_local.rs b/ctr-std/src/sys/redox/fast_thread_local.rs
new file mode 100644
index 0000000..6a007e9
--- /dev/null
+++ b/ctr-std/src/sys/redox/fast_thread_local.rs
@@ -0,0 +1,121 @@
+// 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.
+
+#![cfg(target_thread_local)]
+#![unstable(feature = "thread_local_internals", issue = "0")]
+
+use cell::{Cell, UnsafeCell};
+use mem;
+use ptr;
+
+
+pub struct Key<T> {
+ inner: UnsafeCell<Option<T>>,
+
+ // Metadata to keep track of the state of the destructor. Remember that
+ // these variables are thread-local, not global.
+ dtor_registered: Cell<bool>,
+ dtor_running: Cell<bool>,
+}
+
+unsafe impl<T> ::marker::Sync for Key<T> { }
+
+impl<T> Key<T> {
+ pub const fn new() -> Key<T> {
+ Key {
+ inner: UnsafeCell::new(None),
+ dtor_registered: Cell::new(false),
+ dtor_running: Cell::new(false)
+ }
+ }
+
+ pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
+ unsafe {
+ if mem::needs_drop::<T>() && self.dtor_running.get() {
+ return None
+ }
+ self.register_dtor();
+ }
+ Some(&self.inner)
+ }
+
+ unsafe fn register_dtor(&self) {
+ if !mem::needs_drop::<T>() || self.dtor_registered.get() {
+ return
+ }
+
+ register_dtor(self as *const _ as *mut u8,
+ destroy_value::<T>);
+ self.dtor_registered.set(true);
+ }
+}
+
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
+ // The fallback implementation uses a vanilla OS-based TLS key to track
+ // the list of destructors that need to be run for this thread. The key
+ // then has its own destructor which runs all the other destructors.
+ //
+ // The destructor for DTORS is a little special in that it has a `while`
+ // loop to continuously drain the list of registered destructors. It
+ // *should* be the case that this loop always terminates because we
+ // provide the guarantee that a TLS key cannot be set after it is
+ // flagged for destruction.
+ use sys_common::thread_local as os;
+
+ static DTORS: os::StaticKey = os::StaticKey::new(Some(run_dtors));
+ type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>;
+ if DTORS.get().is_null() {
+ let v: Box<List> = box Vec::new();
+ DTORS.set(Box::into_raw(v) as *mut u8);
+ }
+ let list: &mut List = &mut *(DTORS.get() as *mut List);
+ list.push((t, dtor));
+
+ unsafe extern fn run_dtors(mut ptr: *mut u8) {
+ while !ptr.is_null() {
+ let list: Box<List> = Box::from_raw(ptr as *mut List);
+ for (ptr, dtor) in list.into_iter() {
+ dtor(ptr);
+ }
+ ptr = DTORS.get();
+ DTORS.set(ptr::null_mut());
+ }
+ }
+}
+
+pub unsafe extern fn destroy_value<T>(ptr: *mut u8) {
+ let ptr = ptr as *mut Key<T>;
+ // Right before we run the user destructor be sure to flag the
+ // destructor as running for this thread so calls to `get` will return
+ // `None`.
+ (*ptr).dtor_running.set(true);
+
+ // The macOS implementation of TLS apparently had an odd aspect to it
+ // where the pointer we have may be overwritten while this destructor
+ // is running. Specifically if a TLS destructor re-accesses TLS it may
+ // trigger a re-initialization of all TLS variables, paving over at
+ // least some destroyed ones with initial values.
+ //
+ // This means that if we drop a TLS value in place on macOS that we could
+ // revert the value to its original state halfway through the
+ // destructor, which would be bad!
+ //
+ // Hence, we use `ptr::read` on macOS (to move to a "safe" location)
+ // instead of drop_in_place.
+ if cfg!(target_os = "macos") {
+ ptr::read((*ptr).inner.get());
+ } else {
+ ptr::drop_in_place((*ptr).inner.get());
+ }
+}
+
+pub fn requires_move_before_drop() -> bool {
+ false
+}
diff --git a/ctr-std/src/sys/redox/fd.rs b/ctr-std/src/sys/redox/fd.rs
new file mode 100644
index 0000000..ba7bbdc
--- /dev/null
+++ b/ctr-std/src/sys/redox/fd.rs
@@ -0,0 +1,95 @@
+// 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.
+
+#![unstable(reason = "not public", issue = "0", feature = "fd")]
+
+use io::{self, Read};
+use mem;
+use sys::{cvt, syscall};
+use sys_common::AsInner;
+
+pub struct FileDesc {
+ fd: usize,
+}
+
+impl FileDesc {
+ pub fn new(fd: usize) -> FileDesc {
+ FileDesc { fd: fd }
+ }
+
+ pub fn raw(&self) -> usize { self.fd }
+
+ /// Extracts the actual filedescriptor without closing it.
+ pub fn into_raw(self) -> usize {
+ let fd = self.fd;
+ mem::forget(self);
+ fd
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ cvt(syscall::read(self.fd, buf))
+ }
+
+ 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> {
+ cvt(syscall::write(self.fd, buf))
+ }
+
+ pub fn duplicate(&self) -> io::Result<FileDesc> {
+ let new_fd = cvt(syscall::dup(self.fd, &[]))?;
+ Ok(FileDesc::new(new_fd))
+ }
+
+ pub fn nonblocking(&self) -> io::Result<bool> {
+ let flags = cvt(syscall::fcntl(self.fd, syscall::F_GETFL, 0))?;
+ Ok(flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK)
+ }
+
+ pub fn set_cloexec(&self) -> io::Result<()> {
+ let mut flags = cvt(syscall::fcntl(self.fd, syscall::F_GETFD, 0))?;
+ flags |= syscall::O_CLOEXEC;
+ cvt(syscall::fcntl(self.fd, syscall::F_SETFD, flags)).and(Ok(()))
+ }
+
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ let mut flags = cvt(syscall::fcntl(self.fd, syscall::F_GETFL, 0))?;
+ if nonblocking {
+ flags |= syscall::O_NONBLOCK;
+ } else {
+ flags &= !syscall::O_NONBLOCK;
+ }
+ cvt(syscall::fcntl(self.fd, syscall::F_SETFL, flags)).and(Ok(()))
+ }
+}
+
+impl<'a> Read for &'a FileDesc {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ (**self).read(buf)
+ }
+}
+
+impl AsInner<usize> for FileDesc {
+ fn as_inner(&self) -> &usize { &self.fd }
+}
+
+impl Drop for FileDesc {
+ fn drop(&mut self) {
+ // Note that errors are ignored when closing a file descriptor. The
+ // reason for this is that if an error occurs we don't actually know if
+ // the file descriptor was closed or not, and if we retried (for
+ // something like EINTR), we might close another valid file descriptor
+ // (opened after we closed ours.
+ let _ = syscall::close(self.fd);
+ }
+}
diff --git a/ctr-std/src/sys/redox/fs.rs b/ctr-std/src/sys/redox/fs.rs
new file mode 100644
index 0000000..2e22161
--- /dev/null
+++ b/ctr-std/src/sys/redox/fs.rs
@@ -0,0 +1,483 @@
+// 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::unix::prelude::*;
+
+use ffi::{OsString, OsStr};
+use fmt;
+use io::{self, Error, ErrorKind, SeekFrom};
+use path::{Path, PathBuf};
+use sync::Arc;
+use sys::fd::FileDesc;
+use sys::time::SystemTime;
+use sys::{cvt, syscall};
+use sys_common::{AsInner, FromInner};
+
+pub struct File(FileDesc);
+
+#[derive(Clone)]
+pub struct FileAttr {
+ stat: syscall::Stat,
+}
+
+pub struct ReadDir {
+ data: Vec<u8>,
+ i: usize,
+ root: Arc<PathBuf>,
+}
+
+struct Dir(FileDesc);
+
+unsafe impl Send for Dir {}
+unsafe impl Sync for Dir {}
+
+pub struct DirEntry {
+ root: Arc<PathBuf>,
+ name: Box<[u8]>
+}
+
+#[derive(Clone, Debug)]
+pub struct OpenOptions {
+ // generic
+ read: bool,
+ write: bool,
+ append: bool,
+ truncate: bool,
+ create: bool,
+ create_new: bool,
+ // system-specific
+ custom_flags: i32,
+ mode: u16,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct FilePermissions { mode: u16 }
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub struct FileType { mode: u16 }
+
+#[derive(Debug)]
+pub struct DirBuilder { mode: u16 }
+
+impl FileAttr {
+ pub fn size(&self) -> u64 { self.stat.st_size as u64 }
+ pub fn perm(&self) -> FilePermissions {
+ FilePermissions { mode: (self.stat.st_mode as u16) & 0o777 }
+ }
+
+ pub fn file_type(&self) -> FileType {
+ FileType { mode: self.stat.st_mode as u16 }
+ }
+}
+
+impl FileAttr {
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(syscall::TimeSpec {
+ tv_sec: self.stat.st_mtime as i64,
+ tv_nsec: self.stat.st_mtime_nsec as i32,
+ }))
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(syscall::TimeSpec {
+ tv_sec: self.stat.st_atime as i64,
+ tv_nsec: self.stat.st_atime_nsec as i32,
+ }))
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(syscall::TimeSpec {
+ tv_sec: self.stat.st_ctime as i64,
+ tv_nsec: self.stat.st_ctime_nsec as i32,
+ }))
+ }
+}
+
+impl AsInner<syscall::Stat> for FileAttr {
+ fn as_inner(&self) -> &syscall::Stat { &self.stat }
+}
+
+impl FilePermissions {
+ pub fn readonly(&self) -> bool { self.mode & 0o222 == 0 }
+ pub fn set_readonly(&mut self, readonly: bool) {
+ if readonly {
+ self.mode &= !0o222;
+ } else {
+ self.mode |= 0o222;
+ }
+ }
+ pub fn mode(&self) -> u32 { self.mode as u32 }
+}
+
+impl FileType {
+ pub fn is_dir(&self) -> bool { self.is(syscall::MODE_DIR) }
+ pub fn is_file(&self) -> bool { self.is(syscall::MODE_FILE) }
+ pub fn is_symlink(&self) -> bool { self.is(syscall::MODE_SYMLINK) }
+
+ pub fn is(&self, mode: u16) -> bool {
+ self.mode & syscall::MODE_TYPE == mode
+ }
+}
+
+impl FromInner<u32> for FilePermissions {
+ fn from_inner(mode: u32) -> FilePermissions {
+ FilePermissions { mode: mode as u16 }
+ }
+}
+
+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("/home")'
+ fmt::Debug::fmt(&*self.root, f)
+ }
+}
+
+impl Iterator for ReadDir {
+ type Item = io::Result<DirEntry>;
+
+ fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ loop {
+ let start = self.i;
+ let mut i = self.i;
+ while i < self.data.len() {
+ self.i += 1;
+ if self.data[i] == b'\n' {
+ break;
+ }
+ i += 1;
+ }
+ if start < self.i {
+ let ret = DirEntry {
+ name: self.data[start .. i].to_owned().into_boxed_slice(),
+ root: self.root.clone()
+ };
+ if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
+ return Some(Ok(ret))
+ }
+ } else {
+ return None;
+ }
+ }
+ }
+}
+
+impl DirEntry {
+ pub fn path(&self) -> PathBuf {
+ self.root.join(OsStr::from_bytes(self.name_bytes()))
+ }
+
+ pub fn file_name(&self) -> OsString {
+ OsStr::from_bytes(self.name_bytes()).to_os_string()
+ }
+
+ pub fn metadata(&self) -> io::Result<FileAttr> {
+ lstat(&self.path())
+ }
+
+ pub fn file_type(&self) -> io::Result<FileType> {
+ lstat(&self.path()).map(|m| m.file_type())
+ }
+
+ fn name_bytes(&self) -> &[u8] {
+ &*self.name
+ }
+}
+
+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,
+ mode: 0o666,
+ }
+ }
+
+ pub fn read(&mut self, read: bool) { self.read = read; }
+ pub fn write(&mut self, write: bool) { self.write = write; }
+ pub fn append(&mut self, append: bool) { self.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: i32) { self.custom_flags = flags; }
+ pub fn mode(&mut self, mode: u32) { self.mode = mode as u16; }
+
+ fn get_access_mode(&self) -> io::Result<usize> {
+ match (self.read, self.write, self.append) {
+ (true, false, false) => Ok(syscall::O_RDONLY),
+ (false, true, false) => Ok(syscall::O_WRONLY),
+ (true, true, false) => Ok(syscall::O_RDWR),
+ (false, _, true) => Ok(syscall::O_WRONLY | syscall::O_APPEND),
+ (true, _, true) => Ok(syscall::O_RDWR | syscall::O_APPEND),
+ (false, false, false) => Err(Error::from_raw_os_error(syscall::EINVAL)),
+ }
+ }
+
+ fn get_creation_mode(&self) -> io::Result<usize> {
+ match (self.write, self.append) {
+ (true, false) => {}
+ (false, false) =>
+ if self.truncate || self.create || self.create_new {
+ return Err(Error::from_raw_os_error(syscall::EINVAL));
+ },
+ (_, true) =>
+ if self.truncate && !self.create_new {
+ return Err(Error::from_raw_os_error(syscall::EINVAL));
+ },
+ }
+
+ Ok(match (self.create, self.truncate, self.create_new) {
+ (false, false, false) => 0,
+ (true, false, false) => syscall::O_CREAT,
+ (false, true, false) => syscall::O_TRUNC,
+ (true, true, false) => syscall::O_CREAT | syscall::O_TRUNC,
+ (_, _, true) => syscall::O_CREAT | syscall::O_EXCL,
+ })
+ }
+}
+
+impl File {
+ pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+ let flags = syscall::O_CLOEXEC |
+ opts.get_access_mode()? as usize |
+ opts.get_creation_mode()? as usize |
+ (opts.custom_flags as usize & !syscall::O_ACCMODE);
+ let fd = cvt(syscall::open(path.to_str().unwrap(), flags | opts.mode as usize))?;
+ Ok(File(FileDesc::new(fd)))
+ }
+
+ pub fn file_attr(&self) -> io::Result<FileAttr> {
+ let mut stat = syscall::Stat::default();
+ cvt(syscall::fstat(self.0.raw(), &mut stat))?;
+ Ok(FileAttr { stat: stat })
+ }
+
+ pub fn fsync(&self) -> io::Result<()> {
+ cvt(syscall::fsync(self.0.raw()))?;
+ Ok(())
+ }
+
+ pub fn datasync(&self) -> io::Result<()> {
+ self.fsync()
+ }
+
+ pub fn truncate(&self, size: u64) -> io::Result<()> {
+ cvt(syscall::ftruncate(self.0.raw(), size as usize))?;
+ Ok(())
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ 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, too large values will end up as
+ // negative which will cause an error in `lseek64`.
+ SeekFrom::Start(off) => (syscall::SEEK_SET, off as i64),
+ SeekFrom::End(off) => (syscall::SEEK_END, off),
+ SeekFrom::Current(off) => (syscall::SEEK_CUR, off),
+ };
+ let n = cvt(syscall::lseek(self.0.raw(), pos as isize, whence))?;
+ Ok(n as u64)
+ }
+
+ pub fn duplicate(&self) -> io::Result<File> {
+ self.0.duplicate().map(File)
+ }
+
+ pub fn dup(&self, buf: &[u8]) -> io::Result<File> {
+ let fd = cvt(syscall::dup(*self.fd().as_inner() as usize, buf))?;
+ Ok(File(FileDesc::new(fd)))
+ }
+
+ pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
+ set_perm(&self.path()?, perm)
+ }
+
+ pub fn path(&self) -> io::Result<PathBuf> {
+ let mut buf: [u8; 4096] = [0; 4096];
+ let count = cvt(syscall::fpath(*self.fd().as_inner() as usize, &mut buf))?;
+ Ok(PathBuf::from(unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) }))
+ }
+
+ pub fn fd(&self) -> &FileDesc { &self.0 }
+
+ pub fn into_fd(self) -> FileDesc { self.0 }
+}
+
+impl DirBuilder {
+ pub fn new() -> DirBuilder {
+ DirBuilder { mode: 0o777 }
+ }
+
+ pub fn mkdir(&self, p: &Path) -> io::Result<()> {
+ let flags = syscall::O_CREAT | syscall::O_CLOEXEC | syscall::O_DIRECTORY | syscall::O_EXCL;
+ let fd = cvt(syscall::open(p.to_str().unwrap(), flags | (self.mode as usize & 0o777)))?;
+ let _ = syscall::close(fd);
+ Ok(())
+ }
+
+ pub fn set_mode(&mut self, mode: u32) {
+ self.mode = mode as u16;
+ }
+}
+
+impl FromInner<usize> for File {
+ fn from_inner(fd: usize) -> File {
+ File(FileDesc::new(fd))
+ }
+}
+
+impl fmt::Debug for File {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut b = f.debug_struct("File");
+ b.field("fd", &self.0.raw());
+ if let Ok(path) = self.path() {
+ b.field("path", &path);
+ }
+ /*
+ if let Some((read, write)) = get_mode(fd) {
+ b.field("read", &read).field("write", &write);
+ }
+ */
+ b.finish()
+ }
+}
+
+pub fn readdir(p: &Path) -> io::Result<ReadDir> {
+ let root = Arc::new(p.to_path_buf());
+
+ let flags = syscall::O_CLOEXEC | syscall::O_RDONLY | syscall::O_DIRECTORY;
+ let fd = cvt(syscall::open(p.to_str().unwrap(), flags))?;
+ let file = FileDesc::new(fd);
+ let mut data = Vec::new();
+ file.read_to_end(&mut data)?;
+
+ Ok(ReadDir { data: data, i: 0, root: root })
+}
+
+pub fn unlink(p: &Path) -> io::Result<()> {
+ cvt(syscall::unlink(p.to_str().unwrap()))?;
+ Ok(())
+}
+
+pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
+ let fd = cvt(syscall::open(old.to_str().unwrap(),
+ syscall::O_CLOEXEC | syscall::O_STAT | syscall::O_NOFOLLOW))?;
+ let res = cvt(syscall::frename(fd, new.to_str().unwrap()));
+ cvt(syscall::close(fd))?;
+ res?;
+ Ok(())
+}
+
+pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
+ cvt(syscall::chmod(p.to_str().unwrap(), perm.mode as usize))?;
+ Ok(())
+}
+
+pub fn rmdir(p: &Path) -> io::Result<()> {
+ cvt(syscall::rmdir(p.to_str().unwrap()))?;
+ Ok(())
+}
+
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+ let filetype = lstat(path)?.file_type();
+ if filetype.is_symlink() {
+ unlink(path)
+ } else {
+ remove_dir_all_recursive(path)
+ }
+}
+
+fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
+ for child in readdir(path)? {
+ let child = child?;
+ if child.file_type()?.is_dir() {
+ remove_dir_all_recursive(&child.path())?;
+ } else {
+ unlink(&child.path())?;
+ }
+ }
+ rmdir(path)
+}
+
+pub fn readlink(p: &Path) -> io::Result<PathBuf> {
+ let fd = cvt(syscall::open(p.to_str().unwrap(),
+ syscall::O_CLOEXEC | syscall::O_SYMLINK | syscall::O_RDONLY))?;
+ let mut buf: [u8; 4096] = [0; 4096];
+ let res = cvt(syscall::read(fd, &mut buf));
+ cvt(syscall::close(fd))?;
+ let count = res?;
+ Ok(PathBuf::from(unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) }))
+}
+
+pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
+ let fd = cvt(syscall::open(dst.to_str().unwrap(),
+ syscall::O_CLOEXEC | syscall::O_SYMLINK |
+ syscall::O_CREAT | syscall::O_WRONLY | 0o777))?;
+ let res = cvt(syscall::write(fd, src.to_str().unwrap().as_bytes()));
+ cvt(syscall::close(fd))?;
+ res?;
+ Ok(())
+}
+
+pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
+ Err(Error::from_raw_os_error(syscall::ENOSYS))
+}
+
+pub fn stat(p: &Path) -> io::Result<FileAttr> {
+ let fd = cvt(syscall::open(p.to_str().unwrap(), syscall::O_CLOEXEC | syscall::O_STAT))?;
+ let file = File(FileDesc::new(fd));
+ file.file_attr()
+}
+
+pub fn lstat(p: &Path) -> io::Result<FileAttr> {
+ let fd = cvt(syscall::open(p.to_str().unwrap(),
+ syscall::O_CLOEXEC | syscall::O_STAT | syscall::O_NOFOLLOW))?;
+ let file = File(FileDesc::new(fd));
+ file.file_attr()
+}
+
+pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
+ let fd = cvt(syscall::open(p.to_str().unwrap(), syscall::O_CLOEXEC | syscall::O_STAT))?;
+ let file = File(FileDesc::new(fd));
+ file.path()
+}
+
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+ use fs::{File, set_permissions};
+ if !from.is_file() {
+ return Err(Error::new(ErrorKind::InvalidInput,
+ "the source path is not an existing regular file"))
+ }
+
+ let mut reader = File::open(from)?;
+ let mut writer = File::create(to)?;
+ let perm = reader.metadata()?.permissions();
+
+ let ret = io::copy(&mut reader, &mut writer)?;
+ set_permissions(to, perm)?;
+ Ok(ret)
+}
diff --git a/ctr-std/src/sys/redox/memchr.rs b/ctr-std/src/sys/redox/memchr.rs
new file mode 100644
index 0000000..873b335
--- /dev/null
+++ b/ctr-std/src/sys/redox/memchr.rs
@@ -0,0 +1,14 @@
+// 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
+
+pub use core::slice::memchr::{memchr, memrchr};
diff --git a/ctr-std/src/sys/redox/mod.rs b/ctr-std/src/sys/redox/mod.rs
new file mode 100644
index 0000000..4352b72
--- /dev/null
+++ b/ctr-std/src/sys/redox/mod.rs
@@ -0,0 +1,81 @@
+// 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.
+
+#![allow(dead_code, missing_docs, bad_style)]
+
+use io::{self, ErrorKind};
+
+pub use libc::strlen;
+pub use self::rand::hashmap_random_keys;
+
+pub mod args;
+#[cfg(feature = "backtrace")]
+pub mod backtrace;
+pub mod cmath;
+pub mod condvar;
+pub mod env;
+pub mod ext;
+pub mod fast_thread_local;
+pub mod fd;
+pub mod fs;
+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 stdio;
+pub mod syscall;
+pub mod thread;
+pub mod thread_local;
+pub mod time;
+
+#[cfg(not(test))]
+pub fn init() {}
+
+pub fn decode_error_kind(errno: i32) -> ErrorKind {
+ match errno {
+ syscall::ECONNREFUSED => ErrorKind::ConnectionRefused,
+ syscall::ECONNRESET => ErrorKind::ConnectionReset,
+ syscall::EPERM | syscall::EACCES => ErrorKind::PermissionDenied,
+ syscall::EPIPE => ErrorKind::BrokenPipe,
+ syscall::ENOTCONN => ErrorKind::NotConnected,
+ syscall::ECONNABORTED => ErrorKind::ConnectionAborted,
+ syscall::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
+ syscall::EADDRINUSE => ErrorKind::AddrInUse,
+ syscall::ENOENT => ErrorKind::NotFound,
+ syscall::EINTR => ErrorKind::Interrupted,
+ syscall::EINVAL => ErrorKind::InvalidInput,
+ syscall::ETIMEDOUT => ErrorKind::TimedOut,
+ syscall::EEXIST => ErrorKind::AlreadyExists,
+
+ // These two constants can have the same value on some systems,
+ // but different values on others, so we can't use a match
+ // clause
+ x if x == syscall::EAGAIN || x == syscall::EWOULDBLOCK =>
+ ErrorKind::WouldBlock,
+
+ _ => ErrorKind::Other,
+ }
+}
+
+pub fn cvt(result: Result<usize, syscall::Error>) -> io::Result<usize> {
+ result.map_err(|err| io::Error::from_raw_os_error(err.errno))
+}
+
+/// On Redox, use an illegal instruction to abort
+pub unsafe fn abort_internal() -> ! {
+ ::core::intrinsics::abort();
+}
diff --git a/ctr-std/src/sys/redox/mutex.rs b/ctr-std/src/sys/redox/mutex.rs
new file mode 100644
index 0000000..a995f59
--- /dev/null
+++ b/ctr-std/src/sys/redox/mutex.rs
@@ -0,0 +1,179 @@
+// 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 cell::UnsafeCell;
+use intrinsics::{atomic_cxchg, atomic_xchg};
+use ptr;
+
+use sys::syscall::{futex, getpid, FUTEX_WAIT, FUTEX_WAKE};
+
+pub unsafe fn mutex_try_lock(m: *mut i32) -> bool {
+ atomic_cxchg(m, 0, 1).0 == 0
+}
+
+pub unsafe fn mutex_lock(m: *mut i32) {
+ let mut c = 0;
+ //Set to larger value for longer spin test
+ for _i in 0..100 {
+ c = atomic_cxchg(m, 0, 1).0;
+ if c == 0 {
+ break;
+ }
+ //cpu_relax()
+ }
+ if c == 1 {
+ c = atomic_xchg(m, 2);
+ }
+ while c != 0 {
+ let _ = futex(m, FUTEX_WAIT, 2, 0, ptr::null_mut());
+ c = atomic_xchg(m, 2);
+ }
+}
+
+pub unsafe fn mutex_unlock(m: *mut i32) {
+ if *m == 2 {
+ *m = 0;
+ } else if atomic_xchg(m, 0) == 1 {
+ return;
+ }
+ //Set to larger value for longer spin test
+ for _i in 0..100 {
+ if *m != 0 {
+ if atomic_cxchg(m, 1, 2).0 != 0 {
+ return;
+ }
+ }
+ //cpu_relax()
+ }
+ let _ = futex(m, FUTEX_WAKE, 1, 0, ptr::null_mut());
+}
+
+pub struct Mutex {
+ pub lock: UnsafeCell<i32>,
+}
+
+impl Mutex {
+ /// Create a new mutex.
+ pub const fn new() -> Self {
+ Mutex {
+ lock: UnsafeCell::new(0),
+ }
+ }
+
+ #[inline]
+ pub unsafe fn init(&self) {
+ *self.lock.get() = 0;
+ }
+
+ /// Try to lock the mutex
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ mutex_try_lock(self.lock.get())
+ }
+
+ /// Lock the mutex
+ #[inline]
+ pub unsafe fn lock(&self) {
+ mutex_lock(self.lock.get());
+ }
+
+ /// Unlock the mutex
+ #[inline]
+ pub unsafe fn unlock(&self) {
+ mutex_unlock(self.lock.get());
+ }
+
+ #[inline]
+ pub unsafe fn destroy(&self) {
+ *self.lock.get() = 0;
+ }
+}
+
+unsafe impl Send for Mutex {}
+
+unsafe impl Sync for Mutex {}
+
+pub struct ReentrantMutex {
+ pub lock: UnsafeCell<i32>,
+ pub owner: UnsafeCell<usize>,
+ pub own_count: UnsafeCell<usize>,
+}
+
+impl ReentrantMutex {
+ pub const fn uninitialized() -> Self {
+ ReentrantMutex {
+ lock: UnsafeCell::new(0),
+ owner: UnsafeCell::new(0),
+ own_count: UnsafeCell::new(0),
+ }
+ }
+
+ #[inline]
+ pub unsafe fn init(&mut self) {
+ *self.lock.get() = 0;
+ *self.owner.get() = 0;
+ *self.own_count.get() = 0;
+ }
+
+ /// Try to lock the mutex
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ let pid = getpid().unwrap();
+ if *self.own_count.get() > 0 && *self.owner.get() == pid {
+ *self.own_count.get() += 1;
+ true
+ } else {
+ if mutex_try_lock(self.lock.get()) {
+ *self.owner.get() = pid;
+ *self.own_count.get() = 1;
+ true
+ } else {
+ false
+ }
+ }
+ }
+
+ /// Lock the mutex
+ #[inline]
+ pub unsafe fn lock(&self) {
+ let pid = getpid().unwrap();
+ if *self.own_count.get() > 0 && *self.owner.get() == pid {
+ *self.own_count.get() += 1;
+ } else {
+ mutex_lock(self.lock.get());
+ *self.owner.get() = pid;
+ *self.own_count.get() = 1;
+ }
+ }
+
+ /// Unlock the mutex
+ #[inline]
+ pub unsafe fn unlock(&self) {
+ let pid = getpid().unwrap();
+ if *self.own_count.get() > 0 && *self.owner.get() == pid {
+ *self.own_count.get() -= 1;
+ if *self.own_count.get() == 0 {
+ *self.owner.get() = 0;
+ mutex_unlock(self.lock.get());
+ }
+ }
+ }
+
+ #[inline]
+ pub unsafe fn destroy(&self) {
+ *self.lock.get() = 0;
+ *self.owner.get() = 0;
+ *self.own_count.get() = 0;
+ }
+}
+
+unsafe impl Send for ReentrantMutex {}
+
+unsafe impl Sync for ReentrantMutex {}
diff --git a/ctr-std/src/sys/redox/net/dns/answer.rs b/ctr-std/src/sys/redox/net/dns/answer.rs
new file mode 100644
index 0000000..8e6aaeb
--- /dev/null
+++ b/ctr-std/src/sys/redox/net/dns/answer.rs
@@ -0,0 +1,22 @@
+// 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 string::String;
+use vec::Vec;
+
+#[derive(Clone, Debug)]
+pub struct DnsAnswer {
+ pub name: String,
+ pub a_type: u16,
+ pub a_class: u16,
+ pub ttl_a: u16,
+ pub ttl_b: u16,
+ pub data: Vec<u8>
+}
diff --git a/ctr-std/src/sys/redox/net/dns/mod.rs b/ctr-std/src/sys/redox/net/dns/mod.rs
new file mode 100644
index 0000000..1a26257
--- /dev/null
+++ b/ctr-std/src/sys/redox/net/dns/mod.rs
@@ -0,0 +1,215 @@
+// 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.
+
+pub use self::answer::DnsAnswer;
+pub use self::query::DnsQuery;
+
+use slice;
+use u16;
+use string::String;
+use vec::Vec;
+
+mod answer;
+mod query;
+
+#[unstable(feature = "n16", issue="0")]
+#[allow(non_camel_case_types)]
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(packed)]
+pub struct n16 {
+ inner: u16
+}
+
+impl n16 {
+ #[unstable(feature = "n16", issue="0")]
+ pub fn as_bytes(&self) -> &[u8] {
+ unsafe { slice::from_raw_parts((&self.inner as *const u16) as *const u8, 2) }
+ }
+
+ #[unstable(feature = "n16", issue="0")]
+ pub fn from_bytes(bytes: &[u8]) -> Self {
+ n16 {
+ inner: unsafe { slice::from_raw_parts(bytes.as_ptr() as *const u16, bytes.len()/2)[0] }
+ }
+ }
+}
+
+#[unstable(feature = "n16", issue="0")]
+impl From<u16> for n16 {
+ fn from(value: u16) -> Self {
+ n16 {
+ inner: value.to_be()
+ }
+ }
+}
+
+#[unstable(feature = "n16", issue="0")]
+impl From<n16> for u16 {
+ fn from(value: n16) -> Self {
+ u16::from_be(value.inner)
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct Dns {
+ pub transaction_id: u16,
+ pub flags: u16,
+ pub queries: Vec<DnsQuery>,
+ pub answers: Vec<DnsAnswer>
+}
+
+impl Dns {
+ pub fn compile(&self) -> Vec<u8> {
+ let mut data = Vec::new();
+
+ macro_rules! push_u8 {
+ ($value:expr) => {
+ data.push($value);
+ };
+ };
+
+ macro_rules! push_n16 {
+ ($value:expr) => {
+ data.extend_from_slice(n16::from($value).as_bytes());
+ };
+ };
+
+ push_n16!(self.transaction_id);
+ push_n16!(self.flags);
+ push_n16!(self.queries.len() as u16);
+ push_n16!(self.answers.len() as u16);
+ push_n16!(0);
+ push_n16!(0);
+
+ for query in self.queries.iter() {
+ for part in query.name.split('.') {
+ push_u8!(part.len() as u8);
+ data.extend_from_slice(part.as_bytes());
+ }
+ push_u8!(0);
+ push_n16!(query.q_type);
+ push_n16!(query.q_class);
+ }
+
+ data
+ }
+
+ pub fn parse(data: &[u8]) -> Result<Self, String> {
+ let name_ind = 0b11000000;
+ let mut i = 0;
+
+ macro_rules! pop_u8 {
+ () => {
+ {
+ i += 1;
+ if i > data.len() {
+ return Err(format!("{}: {}: pop_u8", file!(), line!()));
+ }
+ data[i - 1]
+ }
+ };
+ };
+
+ macro_rules! pop_n16 {
+ () => {
+ {
+ i += 2;
+ if i > data.len() {
+ return Err(format!("{}: {}: pop_n16", file!(), line!()));
+ }
+ u16::from(n16::from_bytes(&data[i - 2 .. i]))
+ }
+ };
+ };
+
+ macro_rules! pop_data {
+ () => {
+ {
+ let mut data = Vec::new();
+
+ let data_len = pop_n16!();
+ for _data_i in 0..data_len {
+ data.push(pop_u8!());
+ }
+
+ data
+ }
+ };
+ };
+
+ macro_rules! pop_name {
+ () => {
+ {
+ let mut name = String::new();
+ let old_i = i;
+
+ loop {
+ let name_len = pop_u8!();
+ if name_len & name_ind == name_ind {
+ i -= 1;
+ i = (pop_n16!() - ((name_ind as u16) << 8)) as usize;
+ continue;
+ }
+ if name_len == 0 {
+ break;
+ }
+ if ! name.is_empty() {
+ name.push('.');
+ }
+ for _name_i in 0..name_len {
+ name.push(pop_u8!() as char);
+ }
+ }
+
+ if i <= old_i {
+ i = old_i + 2;
+ }
+
+ name
+ }
+ };
+ };
+
+ let transaction_id = pop_n16!();
+ let flags = pop_n16!();
+ let queries_len = pop_n16!();
+ let answers_len = pop_n16!();
+ pop_n16!();
+ pop_n16!();
+
+ let mut queries = Vec::new();
+ for _query_i in 0..queries_len {
+ queries.push(DnsQuery {
+ name: pop_name!(),
+ q_type: pop_n16!(),
+ q_class: pop_n16!()
+ });
+ }
+
+ let mut answers = Vec::new();
+ for _answer_i in 0..answers_len {
+ answers.push(DnsAnswer {
+ name: pop_name!(),
+ a_type: pop_n16!(),
+ a_class: pop_n16!(),
+ ttl_a: pop_n16!(),
+ ttl_b: pop_n16!(),
+ data: pop_data!()
+ });
+ }
+
+ Ok(Dns {
+ transaction_id,
+ flags,
+ queries,
+ answers,
+ })
+ }
+}
diff --git a/ctr-std/src/sys/redox/net/dns/query.rs b/ctr-std/src/sys/redox/net/dns/query.rs
new file mode 100644
index 0000000..b0dcdcb
--- /dev/null
+++ b/ctr-std/src/sys/redox/net/dns/query.rs
@@ -0,0 +1,18 @@
+// 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 string::String;
+
+#[derive(Clone, Debug)]
+pub struct DnsQuery {
+ pub name: String,
+ pub q_type: u16,
+ pub q_class: u16
+}
diff --git a/ctr-std/src/sys/redox/net/mod.rs b/ctr-std/src/sys/redox/net/mod.rs
new file mode 100644
index 0000000..0291d7f
--- /dev/null
+++ b/ctr-std/src/sys/redox/net/mod.rs
@@ -0,0 +1,115 @@
+// 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 fs::File;
+use io::{Error, Result, Read};
+use iter::Iterator;
+use net::{Ipv4Addr, SocketAddr, SocketAddrV4};
+use str::FromStr;
+use string::{String, ToString};
+use sys::syscall::EINVAL;
+use time::{self, Duration};
+use vec::{IntoIter, Vec};
+
+use self::dns::{Dns, DnsQuery};
+
+pub use self::tcp::{TcpStream, TcpListener};
+pub use self::udp::UdpSocket;
+
+pub mod netc;
+
+mod dns;
+mod tcp;
+mod udp;
+
+pub struct LookupHost(IntoIter<SocketAddr>);
+
+impl Iterator for LookupHost {
+ type Item = SocketAddr;
+ fn next(&mut self) -> Option<Self::Item> {
+ self.0.next()
+ }
+}
+
+pub fn lookup_host(host: &str) -> Result<LookupHost> {
+ let mut ip_string = String::new();
+ File::open("/etc/net/ip")?.read_to_string(&mut ip_string)?;
+ let ip: Vec<u8> = ip_string.trim().split(".").map(|part| part.parse::<u8>()
+ .unwrap_or(0)).collect();
+
+ let mut dns_string = String::new();
+ File::open("/etc/net/dns")?.read_to_string(&mut dns_string)?;
+ let dns: Vec<u8> = dns_string.trim().split(".").map(|part| part.parse::<u8>()
+ .unwrap_or(0)).collect();
+
+ if ip.len() == 4 && dns.len() == 4 {
+ let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap();
+ let tid = (time.subsec_nanos() >> 16) as u16;
+
+ let packet = Dns {
+ transaction_id: tid,
+ flags: 0x0100,
+ queries: vec![DnsQuery {
+ name: host.to_string(),
+ q_type: 0x0001,
+ q_class: 0x0001,
+ }],
+ answers: vec![]
+ };
+
+ let packet_data = packet.compile();
+
+ let my_ip = Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]);
+ let dns_ip = Ipv4Addr::new(dns[0], dns[1], dns[2], dns[3]);
+ let socket = UdpSocket::bind(&SocketAddr::V4(SocketAddrV4::new(my_ip, 0)))?;
+ socket.set_read_timeout(Some(Duration::new(5, 0)))?;
+ socket.set_write_timeout(Some(Duration::new(5, 0)))?;
+ socket.connect(&SocketAddr::V4(SocketAddrV4::new(dns_ip, 53)))?;
+ socket.send(&packet_data)?;
+
+ let mut buf = [0; 65536];
+ let count = socket.recv(&mut buf)?;
+
+ match Dns::parse(&buf[.. count]) {
+ Ok(response) => {
+ let mut addrs = vec![];
+ for answer in response.answers.iter() {
+ if answer.a_type == 0x0001 && answer.a_class == 0x0001
+ && answer.data.len() == 4
+ {
+ let answer_ip = Ipv4Addr::new(answer.data[0],
+ answer.data[1],
+ answer.data[2],
+ answer.data[3]);
+ addrs.push(SocketAddr::V4(SocketAddrV4::new(answer_ip, 0)));
+ }
+ }
+ Ok(LookupHost(addrs.into_iter()))
+ },
+ Err(_err) => Err(Error::from_raw_os_error(EINVAL))
+ }
+ } else {
+ Err(Error::from_raw_os_error(EINVAL))
+ }
+}
+
+fn path_to_peer_addr(path_str: &str) -> SocketAddr {
+ let mut parts = path_str.split('/').next().unwrap_or("").split(':').skip(1);
+ let host = Ipv4Addr::from_str(parts.next().unwrap_or("")).unwrap_or(Ipv4Addr::new(0, 0, 0, 0));
+ let port = parts.next().unwrap_or("").parse::<u16>().unwrap_or(0);
+ SocketAddr::V4(SocketAddrV4::new(host, port))
+}
+
+fn path_to_local_addr(path_str: &str) -> SocketAddr {
+ let mut parts = path_str.split('/').nth(1).unwrap_or("").split(':');
+ let host = Ipv4Addr::from_str(parts.next().unwrap_or("")).unwrap_or(Ipv4Addr::new(0, 0, 0, 0));
+ let port = parts.next().unwrap_or("").parse::<u16>().unwrap_or(0);
+ SocketAddr::V4(SocketAddrV4::new(host, port))
+}
diff --git a/ctr-std/src/sys/redox/net/netc.rs b/ctr-std/src/sys/redox/net/netc.rs
new file mode 100644
index 0000000..d443a4d
--- /dev/null
+++ b/ctr-std/src/sys/redox/net/netc.rs
@@ -0,0 +1,57 @@
+// 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.
+
+pub type in_addr_t = u32;
+pub type in_port_t = u16;
+
+pub type socklen_t = u32;
+pub type sa_family_t = u16;
+
+pub const AF_INET: sa_family_t = 2;
+pub const AF_INET6: sa_family_t = 23;
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub struct in_addr {
+ pub s_addr: in_addr_t,
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub struct in6_addr {
+ pub s6_addr: [u8; 16],
+ __align: [u32; 0],
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub struct sockaddr {
+ pub sa_family: sa_family_t,
+ pub sa_data: [u8; 14],
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub struct sockaddr_in {
+ pub sin_family: sa_family_t,
+ pub sin_port: in_port_t,
+ pub sin_addr: in_addr,
+ pub sin_zero: [u8; 8],
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub struct sockaddr_in6 {
+ pub sin6_family: sa_family_t,
+ pub sin6_port: in_port_t,
+ pub sin6_flowinfo: u32,
+ pub sin6_addr: in6_addr,
+ pub sin6_scope_id: u32,
+}
diff --git a/ctr-std/src/sys/redox/net/tcp.rs b/ctr-std/src/sys/redox/net/tcp.rs
new file mode 100644
index 0000000..b566490
--- /dev/null
+++ b/ctr-std/src/sys/redox/net/tcp.rs
@@ -0,0 +1,253 @@
+// 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 cmp;
+use io::{self, Error, ErrorKind, Result};
+use mem;
+use net::{SocketAddr, Shutdown};
+use path::Path;
+use sys::fs::{File, OpenOptions};
+use sys::syscall::TimeSpec;
+use sys_common::{AsInner, FromInner, IntoInner};
+use time::Duration;
+
+use super::{path_to_peer_addr, path_to_local_addr};
+
+#[derive(Debug)]
+pub struct TcpStream(File);
+
+impl TcpStream {
+ pub fn connect(addr: &SocketAddr) -> Result<TcpStream> {
+ let path = format!("tcp:{}", addr);
+ let mut options = OpenOptions::new();
+ options.read(true);
+ options.write(true);
+ Ok(TcpStream(File::open(Path::new(path.as_str()), &options)?))
+ }
+
+ pub fn connect_timeout(_addr: &SocketAddr, _timeout: Duration) -> Result<TcpStream> {
+ Err(Error::new(ErrorKind::Other, "TcpStream::connect_timeout not implemented"))
+ }
+
+ pub fn duplicate(&self) -> Result<TcpStream> {
+ Ok(TcpStream(self.0.dup(&[])?))
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
+ self.0.read(buf)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> Result<usize> {
+ self.0.write(buf)
+ }
+
+ pub fn take_error(&self) -> Result<Option<Error>> {
+ Ok(None)
+ }
+
+ pub fn peer_addr(&self) -> Result<SocketAddr> {
+ let path = self.0.path()?;
+ Ok(path_to_peer_addr(path.to_str().unwrap_or("")))
+ }
+
+ pub fn socket_addr(&self) -> Result<SocketAddr> {
+ let path = self.0.path()?;
+ Ok(path_to_local_addr(path.to_str().unwrap_or("")))
+ }
+
+ pub fn peek(&self, _buf: &mut [u8]) -> Result<usize> {
+ Err(Error::new(ErrorKind::Other, "TcpStream::peek not implemented"))
+ }
+
+ pub fn shutdown(&self, _how: Shutdown) -> Result<()> {
+ Err(Error::new(ErrorKind::Other, "TcpStream::shutdown not implemented"))
+ }
+
+ pub fn nodelay(&self) -> Result<bool> {
+ Err(Error::new(ErrorKind::Other, "TcpStream::nodelay not implemented"))
+ }
+
+ pub fn nonblocking(&self) -> Result<bool> {
+ self.0.fd().nonblocking()
+ }
+
+ pub fn only_v6(&self) -> Result<bool> {
+ Err(Error::new(ErrorKind::Other, "TcpStream::only_v6 not implemented"))
+ }
+
+ pub fn ttl(&self) -> Result<u32> {
+ let mut ttl = [0];
+ let file = self.0.dup(b"ttl")?;
+ file.read(&mut ttl)?;
+ Ok(ttl[0] as u32)
+ }
+
+ pub fn read_timeout(&self) -> Result<Option<Duration>> {
+ let mut time = TimeSpec::default();
+ let file = self.0.dup(b"read_timeout")?;
+ if file.read(&mut time)? >= mem::size_of::<TimeSpec>() {
+ Ok(Some(Duration::new(time.tv_sec as u64, time.tv_nsec as u32)))
+ } else {
+ Ok(None)
+ }
+ }
+
+ pub fn write_timeout(&self) -> Result<Option<Duration>> {
+ let mut time = TimeSpec::default();
+ let file = self.0.dup(b"write_timeout")?;
+ if file.read(&mut time)? >= mem::size_of::<TimeSpec>() {
+ Ok(Some(Duration::new(time.tv_sec as u64, time.tv_nsec as u32)))
+ } else {
+ Ok(None)
+ }
+ }
+
+ pub fn set_nodelay(&self, _nodelay: bool) -> Result<()> {
+ Err(Error::new(ErrorKind::Other, "TcpStream::set_nodelay not implemented"))
+ }
+
+ pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()> {
+ self.0.fd().set_nonblocking(nonblocking)
+ }
+
+ pub fn set_only_v6(&self, _only_v6: bool) -> Result<()> {
+ Err(Error::new(ErrorKind::Other, "TcpStream::set_only_v6 not implemented"))
+ }
+
+ pub fn set_ttl(&self, ttl: u32) -> Result<()> {
+ let file = self.0.dup(b"ttl")?;
+ file.write(&[cmp::min(ttl, 255) as u8])?;
+ Ok(())
+ }
+
+ pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
+ let file = self.0.dup(b"read_timeout")?;
+ if let Some(duration) = duration_option {
+ if duration.as_secs() == 0 && duration.subsec_nanos() == 0 {
+ return Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot set a 0 duration timeout"));
+ }
+ file.write(&TimeSpec {
+ tv_sec: duration.as_secs() as i64,
+ tv_nsec: duration.subsec_nanos() as i32
+ })?;
+ } else {
+ file.write(&[])?;
+ }
+ Ok(())
+ }
+
+ pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
+ let file = self.0.dup(b"write_timeout")?;
+ if let Some(duration) = duration_option {
+ if duration.as_secs() == 0 && duration.subsec_nanos() == 0 {
+ return Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot set a 0 duration timeout"));
+ }
+ file.write(&TimeSpec {
+ tv_sec: duration.as_secs() as i64,
+ tv_nsec: duration.subsec_nanos() as i32
+ })?;
+ } else {
+ file.write(&[])?;
+ }
+ Ok(())
+ }
+}
+
+impl AsInner<File> for TcpStream {
+ fn as_inner(&self) -> &File { &self.0 }
+}
+
+impl FromInner<File> for TcpStream {
+ fn from_inner(file: File) -> TcpStream {
+ TcpStream(file)
+ }
+}
+
+impl IntoInner<File> for TcpStream {
+ fn into_inner(self) -> File { self.0 }
+}
+
+#[derive(Debug)]
+pub struct TcpListener(File);
+
+impl TcpListener {
+ pub fn bind(addr: &SocketAddr) -> Result<TcpListener> {
+ let path = format!("tcp:/{}", addr);
+ let mut options = OpenOptions::new();
+ options.read(true);
+ options.write(true);
+ Ok(TcpListener(File::open(Path::new(path.as_str()), &options)?))
+ }
+
+ pub fn accept(&self) -> Result<(TcpStream, SocketAddr)> {
+ let file = self.0.dup(b"listen")?;
+ let path = file.path()?;
+ let peer_addr = path_to_peer_addr(path.to_str().unwrap_or(""));
+ Ok((TcpStream(file), peer_addr))
+ }
+
+ pub fn duplicate(&self) -> Result<TcpListener> {
+ Ok(TcpListener(self.0.dup(&[])?))
+ }
+
+ pub fn take_error(&self) -> Result<Option<Error>> {
+ Ok(None)
+ }
+
+ pub fn socket_addr(&self) -> Result<SocketAddr> {
+ let path = self.0.path()?;
+ Ok(path_to_local_addr(path.to_str().unwrap_or("")))
+ }
+
+ pub fn nonblocking(&self) -> Result<bool> {
+ Err(Error::new(ErrorKind::Other, "TcpListener::nonblocking not implemented"))
+ }
+
+ pub fn only_v6(&self) -> Result<bool> {
+ Err(Error::new(ErrorKind::Other, "TcpListener::only_v6 not implemented"))
+ }
+
+ pub fn ttl(&self) -> Result<u32> {
+ let mut ttl = [0];
+ let file = self.0.dup(b"ttl")?;
+ file.read(&mut ttl)?;
+ Ok(ttl[0] as u32)
+ }
+
+ pub fn set_nonblocking(&self, _nonblocking: bool) -> Result<()> {
+ Err(Error::new(ErrorKind::Other, "TcpListener::set_nonblocking not implemented"))
+ }
+
+ pub fn set_only_v6(&self, _only_v6: bool) -> Result<()> {
+ Err(Error::new(ErrorKind::Other, "TcpListener::set_only_v6 not implemented"))
+ }
+
+ pub fn set_ttl(&self, ttl: u32) -> Result<()> {
+ let file = self.0.dup(b"ttl")?;
+ file.write(&[cmp::min(ttl, 255) as u8])?;
+ Ok(())
+ }
+}
+
+impl AsInner<File> for TcpListener {
+ fn as_inner(&self) -> &File { &self.0 }
+}
+
+impl FromInner<File> for TcpListener {
+ fn from_inner(file: File) -> TcpListener {
+ TcpListener(file)
+ }
+}
+
+impl IntoInner<File> for TcpListener {
+ fn into_inner(self) -> File { self.0 }
+}
diff --git a/ctr-std/src/sys/redox/net/udp.rs b/ctr-std/src/sys/redox/net/udp.rs
new file mode 100644
index 0000000..2ed67bd
--- /dev/null
+++ b/ctr-std/src/sys/redox/net/udp.rs
@@ -0,0 +1,242 @@
+// 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 cell::UnsafeCell;
+use cmp;
+use io::{self, Error, ErrorKind, Result};
+use mem;
+use net::{SocketAddr, Ipv4Addr, Ipv6Addr};
+use path::Path;
+use sys::fs::{File, OpenOptions};
+use sys::syscall::TimeSpec;
+use sys_common::{AsInner, FromInner, IntoInner};
+use time::Duration;
+
+use super::{path_to_peer_addr, path_to_local_addr};
+
+#[derive(Debug)]
+pub struct UdpSocket(File, UnsafeCell<Option<SocketAddr>>);
+
+impl UdpSocket {
+ pub fn bind(addr: &SocketAddr) -> Result<UdpSocket> {
+ let path = format!("udp:/{}", addr);
+ let mut options = OpenOptions::new();
+ options.read(true);
+ options.write(true);
+ Ok(UdpSocket(File::open(Path::new(path.as_str()), &options)?, UnsafeCell::new(None)))
+ }
+
+ fn get_conn(&self) -> &mut Option<SocketAddr> {
+ unsafe { &mut *(self.1.get()) }
+ }
+
+ pub fn connect(&self, addr: &SocketAddr) -> Result<()> {
+ unsafe { *self.1.get() = Some(*addr) };
+ Ok(())
+ }
+
+ pub fn duplicate(&self) -> Result<UdpSocket> {
+ let new_bind = self.0.dup(&[])?;
+ let new_conn = *self.get_conn();
+ Ok(UdpSocket(new_bind, UnsafeCell::new(new_conn)))
+ }
+
+ pub fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr)> {
+ let from = self.0.dup(b"listen")?;
+ let path = from.path()?;
+ let peer_addr = path_to_peer_addr(path.to_str().unwrap_or(""));
+ let count = from.read(buf)?;
+ Ok((count, peer_addr))
+ }
+
+ pub fn recv(&self, buf: &mut [u8]) -> Result<usize> {
+ if let Some(addr) = *self.get_conn() {
+ let from = self.0.dup(format!("{}", addr).as_bytes())?;
+ from.read(buf)
+ } else {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::recv not connected"))
+ }
+ }
+
+ pub fn send_to(&self, buf: &[u8], addr: &SocketAddr) -> Result<usize> {
+ let to = self.0.dup(format!("{}", addr).as_bytes())?;
+ to.write(buf)
+ }
+
+ pub fn send(&self, buf: &[u8]) -> Result<usize> {
+ if let Some(addr) = *self.get_conn() {
+ self.send_to(buf, &addr)
+ } else {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::send not connected"))
+ }
+ }
+
+ pub fn take_error(&self) -> Result<Option<Error>> {
+ Ok(None)
+ }
+
+ pub fn socket_addr(&self) -> Result<SocketAddr> {
+ let path = self.0.path()?;
+ Ok(path_to_local_addr(path.to_str().unwrap_or("")))
+ }
+
+ pub fn peek(&self, _buf: &mut [u8]) -> Result<usize> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::peek not implemented"))
+ }
+
+ pub fn peek_from(&self, _buf: &mut [u8]) -> Result<(usize, SocketAddr)> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::peek_from not implemented"))
+ }
+
+ pub fn broadcast(&self) -> Result<bool> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::broadcast not implemented"))
+ }
+
+ pub fn multicast_loop_v4(&self) -> Result<bool> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::multicast_loop_v4 not implemented"))
+ }
+
+ pub fn multicast_loop_v6(&self) -> Result<bool> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::multicast_loop_v6 not implemented"))
+ }
+
+ pub fn multicast_ttl_v4(&self) -> Result<u32> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::multicast_ttl_v4 not implemented"))
+ }
+
+ pub fn nonblocking(&self) -> Result<bool> {
+ self.0.fd().nonblocking()
+ }
+
+ pub fn only_v6(&self) -> Result<bool> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::only_v6 not implemented"))
+ }
+
+ pub fn ttl(&self) -> Result<u32> {
+ let mut ttl = [0];
+ let file = self.0.dup(b"ttl")?;
+ file.read(&mut ttl)?;
+ Ok(ttl[0] as u32)
+ }
+
+ pub fn read_timeout(&self) -> Result<Option<Duration>> {
+ let mut time = TimeSpec::default();
+ let file = self.0.dup(b"read_timeout")?;
+ if file.read(&mut time)? >= mem::size_of::<TimeSpec>() {
+ Ok(Some(Duration::new(time.tv_sec as u64, time.tv_nsec as u32)))
+ } else {
+ Ok(None)
+ }
+ }
+
+ pub fn write_timeout(&self) -> Result<Option<Duration>> {
+ let mut time = TimeSpec::default();
+ let file = self.0.dup(b"write_timeout")?;
+ if file.read(&mut time)? >= mem::size_of::<TimeSpec>() {
+ Ok(Some(Duration::new(time.tv_sec as u64, time.tv_nsec as u32)))
+ } else {
+ Ok(None)
+ }
+ }
+
+ pub fn set_broadcast(&self, _broadcast: bool) -> Result<()> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::set_broadcast not implemented"))
+ }
+
+ pub fn set_multicast_loop_v4(&self, _multicast_loop_v4: bool) -> Result<()> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::set_multicast_loop_v4 not implemented"))
+ }
+
+ pub fn set_multicast_loop_v6(&self, _multicast_loop_v6: bool) -> Result<()> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::set_multicast_loop_v6 not implemented"))
+ }
+
+ pub fn set_multicast_ttl_v4(&self, _multicast_ttl_v4: u32) -> Result<()> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::set_multicast_ttl_v4 not implemented"))
+ }
+
+ pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()> {
+ self.0.fd().set_nonblocking(nonblocking)
+ }
+
+ pub fn set_only_v6(&self, _only_v6: bool) -> Result<()> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::set_only_v6 not implemented"))
+ }
+
+ pub fn set_ttl(&self, ttl: u32) -> Result<()> {
+ let file = self.0.dup(b"ttl")?;
+ file.write(&[cmp::min(ttl, 255) as u8])?;
+ Ok(())
+ }
+
+ pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
+ let file = self.0.dup(b"read_timeout")?;
+ if let Some(duration) = duration_option {
+ if duration.as_secs() == 0 && duration.subsec_nanos() == 0 {
+ return Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot set a 0 duration timeout"));
+ }
+ file.write(&TimeSpec {
+ tv_sec: duration.as_secs() as i64,
+ tv_nsec: duration.subsec_nanos() as i32
+ })?;
+ } else {
+ file.write(&[])?;
+ }
+ Ok(())
+ }
+
+ pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
+ let file = self.0.dup(b"write_timeout")?;
+ if let Some(duration) = duration_option {
+ if duration.as_secs() == 0 && duration.subsec_nanos() == 0 {
+ return Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot set a 0 duration timeout"));
+ }
+ file.write(&TimeSpec {
+ tv_sec: duration.as_secs() as i64,
+ tv_nsec: duration.subsec_nanos() as i32
+ })?;
+ } else {
+ file.write(&[])?;
+ }
+ Ok(())
+ }
+
+ pub fn join_multicast_v4(&self, _multiaddr: &Ipv4Addr, _interface: &Ipv4Addr) -> Result<()> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::join_multicast_v4 not implemented"))
+ }
+
+ pub fn join_multicast_v6(&self, _multiaddr: &Ipv6Addr, _interface: u32) -> Result<()> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::join_multicast_v6 not implemented"))
+ }
+
+ pub fn leave_multicast_v4(&self, _multiaddr: &Ipv4Addr, _interface: &Ipv4Addr) -> Result<()> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::leave_multicast_v4 not implemented"))
+ }
+
+ pub fn leave_multicast_v6(&self, _multiaddr: &Ipv6Addr, _interface: u32) -> Result<()> {
+ Err(Error::new(ErrorKind::Other, "UdpSocket::leave_multicast_v6 not implemented"))
+ }
+}
+
+impl AsInner<File> for UdpSocket {
+ fn as_inner(&self) -> &File { &self.0 }
+}
+
+impl FromInner<File> for UdpSocket {
+ fn from_inner(file: File) -> UdpSocket {
+ UdpSocket(file, UnsafeCell::new(None))
+ }
+}
+
+impl IntoInner<File> for UdpSocket {
+ fn into_inner(self) -> File { self.0 }
+}
diff --git a/ctr-std/src/sys/redox/os.rs b/ctr-std/src/sys/redox/os.rs
new file mode 100644
index 0000000..480765b
--- /dev/null
+++ b/ctr-std/src/sys/redox/os.rs
@@ -0,0 +1,219 @@
+// 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.
+
+//! Implementation of `std::os` functionality for unix systems
+
+#![allow(unused_imports)] // lots of cfg code here
+
+use os::unix::prelude::*;
+
+use error::Error as StdError;
+use ffi::{OsString, OsStr};
+use fmt;
+use io::{self, Read, Write};
+use iter;
+use marker::PhantomData;
+use mem;
+use memchr;
+use path::{self, PathBuf};
+use ptr;
+use slice;
+use str;
+use sys_common::mutex::Mutex;
+use sys::{cvt, fd, syscall};
+use vec;
+
+const TMPBUF_SZ: usize = 128;
+static ENV_LOCK: Mutex = Mutex::new();
+
+extern {
+ #[link_name = "__errno_location"]
+ fn errno_location() -> *mut i32;
+}
+
+/// Returns the platform-specific value of errno
+pub fn errno() -> i32 {
+ unsafe {
+ (*errno_location())
+ }
+}
+
+/// Gets a detailed string description for the given error number.
+pub fn error_string(errno: i32) -> String {
+ if let Some(string) = syscall::STR_ERROR.get(errno as usize) {
+ string.to_string()
+ } else {
+ "unknown error".to_string()
+ }
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+ let mut buf = [0; 4096];
+ let count = cvt(syscall::getcwd(&mut buf))?;
+ Ok(PathBuf::from(OsString::from_vec(buf[.. count].to_vec())))
+}
+
+pub fn chdir(p: &path::Path) -> io::Result<()> {
+ cvt(syscall::chdir(p.to_str().unwrap())).and(Ok(()))
+}
+
+pub struct SplitPaths<'a> {
+ iter: iter::Map<slice::Split<'a, u8, fn(&u8) -> bool>,
+ fn(&'a [u8]) -> PathBuf>,
+}
+
+pub fn split_paths(unparsed: &OsStr) -> SplitPaths {
+ fn bytes_to_path(b: &[u8]) -> PathBuf {
+ PathBuf::from(<OsStr as OsStrExt>::from_bytes(b))
+ }
+ fn is_semicolon(b: &u8) -> bool { *b == b';' }
+ let unparsed = unparsed.as_bytes();
+ SplitPaths {
+ iter: unparsed.split(is_semicolon as fn(&u8) -> bool)
+ .map(bytes_to_path as fn(&[u8]) -> PathBuf)
+ }
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+ type Item = PathBuf;
+ fn next(&mut self) -> Option<PathBuf> { self.iter.next() }
+ fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
+}
+
+#[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';';
+
+ for (i, path) in paths.enumerate() {
+ let path = path.as_ref().as_bytes();
+ if i > 0 { joined.push(sep) }
+ if path.contains(&sep) {
+ return Err(JoinPathsError)
+ }
+ joined.extend_from_slice(path);
+ }
+ Ok(OsStringExt::from_vec(joined))
+}
+
+impl fmt::Display for JoinPathsError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ "path segment contains separator `:`".fmt(f)
+ }
+}
+
+impl StdError for JoinPathsError {
+ fn description(&self) -> &str { "failed to join paths" }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+ use fs::File;
+
+ let mut file = File::open("sys:exe")?;
+
+ let mut path = String::new();
+ file.read_to_string(&mut path)?;
+
+ if path.ends_with('\n') {
+ path.pop();
+ }
+
+ Ok(PathBuf::from(path))
+}
+
+pub struct Env {
+ iter: vec::IntoIter<(OsString, OsString)>,
+ _dont_send_or_sync_me: PhantomData<*mut ()>,
+}
+
+impl Iterator for Env {
+ type Item = (OsString, OsString);
+ fn next(&mut self) -> Option<(OsString, OsString)> { self.iter.next() }
+ fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
+}
+
+/// Returns a vector of (variable, value) byte-vector pairs for all the
+/// environment variables of the current process.
+pub fn env() -> Env {
+ let mut variables: Vec<(OsString, OsString)> = Vec::new();
+ if let Ok(mut file) = ::fs::File::open("env:") {
+ let mut string = String::new();
+ if file.read_to_string(&mut string).is_ok() {
+ for line in string.lines() {
+ let mut parts = line.splitn(2, '=');
+ if let Some(name) = parts.next() {
+ let value = parts.next().unwrap_or("");
+ variables.push((OsString::from(name.to_string()),
+ OsString::from(value.to_string())));
+ }
+ }
+ }
+ }
+ Env { iter: variables.into_iter(), _dont_send_or_sync_me: PhantomData }
+}
+
+pub fn getenv(key: &OsStr) -> io::Result<Option<OsString>> {
+ if ! key.is_empty() {
+ if let Ok(mut file) = ::fs::File::open(&("env:".to_owned() + key.to_str().unwrap())) {
+ let mut string = String::new();
+ file.read_to_string(&mut string)?;
+ Ok(Some(OsString::from(string)))
+ } else {
+ Ok(None)
+ }
+ } else {
+ Ok(None)
+ }
+}
+
+pub fn setenv(key: &OsStr, value: &OsStr) -> io::Result<()> {
+ if ! key.is_empty() {
+ let mut file = ::fs::File::create(&("env:".to_owned() + key.to_str().unwrap()))?;
+ file.write_all(value.as_bytes())?;
+ file.set_len(value.len() as u64)?;
+ }
+ Ok(())
+}
+
+pub fn unsetenv(key: &OsStr) -> io::Result<()> {
+ ::fs::remove_file(&("env:".to_owned() + key.to_str().unwrap()))?;
+ Ok(())
+}
+
+pub fn page_size() -> usize {
+ 4096
+}
+
+pub fn temp_dir() -> PathBuf {
+ ::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| {
+ PathBuf::from("/tmp")
+ })
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+ return ::env::var_os("HOME").map(PathBuf::from);
+}
+
+pub fn exit(code: i32) -> ! {
+ let _ = syscall::exit(code as usize);
+ unreachable!();
+}
+
+pub fn getpid() -> u32 {
+ syscall::getpid().unwrap() as u32
+}
+
+pub fn getppid() -> u32 {
+ syscall::getppid().unwrap() as u32
+}
diff --git a/ctr-std/src/sys/redox/os_str.rs b/ctr-std/src/sys/redox/os_str.rs
new file mode 100644
index 0000000..eb3a1ea
--- /dev/null
+++ b/ctr-std/src/sys/redox/os_str.rs
@@ -0,0 +1,189 @@
+// 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.
+
+/// The underlying OsString/OsStr implementation on Unix systems: just
+/// a `Vec<u8>`/`[u8]`.
+
+use borrow::Cow;
+use fmt;
+use str;
+use mem;
+use rc::Rc;
+use sync::Arc;
+use sys_common::{AsInner, IntoInner};
+use sys_common::bytestring::debug_fmt_bytestring;
+use core::str::lossy::Utf8Lossy;
+
+#[derive(Clone, Hash)]
+pub struct Buf {
+ pub inner: Vec<u8>
+}
+
+pub struct Slice {
+ pub inner: [u8]
+}
+
+impl fmt::Debug for Slice {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ debug_fmt_bytestring(&self.inner, formatter)
+ }
+}
+
+impl fmt::Display for Slice {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter)
+ }
+}
+
+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)
+ }
+}
+
+impl IntoInner<Vec<u8>> for Buf {
+ fn into_inner(self) -> Vec<u8> {
+ self.inner
+ }
+}
+
+impl AsInner<[u8]> for Buf {
+ fn as_inner(&self) -> &[u8] {
+ &self.inner
+ }
+}
+
+
+impl Buf {
+ pub fn from_string(s: String) -> Buf {
+ Buf { inner: s.into_bytes() }
+ }
+
+ #[inline]
+ pub fn with_capacity(capacity: usize) -> Buf {
+ Buf {
+ inner: Vec::with_capacity(capacity)
+ }
+ }
+
+ #[inline]
+ pub fn clear(&mut self) {
+ self.inner.clear()
+ }
+
+ #[inline]
+ pub fn capacity(&self) -> usize {
+ self.inner.capacity()
+ }
+
+ #[inline]
+ pub fn reserve(&mut self, additional: usize) {
+ self.inner.reserve(additional)
+ }
+
+ #[inline]
+ pub fn reserve_exact(&mut self, additional: usize) {
+ self.inner.reserve_exact(additional)
+ }
+
+ #[inline]
+ 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)
+ }
+
+ pub fn as_slice(&self) -> &Slice {
+ unsafe { mem::transmute(&*self.inner) }
+ }
+
+ pub fn into_string(self) -> Result<String, Buf> {
+ String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } )
+ }
+
+ pub fn push_slice(&mut self, s: &Slice) {
+ self.inner.extend_from_slice(&s.inner)
+ }
+
+ #[inline]
+ pub fn into_box(self) -> Box<Slice> {
+ unsafe { mem::transmute(self.inner.into_boxed_slice()) }
+ }
+
+ #[inline]
+ pub fn from_box(boxed: Box<Slice>) -> Buf {
+ let inner: Box<[u8]> = unsafe { mem::transmute(boxed) };
+ Buf { inner: inner.into_vec() }
+ }
+
+ #[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 {
+ fn from_u8_slice(s: &[u8]) -> &Slice {
+ unsafe { mem::transmute(s) }
+ }
+
+ pub fn from_str(s: &str) -> &Slice {
+ Slice::from_u8_slice(s.as_bytes())
+ }
+
+ pub fn to_str(&self) -> Option<&str> {
+ str::from_utf8(&self.inner).ok()
+ }
+
+ pub fn to_string_lossy(&self) -> Cow<str> {
+ String::from_utf8_lossy(&self.inner)
+ }
+
+ pub fn to_owned(&self) -> Buf {
+ Buf { inner: self.inner.to_vec() }
+ }
+
+ #[inline]
+ pub fn into_box(&self) -> Box<Slice> {
+ let boxed: Box<[u8]> = self.inner.into();
+ unsafe { mem::transmute(boxed) }
+ }
+
+ pub fn empty_box() -> Box<Slice> {
+ let boxed: Box<[u8]> = Default::default();
+ unsafe { mem::transmute(boxed) }
+ }
+
+ #[inline]
+ pub fn into_arc(&self) -> Arc<Slice> {
+ let arc: Arc<[u8]> = Arc::from(&self.inner);
+ unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) }
+ }
+
+ #[inline]
+ pub fn into_rc(&self) -> Rc<Slice> {
+ let rc: Rc<[u8]> = Rc::from(&self.inner);
+ unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) }
+ }
+}
diff --git a/ctr-std/src/sys/redox/path.rs b/ctr-std/src/sys/redox/path.rs
new file mode 100644
index 0000000..e6a267d
--- /dev/null
+++ b/ctr-std/src/sys/redox/path.rs
@@ -0,0 +1,39 @@
+// 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 ffi::OsStr;
+use path::Prefix;
+
+#[inline]
+pub fn is_sep_byte(b: u8) -> bool {
+ b == b'/'
+}
+
+#[inline]
+pub fn is_verbatim_sep(b: u8) -> bool {
+ b == b'/'
+}
+
+pub fn parse_prefix(path: &OsStr) -> Option<Prefix> {
+ if let Some(path_str) = path.to_str() {
+ if let Some(_i) = path_str.find(':') {
+ // FIXME: Redox specific prefix
+ // Some(Prefix::Verbatim(OsStr::new(&path_str[..i])))
+ None
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+}
+
+pub const MAIN_SEP_STR: &'static str = "/";
+pub const MAIN_SEP: char = '/';
diff --git a/ctr-std/src/sys/redox/pipe.rs b/ctr-std/src/sys/redox/pipe.rs
new file mode 100644
index 0000000..28645fa
--- /dev/null
+++ b/ctr-std/src/sys/redox/pipe.rs
@@ -0,0 +1,103 @@
+// 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 io;
+use sys::{cvt, syscall};
+use sys::fd::FileDesc;
+
+////////////////////////////////////////////////////////////////////////////////
+// Anonymous pipes
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct AnonPipe(FileDesc);
+
+pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
+ let mut fds = [0; 2];
+ cvt(syscall::pipe2(&mut fds, syscall::O_CLOEXEC))?;
+ Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1]))))
+}
+
+impl AnonPipe {
+ pub fn from_fd(fd: FileDesc) -> io::Result<AnonPipe> {
+ fd.set_cloexec()?;
+ Ok(AnonPipe(fd))
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ pub fn fd(&self) -> &FileDesc { &self.0 }
+ pub fn into_fd(self) -> FileDesc { self.0 }
+}
+
+pub fn read2(p1: AnonPipe,
+ v1: &mut Vec<u8>,
+ p2: AnonPipe,
+ v2: &mut Vec<u8>) -> io::Result<()> {
+ //FIXME: Use event based I/O multiplexing
+ //unimplemented!()
+
+ p1.0.read_to_end(v1)?;
+ p2.0.read_to_end(v2)?;
+
+ Ok(())
+
+ /*
+ // Set both pipes into nonblocking mode as we're gonna be reading from both
+ // in the `select` loop below, and we wouldn't want one to block the other!
+ let p1 = p1.into_fd();
+ let p2 = p2.into_fd();
+ p1.set_nonblocking(true)?;
+ p2.set_nonblocking(true)?;
+
+ loop {
+ // wait for either pipe to become readable using `select`
+ cvt_r(|| unsafe {
+ let mut read: libc::fd_set = mem::zeroed();
+ libc::FD_SET(p1.raw(), &mut read);
+ libc::FD_SET(p2.raw(), &mut read);
+ libc::select(max + 1, &mut read, ptr::null_mut(), ptr::null_mut(),
+ ptr::null_mut())
+ })?;
+
+ // Read as much as we can from each pipe, ignoring EWOULDBLOCK or
+ // EAGAIN. If we hit EOF, then this will happen because the underlying
+ // reader will return Ok(0), in which case we'll see `Ok` ourselves. In
+ // this case we flip the other fd back into blocking mode and read
+ // whatever's leftover on that file descriptor.
+ let read = |fd: &FileDesc, dst: &mut Vec<u8>| {
+ match fd.read_to_end(dst) {
+ Ok(_) => Ok(true),
+ Err(e) => {
+ if e.raw_os_error() == Some(libc::EWOULDBLOCK) ||
+ e.raw_os_error() == Some(libc::EAGAIN) {
+ Ok(false)
+ } else {
+ Err(e)
+ }
+ }
+ }
+ };
+ if read(&p1, v1)? {
+ p2.set_nonblocking(false)?;
+ return p2.read_to_end(v2).map(|_| ());
+ }
+ if read(&p2, v2)? {
+ p1.set_nonblocking(false)?;
+ return p1.read_to_end(v1).map(|_| ());
+ }
+ }
+ */
+}
diff --git a/ctr-std/src/sys/redox/process.rs b/ctr-std/src/sys/redox/process.rs
new file mode 100644
index 0000000..d0b94e1
--- /dev/null
+++ b/ctr-std/src/sys/redox/process.rs
@@ -0,0 +1,543 @@
+// 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 env::{split_paths};
+use ffi::OsStr;
+use os::unix::ffi::OsStrExt;
+use fmt;
+use io::{self, Error, ErrorKind};
+use libc::{EXIT_SUCCESS, EXIT_FAILURE};
+use path::{Path, PathBuf};
+use sys::fd::FileDesc;
+use sys::fs::{File, OpenOptions};
+use sys::pipe::{self, AnonPipe};
+use sys::{cvt, syscall};
+use sys_common::process::{CommandEnv, DefaultEnvKey};
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct Command {
+ // Currently we try hard to ensure that the call to `.exec()` doesn't
+ // actually allocate any memory. While many platforms try to ensure that
+ // memory allocation works after a fork in a multithreaded process, it's
+ // been observed to be buggy and somewhat unreliable, so we do our best to
+ // just not do it at all!
+ //
+ // Along those lines, the `argv` and `envp` raw pointers here are exactly
+ // what's gonna get passed to `execvp`. The `argv` array starts with the
+ // `program` and ends with a NULL, and the `envp` pointer, if present, is
+ // also null-terminated.
+ //
+ // Right now we don't support removing arguments, so there's no much fancy
+ // support there, but we support adding and removing environment variables,
+ // so a side table is used to track where in the `envp` array each key is
+ // located. Whenever we add a key we update it in place if it's already
+ // present, and whenever we remove a key we update the locations of all
+ // other keys.
+ program: String,
+ args: Vec<String>,
+ env: CommandEnv<DefaultEnvKey>,
+
+ cwd: Option<String>,
+ uid: Option<u32>,
+ gid: Option<u32>,
+ saw_nul: bool,
+ closures: Vec<Box<FnMut() -> io::Result<()> + Send + Sync>>,
+ stdin: Option<Stdio>,
+ stdout: Option<Stdio>,
+ stderr: Option<Stdio>,
+}
+
+// passed back to std::process with the pipes connected to the child, if any
+// were requested
+pub struct StdioPipes {
+ pub stdin: Option<AnonPipe>,
+ pub stdout: Option<AnonPipe>,
+ pub stderr: Option<AnonPipe>,
+}
+
+// passed to do_exec() with configuration of what the child stdio should look
+// like
+struct ChildPipes {
+ stdin: ChildStdio,
+ stdout: ChildStdio,
+ stderr: ChildStdio,
+}
+
+enum ChildStdio {
+ Inherit,
+ Explicit(usize),
+ Owned(FileDesc),
+}
+
+pub enum Stdio {
+ Inherit,
+ Null,
+ MakePipe,
+ Fd(FileDesc),
+}
+
+impl Command {
+ pub fn new(program: &OsStr) -> Command {
+ Command {
+ program: program.to_str().unwrap().to_owned(),
+ args: Vec::new(),
+ env: Default::default(),
+ cwd: None,
+ uid: None,
+ gid: None,
+ saw_nul: false,
+ closures: Vec::new(),
+ stdin: None,
+ stdout: None,
+ stderr: None,
+ }
+ }
+
+ pub fn arg(&mut self, arg: &OsStr) {
+ self.args.push(arg.to_str().unwrap().to_owned());
+ }
+
+ pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> {
+ &mut self.env
+ }
+
+ pub fn cwd(&mut self, dir: &OsStr) {
+ self.cwd = Some(dir.to_str().unwrap().to_owned());
+ }
+ pub fn uid(&mut self, id: u32) {
+ self.uid = Some(id);
+ }
+ pub fn gid(&mut self, id: u32) {
+ self.gid = Some(id);
+ }
+
+ pub fn before_exec(&mut self,
+ f: Box<FnMut() -> io::Result<()> + Send + Sync>) {
+ self.closures.push(f);
+ }
+
+ 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 spawn(&mut self, default: Stdio, needs_stdin: bool)
+ -> io::Result<(Process, StdioPipes)> {
+ const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
+
+ if self.saw_nul {
+ return Err(io::Error::new(ErrorKind::InvalidInput,
+ "nul byte found in provided data"));
+ }
+
+ let (ours, theirs) = self.setup_io(default, needs_stdin)?;
+ let (input, output) = pipe::anon_pipe()?;
+
+ let pid = unsafe {
+ match cvt(syscall::clone(0))? {
+ 0 => {
+ drop(input);
+ let err = self.do_exec(theirs);
+ let errno = err.raw_os_error().unwrap_or(syscall::EINVAL) as u32;
+ let bytes = [
+ (errno >> 24) as u8,
+ (errno >> 16) as u8,
+ (errno >> 8) as u8,
+ (errno >> 0) as u8,
+ CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1],
+ CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3]
+ ];
+ // pipe I/O up to PIPE_BUF bytes should be atomic, and then
+ // we want to be sure we *don't* run at_exit destructors as
+ // we're being torn down regardless
+ assert!(output.write(&bytes).is_ok());
+ let _ = syscall::exit(1);
+ panic!("failed to exit");
+ }
+ n => n,
+ }
+ };
+
+ let mut p = Process { pid: pid, status: None };
+ drop(output);
+ let mut bytes = [0; 8];
+
+ // loop to handle EINTR
+ loop {
+ match input.read(&mut bytes) {
+ Ok(0) => return Ok((p, ours)),
+ Ok(8) => {
+ assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]),
+ "Validation on the CLOEXEC pipe failed: {:?}", bytes);
+ let errno = combine(&bytes[0.. 4]);
+ assert!(p.wait().is_ok(),
+ "wait() should either return Ok or panic");
+ return Err(Error::from_raw_os_error(errno))
+ }
+ Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
+ Err(e) => {
+ assert!(p.wait().is_ok(),
+ "wait() should either return Ok or panic");
+ panic!("the CLOEXEC pipe failed: {:?}", e)
+ },
+ Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic
+ assert!(p.wait().is_ok(),
+ "wait() should either return Ok or panic");
+ panic!("short read on the CLOEXEC pipe")
+ }
+ }
+ }
+
+ fn combine(arr: &[u8]) -> i32 {
+ let a = arr[0] as u32;
+ let b = arr[1] as u32;
+ let c = arr[2] as u32;
+ let d = arr[3] as u32;
+
+ ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32
+ }
+ }
+
+ pub fn exec(&mut self, default: Stdio) -> io::Error {
+ if self.saw_nul {
+ return io::Error::new(ErrorKind::InvalidInput,
+ "nul byte found in provided data")
+ }
+
+ match self.setup_io(default, true) {
+ Ok((_, theirs)) => unsafe { self.do_exec(theirs) },
+ Err(e) => e,
+ }
+ }
+
+ // And at this point we've reached a special time in the life of the
+ // child. The child must now be considered hamstrung and unable to
+ // do anything other than syscalls really. Consider the following
+ // scenario:
+ //
+ // 1. Thread A of process 1 grabs the malloc() mutex
+ // 2. Thread B of process 1 forks(), creating thread C
+ // 3. Thread C of process 2 then attempts to malloc()
+ // 4. The memory of process 2 is the same as the memory of
+ // process 1, so the mutex is locked.
+ //
+ // This situation looks a lot like deadlock, right? It turns out
+ // that this is what pthread_atfork() takes care of, which is
+ // presumably implemented across platforms. The first thing that
+ // threads to *before* forking is to do things like grab the malloc
+ // mutex, and then after the fork they unlock it.
+ //
+ // Despite this information, libnative's spawn has been witnessed to
+ // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but
+ // all collected backtraces point at malloc/free traffic in the
+ // child spawned process.
+ //
+ // For this reason, the block of code below should contain 0
+ // invocations of either malloc of free (or their related friends).
+ //
+ // As an example of not having malloc/free traffic, we don't close
+ // this file descriptor by dropping the FileDesc (which contains an
+ // allocation). Instead we just close it manually. This will never
+ // have the drop glue anyway because this code never returns (the
+ // child will either exec() or invoke syscall::exit)
+ unsafe fn do_exec(&mut self, stdio: ChildPipes) -> io::Error {
+ macro_rules! t {
+ ($e:expr) => (match $e {
+ Ok(e) => e,
+ Err(e) => return e,
+ })
+ }
+
+ if let Some(fd) = stdio.stderr.fd() {
+ t!(cvt(syscall::dup2(fd, 2, &[])));
+ let mut flags = t!(cvt(syscall::fcntl(2, syscall::F_GETFD, 0)));
+ flags &= ! syscall::O_CLOEXEC;
+ t!(cvt(syscall::fcntl(2, syscall::F_SETFD, flags)));
+ }
+ if let Some(fd) = stdio.stdout.fd() {
+ t!(cvt(syscall::dup2(fd, 1, &[])));
+ let mut flags = t!(cvt(syscall::fcntl(1, syscall::F_GETFD, 0)));
+ flags &= ! syscall::O_CLOEXEC;
+ t!(cvt(syscall::fcntl(1, syscall::F_SETFD, flags)));
+ }
+ if let Some(fd) = stdio.stdin.fd() {
+ t!(cvt(syscall::dup2(fd, 0, &[])));
+ let mut flags = t!(cvt(syscall::fcntl(0, syscall::F_GETFD, 0)));
+ flags &= ! syscall::O_CLOEXEC;
+ t!(cvt(syscall::fcntl(0, syscall::F_SETFD, flags)));
+ }
+
+ if let Some(g) = self.gid {
+ t!(cvt(syscall::setregid(g as usize, g as usize)));
+ }
+ if let Some(u) = self.uid {
+ t!(cvt(syscall::setreuid(u as usize, u as usize)));
+ }
+ if let Some(ref cwd) = self.cwd {
+ t!(cvt(syscall::chdir(cwd)));
+ }
+
+ for callback in self.closures.iter_mut() {
+ t!(callback());
+ }
+
+ let mut args: Vec<[usize; 2]> = Vec::new();
+ args.push([self.program.as_ptr() as usize, self.program.len()]);
+ for arg in self.args.iter() {
+ args.push([arg.as_ptr() as usize, arg.len()]);
+ }
+
+ self.env.apply();
+
+ let program = if self.program.contains(':') || self.program.contains('/') {
+ Some(PathBuf::from(&self.program))
+ } else if let Ok(path_env) = ::env::var("PATH") {
+ let mut program = None;
+ for mut path in split_paths(&path_env) {
+ path.push(&self.program);
+ if path.exists() {
+ program = Some(path);
+ break;
+ }
+ }
+ program
+ } else {
+ None
+ };
+
+ if let Some(program) = program {
+ if let Err(err) = syscall::execve(program.as_os_str().as_bytes(), &args) {
+ io::Error::from_raw_os_error(err.errno as i32)
+ } else {
+ panic!("return from exec without err");
+ }
+ } else {
+ io::Error::from_raw_os_error(syscall::ENOENT)
+ }
+ }
+
+
+ fn setup_io(&self, default: Stdio, needs_stdin: bool)
+ -> io::Result<(StdioPipes, ChildPipes)> {
+ 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 (their_stdin, our_stdin) = stdin.to_child_stdio(true)?;
+ let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?;
+ let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?;
+ let ours = StdioPipes {
+ stdin: our_stdin,
+ stdout: our_stdout,
+ stderr: our_stderr,
+ };
+ let theirs = ChildPipes {
+ stdin: their_stdin,
+ stdout: their_stdout,
+ stderr: their_stderr,
+ };
+ Ok((ours, theirs))
+ }
+}
+
+impl Stdio {
+ fn to_child_stdio(&self, readable: bool)
+ -> io::Result<(ChildStdio, Option<AnonPipe>)> {
+ match *self {
+ Stdio::Inherit => Ok((ChildStdio::Inherit, None)),
+
+ // Make sure that the source descriptors are not an stdio
+ // descriptor, otherwise the order which we set the child's
+ // descriptors may blow away a descriptor which we are hoping to
+ // save. For example, suppose we want the child's stderr to be the
+ // parent's stdout, and the child's stdout to be the parent's
+ // stderr. No matter which we dup first, the second will get
+ // overwritten prematurely.
+ Stdio::Fd(ref fd) => {
+ if fd.raw() <= 2 {
+ Ok((ChildStdio::Owned(fd.duplicate()?), None))
+ } else {
+ Ok((ChildStdio::Explicit(fd.raw()), None))
+ }
+ }
+
+ Stdio::MakePipe => {
+ let (reader, writer) = pipe::anon_pipe()?;
+ let (ours, theirs) = if readable {
+ (writer, reader)
+ } else {
+ (reader, writer)
+ };
+ Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours)))
+ }
+
+ Stdio::Null => {
+ let mut opts = OpenOptions::new();
+ opts.read(readable);
+ opts.write(!readable);
+ let fd = File::open(Path::new("null:"), &opts)?;
+ Ok((ChildStdio::Owned(fd.into_fd()), None))
+ }
+ }
+ }
+}
+
+impl From<AnonPipe> for Stdio {
+ fn from(pipe: AnonPipe) -> Stdio {
+ Stdio::Fd(pipe.into_fd())
+ }
+}
+
+impl From<File> for Stdio {
+ fn from(file: File) -> Stdio {
+ Stdio::Fd(file.into_fd())
+ }
+}
+
+impl ChildStdio {
+ fn fd(&self) -> Option<usize> {
+ match *self {
+ ChildStdio::Inherit => None,
+ ChildStdio::Explicit(fd) => Some(fd),
+ ChildStdio::Owned(ref fd) => Some(fd.raw()),
+ }
+ }
+}
+
+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(())
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Processes
+////////////////////////////////////////////////////////////////////////////////
+
+/// Unix exit statuses
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatus(i32);
+
+impl ExitStatus {
+ fn exited(&self) -> bool {
+ self.0 & 0x7F == 0
+ }
+
+ pub fn success(&self) -> bool {
+ self.code() == Some(0)
+ }
+
+ pub fn code(&self) -> Option<i32> {
+ if self.exited() {
+ Some((self.0 >> 8) & 0xFF)
+ } else {
+ None
+ }
+ }
+
+ pub fn signal(&self) -> Option<i32> {
+ if !self.exited() {
+ Some(self.0 & 0x7F)
+ } else {
+ None
+ }
+ }
+}
+
+impl From<i32> for ExitStatus {
+ fn from(a: i32) -> ExitStatus {
+ ExitStatus(a)
+ }
+}
+
+impl fmt::Display for ExitStatus {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if let Some(code) = self.code() {
+ write!(f, "exit code: {}", code)
+ } else {
+ let signal = self.signal().unwrap();
+ write!(f, "signal: {}", signal)
+ }
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitCode(u8);
+
+impl ExitCode {
+ pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _);
+ pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _);
+
+ pub fn as_i32(&self) -> i32 {
+ self.0 as i32
+ }
+}
+
+/// The unique id of the process (this should never be negative).
+pub struct Process {
+ pid: usize,
+ status: Option<ExitStatus>,
+}
+
+impl Process {
+ pub fn id(&self) -> u32 {
+ self.pid as u32
+ }
+
+ pub fn kill(&mut self) -> io::Result<()> {
+ // If we've already waited on this process then the pid can be recycled
+ // and used for another process, and we probably shouldn't be killing
+ // random processes, so just return an error.
+ if self.status.is_some() {
+ Err(Error::new(ErrorKind::InvalidInput,
+ "invalid argument: can't kill an exited process"))
+ } else {
+ cvt(syscall::kill(self.pid, syscall::SIGKILL))?;
+ Ok(())
+ }
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ if let Some(status) = self.status {
+ return Ok(status)
+ }
+ let mut status = 0;
+ cvt(syscall::waitpid(self.pid, &mut status, 0))?;
+ self.status = Some(ExitStatus(status as i32));
+ Ok(ExitStatus(status as i32))
+ }
+
+ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+ if let Some(status) = self.status {
+ return Ok(Some(status))
+ }
+ let mut status = 0;
+ let pid = cvt(syscall::waitpid(self.pid, &mut status, syscall::WNOHANG))?;
+ if pid == 0 {
+ Ok(None)
+ } else {
+ self.status = Some(ExitStatus(status as i32));
+ Ok(Some(ExitStatus(status as i32)))
+ }
+ }
+}
diff --git a/ctr-std/src/sys/redox/rand.rs b/ctr-std/src/sys/redox/rand.rs
new file mode 100644
index 0000000..3b378f5
--- /dev/null
+++ b/ctr-std/src/sys/redox/rand.rs
@@ -0,0 +1,13 @@
+// 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.
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+ (0, 0)
+}
diff --git a/ctr-std/src/sys/redox/rwlock.rs b/ctr-std/src/sys/redox/rwlock.rs
new file mode 100644
index 0000000..d74b614
--- /dev/null
+++ b/ctr-std/src/sys/redox/rwlock.rs
@@ -0,0 +1,61 @@
+// 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 super::mutex::Mutex;
+
+pub struct RWLock {
+ mutex: Mutex
+}
+
+unsafe impl Send for RWLock {}
+unsafe impl Sync for RWLock {}
+
+impl RWLock {
+ pub const fn new() -> RWLock {
+ RWLock {
+ mutex: Mutex::new()
+ }
+ }
+
+ #[inline]
+ pub unsafe fn read(&self) {
+ self.mutex.lock();
+ }
+
+ #[inline]
+ pub unsafe fn try_read(&self) -> bool {
+ self.mutex.try_lock()
+ }
+
+ #[inline]
+ pub unsafe fn write(&self) {
+ self.mutex.lock();
+ }
+
+ #[inline]
+ pub unsafe fn try_write(&self) -> bool {
+ self.mutex.try_lock()
+ }
+
+ #[inline]
+ pub unsafe fn read_unlock(&self) {
+ self.mutex.unlock();
+ }
+
+ #[inline]
+ pub unsafe fn write_unlock(&self) {
+ self.mutex.unlock();
+ }
+
+ #[inline]
+ pub unsafe fn destroy(&self) {
+ self.mutex.destroy();
+ }
+}
diff --git a/ctr-std/src/sys/redox/stack_overflow.rs b/ctr-std/src/sys/redox/stack_overflow.rs
new file mode 100644
index 0000000..760fe06
--- /dev/null
+++ b/ctr-std/src/sys/redox/stack_overflow.rs
@@ -0,0 +1,27 @@
+// 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.
+
+#![cfg_attr(test, allow(dead_code))]
+
+pub struct Handler;
+
+impl Handler {
+ pub unsafe fn new() -> Handler {
+ Handler
+ }
+}
+
+pub unsafe fn init() {
+
+}
+
+pub unsafe fn cleanup() {
+
+}
diff --git a/ctr-std/src/sys/redox/stdio.rs b/ctr-std/src/sys/redox/stdio.rs
new file mode 100644
index 0000000..7a4d11b
--- /dev/null
+++ b/ctr-std/src/sys/redox/stdio.rs
@@ -0,0 +1,81 @@
+// 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 io;
+use sys::{cvt, syscall};
+use sys::fd::FileDesc;
+
+pub struct Stdin(());
+pub struct Stdout(());
+pub struct Stderr(());
+
+impl Stdin {
+ pub fn new() -> io::Result<Stdin> { Ok(Stdin(())) }
+
+ pub fn read(&self, data: &mut [u8]) -> io::Result<usize> {
+ let fd = FileDesc::new(0);
+ let ret = fd.read(data);
+ fd.into_raw();
+ ret
+ }
+}
+
+impl Stdout {
+ pub fn new() -> io::Result<Stdout> { Ok(Stdout(())) }
+
+ pub fn write(&self, data: &[u8]) -> io::Result<usize> {
+ let fd = FileDesc::new(1);
+ let ret = fd.write(data);
+ fd.into_raw();
+ ret
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ cvt(syscall::fsync(1)).and(Ok(()))
+ }
+}
+
+impl Stderr {
+ pub fn new() -> io::Result<Stderr> { Ok(Stderr(())) }
+
+ pub fn write(&self, data: &[u8]) -> io::Result<usize> {
+ let fd = FileDesc::new(2);
+ let ret = fd.write(data);
+ fd.into_raw();
+ ret
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ cvt(syscall::fsync(2)).and(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)
+ }
+}
+
+pub fn is_ebadf(err: &io::Error) -> bool {
+ err.raw_os_error() == Some(::sys::syscall::EBADF as i32)
+}
+
+pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE;
+
+pub fn stderr_prints_nothing() -> bool {
+ false
+}
diff --git a/ctr-std/src/sys/redox/syscall/arch/arm.rs b/ctr-std/src/sys/redox/syscall/arch/arm.rs
new file mode 100644
index 0000000..9fb3961
--- /dev/null
+++ b/ctr-std/src/sys/redox/syscall/arch/arm.rs
@@ -0,0 +1,83 @@
+// 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 super::error::{Error, Result};
+
+pub unsafe fn syscall0(mut a: usize) -> Result<usize> {
+ asm!("swi $$0"
+ : "={r0}"(a)
+ : "{r7}"(a)
+ : "memory"
+ : "volatile");
+
+ Error::demux(a)
+}
+
+pub unsafe fn syscall1(mut a: usize, b: usize) -> Result<usize> {
+ asm!("swi $$0"
+ : "={r0}"(a)
+ : "{r7}"(a), "{r0}"(b)
+ : "memory"
+ : "volatile");
+
+ Error::demux(a)
+}
+
+// Clobbers all registers - special for clone
+pub unsafe fn syscall1_clobber(mut a: usize, b: usize) -> Result<usize> {
+ asm!("swi $$0"
+ : "={r0}"(a)
+ : "{r7}"(a), "{r0}"(b)
+ : "memory", "r0", "r1", "r2", "r3", "r4"
+ : "volatile");
+
+ Error::demux(a)
+}
+
+pub unsafe fn syscall2(mut a: usize, b: usize, c: usize) -> Result<usize> {
+ asm!("swi $$0"
+ : "={r0}"(a)
+ : "{r7}"(a), "{r0}"(b), "{r1}"(c)
+ : "memory"
+ : "volatile");
+
+ Error::demux(a)
+}
+
+pub unsafe fn syscall3(mut a: usize, b: usize, c: usize, d: usize) -> Result<usize> {
+ asm!("swi $$0"
+ : "={r0}"(a)
+ : "{r7}"(a), "{r0}"(b), "{r1}"(c), "{r2}"(d)
+ : "memory"
+ : "volatile");
+
+ Error::demux(a)
+}
+
+pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result<usize> {
+ asm!("swi $$0"
+ : "={r0}"(a)
+ : "{r7}"(a), "{r0}"(b), "{r1}"(c), "{r2}"(d), "{r3}"(e)
+ : "memory"
+ : "volatile");
+
+ Error::demux(a)
+}
+
+pub unsafe fn syscall5(mut a: usize, b: usize, c: usize, d: usize, e: usize, f: usize)
+ -> Result<usize> {
+ asm!("swi $$0"
+ : "={r0}"(a)
+ : "{r7}"(a), "{r0}"(b), "{r1}"(c), "{r2}"(d), "{r3}"(e), "{r4}"(f)
+ : "memory"
+ : "volatile");
+
+ Error::demux(a)
+}
diff --git a/ctr-std/src/sys/redox/syscall/arch/x86.rs b/ctr-std/src/sys/redox/syscall/arch/x86.rs
new file mode 100644
index 0000000..724a6b9
--- /dev/null
+++ b/ctr-std/src/sys/redox/syscall/arch/x86.rs
@@ -0,0 +1,83 @@
+// 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 super::error::{Error, Result};
+
+pub unsafe fn syscall0(mut a: usize) -> Result<usize> {
+ asm!("int 0x80"
+ : "={eax}"(a)
+ : "{eax}"(a)
+ : "memory"
+ : "intel", "volatile");
+
+ Error::demux(a)
+}
+
+pub unsafe fn syscall1(mut a: usize, b: usize) -> Result<usize> {
+ asm!("int 0x80"
+ : "={eax}"(a)
+ : "{eax}"(a), "{ebx}"(b)
+ : "memory"
+ : "intel", "volatile");
+
+ Error::demux(a)
+}
+
+// Clobbers all registers - special for clone
+pub unsafe fn syscall1_clobber(mut a: usize, b: usize) -> Result<usize> {
+ asm!("int 0x80"
+ : "={eax}"(a)
+ : "{eax}"(a), "{ebx}"(b)
+ : "memory", "ebx", "ecx", "edx", "esi", "edi"
+ : "intel", "volatile");
+
+ Error::demux(a)
+}
+
+pub unsafe fn syscall2(mut a: usize, b: usize, c: usize) -> Result<usize> {
+ asm!("int 0x80"
+ : "={eax}"(a)
+ : "{eax}"(a), "{ebx}"(b), "{ecx}"(c)
+ : "memory"
+ : "intel", "volatile");
+
+ Error::demux(a)
+}
+
+pub unsafe fn syscall3(mut a: usize, b: usize, c: usize, d: usize) -> Result<usize> {
+ asm!("int 0x80"
+ : "={eax}"(a)
+ : "{eax}"(a), "{ebx}"(b), "{ecx}"(c), "{edx}"(d)
+ : "memory"
+ : "intel", "volatile");
+
+ Error::demux(a)
+}
+
+pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result<usize> {
+ asm!("int 0x80"
+ : "={eax}"(a)
+ : "{eax}"(a), "{ebx}"(b), "{ecx}"(c), "{edx}"(d), "{esi}"(e)
+ : "memory"
+ : "intel", "volatile");
+
+ Error::demux(a)
+}
+
+pub unsafe fn syscall5(mut a: usize, b: usize, c: usize, d: usize, e: usize, f: usize)
+ -> Result<usize> {
+ asm!("int 0x80"
+ : "={eax}"(a)
+ : "{eax}"(a), "{ebx}"(b), "{ecx}"(c), "{edx}"(d), "{esi}"(e), "{edi}"(f)
+ : "memory"
+ : "intel", "volatile");
+
+ Error::demux(a)
+}
diff --git a/ctr-std/src/sys/redox/syscall/arch/x86_64.rs b/ctr-std/src/sys/redox/syscall/arch/x86_64.rs
new file mode 100644
index 0000000..a321c31
--- /dev/null
+++ b/ctr-std/src/sys/redox/syscall/arch/x86_64.rs
@@ -0,0 +1,84 @@
+// 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 super::error::{Error, Result};
+
+pub unsafe fn syscall0(mut a: usize) -> Result<usize> {
+ asm!("int 0x80"
+ : "={rax}"(a)
+ : "{rax}"(a)
+ : "memory"
+ : "intel", "volatile");
+
+ Error::demux(a)
+}
+
+pub unsafe fn syscall1(mut a: usize, b: usize) -> Result<usize> {
+ asm!("int 0x80"
+ : "={rax}"(a)
+ : "{rax}"(a), "{rbx}"(b)
+ : "memory"
+ : "intel", "volatile");
+
+ Error::demux(a)
+}
+
+// Clobbers all registers - special for clone
+pub unsafe fn syscall1_clobber(mut a: usize, b: usize) -> Result<usize> {
+ asm!("int 0x80"
+ : "={rax}"(a)
+ : "{rax}"(a), "{rbx}"(b)
+ : "memory", "rbx", "rcx", "rdx", "rsi", "rdi", "r8",
+ "r9", "r10", "r11", "r12", "r13", "r14", "r15"
+ : "intel", "volatile");
+
+ Error::demux(a)
+}
+
+pub unsafe fn syscall2(mut a: usize, b: usize, c: usize) -> Result<usize> {
+ asm!("int 0x80"
+ : "={rax}"(a)
+ : "{rax}"(a), "{rbx}"(b), "{rcx}"(c)
+ : "memory"
+ : "intel", "volatile");
+
+ Error::demux(a)
+}
+
+pub unsafe fn syscall3(mut a: usize, b: usize, c: usize, d: usize) -> Result<usize> {
+ asm!("int 0x80"
+ : "={rax}"(a)
+ : "{rax}"(a), "{rbx}"(b), "{rcx}"(c), "{rdx}"(d)
+ : "memory"
+ : "intel", "volatile");
+
+ Error::demux(a)
+}
+
+pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result<usize> {
+ asm!("int 0x80"
+ : "={rax}"(a)
+ : "{rax}"(a), "{rbx}"(b), "{rcx}"(c), "{rdx}"(d), "{rsi}"(e)
+ : "memory"
+ : "intel", "volatile");
+
+ Error::demux(a)
+}
+
+pub unsafe fn syscall5(mut a: usize, b: usize, c: usize, d: usize, e: usize, f: usize)
+ -> Result<usize> {
+ asm!("int 0x80"
+ : "={rax}"(a)
+ : "{rax}"(a), "{rbx}"(b), "{rcx}"(c), "{rdx}"(d), "{rsi}"(e), "{rdi}"(f)
+ : "memory"
+ : "intel", "volatile");
+
+ Error::demux(a)
+}
diff --git a/ctr-std/src/sys/redox/syscall/call.rs b/ctr-std/src/sys/redox/syscall/call.rs
new file mode 100644
index 0000000..f9a8bd6
--- /dev/null
+++ b/ctr-std/src/sys/redox/syscall/call.rs
@@ -0,0 +1,363 @@
+// 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 super::arch::*;
+use super::data::{SigAction, Stat, StatVfs, TimeSpec};
+use super::error::Result;
+use super::number::*;
+
+use core::{mem, ptr};
+
+// Signal restorer
+extern "C" fn restorer() -> ! {
+ sigreturn().unwrap();
+ unreachable!();
+}
+
+/// Set the end of the process's heap
+///
+/// When `addr` is `0`, this function will return the current break.
+///
+/// When `addr` is nonzero, this function will attempt to set the end of the process's
+/// heap to `addr` and return the new program break. The new program break should be
+/// checked by the allocator, it may not be exactly `addr`, as it may be aligned to a page
+/// boundary.
+///
+/// On error, `Err(ENOMEM)` will be returned indicating that no memory is available
+pub unsafe fn brk(addr: usize) -> Result<usize> {
+ syscall1(SYS_BRK, addr)
+}
+
+/// Change the process's working directory
+///
+/// This function will attempt to set the process's working directory to `path`, which can be
+/// either a relative, scheme relative, or absolute path.
+///
+/// On success, `Ok(0)` will be returned. On error, one of the following errors will be returned.
+///
+/// # Errors
+///
+/// * `EACCES` - permission is denied for one of the components of `path`, or `path`
+/// * `EFAULT` - `path` does not point to the process's addressible memory
+/// * `EIO` - an I/O error occurred
+/// * `ENOENT` - `path` does not exit
+/// * `ENOTDIR` - `path` is not a directory
+pub fn chdir<T: AsRef<[u8]>>(path: T) -> Result<usize> {
+ unsafe { syscall2(SYS_CHDIR, path.as_ref().as_ptr() as usize, path.as_ref().len()) }
+}
+
+pub fn chmod<T: AsRef<[u8]>>(path: T, mode: usize) -> Result<usize> {
+ unsafe { syscall3(SYS_CHMOD, path.as_ref().as_ptr() as usize, path.as_ref().len(), mode) }
+}
+
+/// Produce a fork of the current process, or a new process thread
+pub unsafe fn clone(flags: usize) -> Result<usize> {
+ syscall1_clobber(SYS_CLONE, flags)
+}
+
+/// Close a file
+pub fn close(fd: usize) -> Result<usize> {
+ unsafe { syscall1(SYS_CLOSE, fd) }
+}
+
+/// Get the current system time
+pub fn clock_gettime(clock: usize, tp: &mut TimeSpec) -> Result<usize> {
+ unsafe { syscall2(SYS_CLOCK_GETTIME, clock, tp as *mut TimeSpec as usize) }
+}
+
+/// Copy and transform a file descriptor
+pub fn dup(fd: usize, buf: &[u8]) -> Result<usize> {
+ unsafe { syscall3(SYS_DUP, fd, buf.as_ptr() as usize, buf.len()) }
+}
+
+/// Copy and transform a file descriptor
+pub fn dup2(fd: usize, newfd: usize, buf: &[u8]) -> Result<usize> {
+ unsafe { syscall4(SYS_DUP2, fd, newfd, buf.as_ptr() as usize, buf.len()) }
+}
+
+/// Replace the current process with a new executable
+pub fn execve<T: AsRef<[u8]>>(path: T, args: &[[usize; 2]]) -> Result<usize> {
+ unsafe { syscall4(SYS_EXECVE, path.as_ref().as_ptr() as usize,
+ path.as_ref().len(), args.as_ptr() as usize, args.len()) }
+}
+
+/// Exit the current process
+pub fn exit(status: usize) -> Result<usize> {
+ unsafe { syscall1(SYS_EXIT, status) }
+}
+
+/// Change file permissions
+pub fn fchmod(fd: usize, mode: u16) -> Result<usize> {
+ unsafe { syscall2(SYS_FCHMOD, fd, mode as usize) }
+
+}
+
+/// Change file ownership
+pub fn fchown(fd: usize, uid: u32, gid: u32) -> Result<usize> {
+ unsafe { syscall3(SYS_FCHOWN, fd, uid as usize, gid as usize) }
+
+}
+
+/// Change file descriptor flags
+pub fn fcntl(fd: usize, cmd: usize, arg: usize) -> Result<usize> {
+ unsafe { syscall3(SYS_FCNTL, fd, cmd, arg) }
+}
+
+/// Register a file for event-based I/O
+pub fn fevent(fd: usize, flags: usize) -> Result<usize> {
+ unsafe { syscall2(SYS_FEVENT, fd, flags) }
+}
+
+/// Map a file into memory
+pub unsafe fn fmap(fd: usize, offset: usize, size: usize) -> Result<usize> {
+ syscall3(SYS_FMAP, fd, offset, size)
+}
+
+/// Unmap a memory-mapped file
+pub unsafe fn funmap(addr: usize) -> Result<usize> {
+ syscall1(SYS_FUNMAP, addr)
+}
+
+/// Retrieve the canonical path of a file
+pub fn fpath(fd: usize, buf: &mut [u8]) -> Result<usize> {
+ unsafe { syscall3(SYS_FPATH, fd, buf.as_mut_ptr() as usize, buf.len()) }
+}
+
+/// Rename a file
+pub fn frename<T: AsRef<[u8]>>(fd: usize, path: T) -> Result<usize> {
+ unsafe { syscall3(SYS_FRENAME, fd, path.as_ref().as_ptr() as usize, path.as_ref().len()) }
+}
+
+/// Get metadata about a file
+pub fn fstat(fd: usize, stat: &mut Stat) -> Result<usize> {
+ unsafe { syscall3(SYS_FSTAT, fd, stat as *mut Stat as usize, mem::size_of::<Stat>()) }
+}
+
+/// Get metadata about a filesystem
+pub fn fstatvfs(fd: usize, stat: &mut StatVfs) -> Result<usize> {
+ unsafe { syscall3(SYS_FSTATVFS, fd, stat as *mut StatVfs as usize, mem::size_of::<StatVfs>()) }
+}
+
+/// Sync a file descriptor to its underlying medium
+pub fn fsync(fd: usize) -> Result<usize> {
+ unsafe { syscall1(SYS_FSYNC, fd) }
+}
+
+/// Truncate or extend a file to a specified length
+pub fn ftruncate(fd: usize, len: usize) -> Result<usize> {
+ unsafe { syscall2(SYS_FTRUNCATE, fd, len) }
+}
+
+// Change modify and/or access times
+pub fn futimens(fd: usize, times: &[TimeSpec]) -> Result<usize> {
+ unsafe { syscall3(SYS_FUTIMENS, fd, times.as_ptr() as usize,
+ times.len() * mem::size_of::<TimeSpec>()) }
+}
+
+/// Fast userspace mutex
+pub unsafe fn futex(addr: *mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32)
+ -> Result<usize> {
+ syscall5(SYS_FUTEX, addr as usize, op, (val as isize) as usize, val2, addr2 as usize)
+}
+
+/// Get the current working directory
+pub fn getcwd(buf: &mut [u8]) -> Result<usize> {
+ unsafe { syscall2(SYS_GETCWD, buf.as_mut_ptr() as usize, buf.len()) }
+}
+
+/// Get the effective group ID
+pub fn getegid() -> Result<usize> {
+ unsafe { syscall0(SYS_GETEGID) }
+}
+
+/// Get the effective namespace
+pub fn getens() -> Result<usize> {
+ unsafe { syscall0(SYS_GETENS) }
+}
+
+/// Get the effective user ID
+pub fn geteuid() -> Result<usize> {
+ unsafe { syscall0(SYS_GETEUID) }
+}
+
+/// Get the current group ID
+pub fn getgid() -> Result<usize> {
+ unsafe { syscall0(SYS_GETGID) }
+}
+
+/// Get the current namespace
+pub fn getns() -> Result<usize> {
+ unsafe { syscall0(SYS_GETNS) }
+}
+
+/// Get the current process ID
+pub fn getpid() -> Result<usize> {
+ unsafe { syscall0(SYS_GETPID) }
+}
+
+/// Get the process group ID
+pub fn getpgid(pid: usize) -> Result<usize> {
+ unsafe { syscall1(SYS_GETPGID, pid) }
+}
+
+/// Get the parent process ID
+pub fn getppid() -> Result<usize> {
+ unsafe { syscall0(SYS_GETPPID) }
+}
+
+/// Get the current user ID
+pub fn getuid() -> Result<usize> {
+ unsafe { syscall0(SYS_GETUID) }
+}
+
+/// Set the I/O privilege level
+pub unsafe fn iopl(level: usize) -> Result<usize> {
+ syscall1(SYS_IOPL, level)
+}
+
+/// Send a signal `sig` to the process identified by `pid`
+pub fn kill(pid: usize, sig: usize) -> Result<usize> {
+ unsafe { syscall2(SYS_KILL, pid, sig) }
+}
+
+/// Create a link to a file
+pub unsafe fn link(old: *const u8, new: *const u8) -> Result<usize> {
+ syscall2(SYS_LINK, old as usize, new as usize)
+}
+
+/// Seek to `offset` bytes in a file descriptor
+pub fn lseek(fd: usize, offset: isize, whence: usize) -> Result<usize> {
+ unsafe { syscall3(SYS_LSEEK, fd, offset as usize, whence) }
+}
+
+/// Make a new scheme namespace
+pub fn mkns(schemes: &[[usize; 2]]) -> Result<usize> {
+ unsafe { syscall2(SYS_MKNS, schemes.as_ptr() as usize, schemes.len()) }
+}
+
+/// Sleep for the time specified in `req`
+pub fn nanosleep(req: &TimeSpec, rem: &mut TimeSpec) -> Result<usize> {
+ unsafe { syscall2(SYS_NANOSLEEP, req as *const TimeSpec as usize,
+ rem as *mut TimeSpec as usize) }
+}
+
+/// Open a file
+pub fn open<T: AsRef<[u8]>>(path: T, flags: usize) -> Result<usize> {
+ unsafe { syscall3(SYS_OPEN, path.as_ref().as_ptr() as usize, path.as_ref().len(), flags) }
+}
+
+/// Allocate pages, linearly in physical memory
+pub unsafe fn physalloc(size: usize) -> Result<usize> {
+ syscall1(SYS_PHYSALLOC, size)
+}
+
+/// Free physically allocated pages
+pub unsafe fn physfree(physical_address: usize, size: usize) -> Result<usize> {
+ syscall2(SYS_PHYSFREE, physical_address, size)
+}
+
+/// Map physical memory to virtual memory
+pub unsafe fn physmap(physical_address: usize, size: usize, flags: usize) -> Result<usize> {
+ syscall3(SYS_PHYSMAP, physical_address, size, flags)
+}
+
+/// Unmap previously mapped physical memory
+pub unsafe fn physunmap(virtual_address: usize) -> Result<usize> {
+ syscall1(SYS_PHYSUNMAP, virtual_address)
+}
+
+/// Create a pair of file descriptors referencing the read and write ends of a pipe
+pub fn pipe2(fds: &mut [usize; 2], flags: usize) -> Result<usize> {
+ unsafe { syscall2(SYS_PIPE2, fds.as_ptr() as usize, flags) }
+}
+
+/// Read from a file descriptor into a buffer
+pub fn read(fd: usize, buf: &mut [u8]) -> Result<usize> {
+ unsafe { syscall3(SYS_READ, fd, buf.as_mut_ptr() as usize, buf.len()) }
+}
+
+/// Remove a directory
+pub fn rmdir<T: AsRef<[u8]>>(path: T) -> Result<usize> {
+ unsafe { syscall2(SYS_RMDIR, path.as_ref().as_ptr() as usize, path.as_ref().len()) }
+}
+
+/// Set the process group ID
+pub fn setpgid(pid: usize, pgid: usize) -> Result<usize> {
+ unsafe { syscall2(SYS_SETPGID, pid, pgid) }
+}
+
+/// Set the current process group IDs
+pub fn setregid(rgid: usize, egid: usize) -> Result<usize> {
+ unsafe { syscall2(SYS_SETREGID, rgid, egid) }
+}
+
+/// Make a new scheme namespace
+pub fn setrens(rns: usize, ens: usize) -> Result<usize> {
+ unsafe { syscall2(SYS_SETRENS, rns, ens) }
+}
+
+/// Set the current process user IDs
+pub fn setreuid(ruid: usize, euid: usize) -> Result<usize> {
+ unsafe { syscall2(SYS_SETREUID, ruid, euid) }
+}
+
+/// Set up a signal handler
+pub fn sigaction(sig: usize, act: Option<&SigAction>, oldact: Option<&mut SigAction>)
+-> Result<usize> {
+ unsafe { syscall4(SYS_SIGACTION, sig,
+ act.map(|x| x as *const _).unwrap_or_else(ptr::null) as usize,
+ oldact.map(|x| x as *mut _).unwrap_or_else(ptr::null_mut) as usize,
+ restorer as usize) }
+}
+
+// Return from signal handler
+pub fn sigreturn() -> Result<usize> {
+ unsafe { syscall0(SYS_SIGRETURN) }
+}
+
+/// Remove a file
+pub fn unlink<T: AsRef<[u8]>>(path: T) -> Result<usize> {
+ unsafe { syscall2(SYS_UNLINK, path.as_ref().as_ptr() as usize, path.as_ref().len()) }
+}
+
+/// Convert a virtual address to a physical one
+pub unsafe fn virttophys(virtual_address: usize) -> Result<usize> {
+ syscall1(SYS_VIRTTOPHYS, virtual_address)
+}
+
+/// Check if a child process has exited or received a signal
+pub fn waitpid(pid: usize, status: &mut usize, options: usize) -> Result<usize> {
+ unsafe { syscall3(SYS_WAITPID, pid, status as *mut usize as usize, options) }
+}
+
+/// Write a buffer to a file descriptor
+///
+/// The kernel will attempt to write the bytes in `buf` to the file descriptor `fd`, returning
+/// either an `Err`, explained below, or `Ok(count)` where `count` is the number of bytes which
+/// were written.
+///
+/// # Errors
+///
+/// * `EAGAIN` - the file descriptor was opened with `O_NONBLOCK` and writing would block
+/// * `EBADF` - the file descriptor is not valid or is not open for writing
+/// * `EFAULT` - `buf` does not point to the process's addressible memory
+/// * `EIO` - an I/O error occurred
+/// * `ENOSPC` - the device containing the file descriptor has no room for data
+/// * `EPIPE` - the file descriptor refers to a pipe or socket whose reading end is closed
+pub fn write(fd: usize, buf: &[u8]) -> Result<usize> {
+ unsafe { syscall3(SYS_WRITE, fd, buf.as_ptr() as usize, buf.len()) }
+}
+
+/// Yield the process's time slice to the kernel
+///
+/// This function will return Ok(0) on success
+pub fn sched_yield() -> Result<usize> {
+ unsafe { syscall0(SYS_YIELD) }
+}
diff --git a/ctr-std/src/sys/redox/syscall/data.rs b/ctr-std/src/sys/redox/syscall/data.rs
new file mode 100644
index 0000000..2e784eb
--- /dev/null
+++ b/ctr-std/src/sys/redox/syscall/data.rs
@@ -0,0 +1,201 @@
+// 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 core::ops::{Deref, DerefMut};
+use core::{mem, slice};
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct Event {
+ pub id: usize,
+ pub flags: usize,
+ pub data: usize
+}
+
+impl Deref for Event {
+ type Target = [u8];
+ fn deref(&self) -> &[u8] {
+ unsafe {
+ slice::from_raw_parts(
+ self as *const Event as *const u8,
+ mem::size_of::<Event>()
+ ) as &[u8]
+ }
+ }
+}
+
+impl DerefMut for Event {
+ fn deref_mut(&mut self) -> &mut [u8] {
+ unsafe {
+ slice::from_raw_parts_mut(
+ self as *mut Event as *mut u8,
+ mem::size_of::<Event>()
+ ) as &mut [u8]
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+pub struct Packet {
+ pub id: u64,
+ pub pid: usize,
+ pub uid: u32,
+ pub gid: u32,
+ pub a: usize,
+ pub b: usize,
+ pub c: usize,
+ pub d: usize
+}
+
+impl Deref for Packet {
+ type Target = [u8];
+ fn deref(&self) -> &[u8] {
+ unsafe {
+ slice::from_raw_parts(
+ self as *const Packet as *const u8,
+ mem::size_of::<Packet>()
+ ) as &[u8]
+ }
+ }
+}
+
+impl DerefMut for Packet {
+ fn deref_mut(&mut self) -> &mut [u8] {
+ unsafe {
+ slice::from_raw_parts_mut(
+ self as *mut Packet as *mut u8,
+ mem::size_of::<Packet>()
+ ) as &mut [u8]
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct SigAction {
+ pub sa_handler: extern "C" fn(usize),
+ pub sa_mask: [u64; 2],
+ pub sa_flags: usize,
+}
+
+impl Default for SigAction {
+ fn default() -> Self {
+ Self {
+ sa_handler: unsafe { mem::transmute(0usize) },
+ sa_mask: [0; 2],
+ sa_flags: 0,
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+pub struct Stat {
+ pub st_dev: u64,
+ pub st_ino: u64,
+ pub st_mode: u16,
+ pub st_nlink: u32,
+ pub st_uid: u32,
+ pub st_gid: u32,
+ pub st_size: u64,
+ pub st_blksize: u32,
+ pub st_blocks: u64,
+ pub st_mtime: u64,
+ pub st_mtime_nsec: u32,
+ pub st_atime: u64,
+ pub st_atime_nsec: u32,
+ pub st_ctime: u64,
+ pub st_ctime_nsec: u32,
+}
+
+impl Deref for Stat {
+ type Target = [u8];
+ fn deref(&self) -> &[u8] {
+ unsafe {
+ slice::from_raw_parts(
+ self as *const Stat as *const u8,
+ mem::size_of::<Stat>()
+ ) as &[u8]
+ }
+ }
+}
+
+impl DerefMut for Stat {
+ fn deref_mut(&mut self) -> &mut [u8] {
+ unsafe {
+ slice::from_raw_parts_mut(
+ self as *mut Stat as *mut u8,
+ mem::size_of::<Stat>()
+ ) as &mut [u8]
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+pub struct StatVfs {
+ pub f_bsize: u32,
+ pub f_blocks: u64,
+ pub f_bfree: u64,
+ pub f_bavail: u64,
+}
+
+impl Deref for StatVfs {
+ type Target = [u8];
+ fn deref(&self) -> &[u8] {
+ unsafe {
+ slice::from_raw_parts(
+ self as *const StatVfs as *const u8,
+ mem::size_of::<StatVfs>()
+ ) as &[u8]
+ }
+ }
+}
+
+impl DerefMut for StatVfs {
+ fn deref_mut(&mut self) -> &mut [u8] {
+ unsafe {
+ slice::from_raw_parts_mut(
+ self as *mut StatVfs as *mut u8,
+ mem::size_of::<StatVfs>()
+ ) as &mut [u8]
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+pub struct TimeSpec {
+ pub tv_sec: i64,
+ pub tv_nsec: i32,
+}
+
+impl Deref for TimeSpec {
+ type Target = [u8];
+ fn deref(&self) -> &[u8] {
+ unsafe {
+ slice::from_raw_parts(
+ self as *const TimeSpec as *const u8,
+ mem::size_of::<TimeSpec>()
+ ) as &[u8]
+ }
+ }
+}
+
+impl DerefMut for TimeSpec {
+ fn deref_mut(&mut self) -> &mut [u8] {
+ unsafe {
+ slice::from_raw_parts_mut(
+ self as *mut TimeSpec as *mut u8,
+ mem::size_of::<TimeSpec>()
+ ) as &mut [u8]
+ }
+ }
+}
diff --git a/ctr-std/src/sys/redox/syscall/error.rs b/ctr-std/src/sys/redox/syscall/error.rs
new file mode 100644
index 0000000..1ef7954
--- /dev/null
+++ b/ctr-std/src/sys/redox/syscall/error.rs
@@ -0,0 +1,325 @@
+// 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 core::{fmt, result};
+
+#[derive(Eq, PartialEq)]
+pub struct Error {
+ pub errno: i32,
+}
+
+pub type Result<T> = result::Result<T, Error>;
+
+impl Error {
+ pub fn new(errno: i32) -> Error {
+ Error { errno: errno }
+ }
+
+ pub fn mux(result: Result<usize>) -> usize {
+ match result {
+ Ok(value) => value,
+ Err(error) => -error.errno as usize,
+ }
+ }
+
+ pub fn demux(value: usize) -> Result<usize> {
+ let errno = -(value as i32);
+ if errno >= 1 && errno < STR_ERROR.len() as i32 {
+ Err(Error::new(errno))
+ } else {
+ Ok(value)
+ }
+ }
+
+ pub fn text(&self) -> &str {
+ if let Some(description) = STR_ERROR.get(self.errno as usize) {
+ description
+ } else {
+ "Unknown Error"
+ }
+ }
+}
+
+impl fmt::Debug for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(self.text())
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(self.text())
+ }
+}
+
+pub const EPERM: i32 = 1; /* Operation not permitted */
+pub const ENOENT: i32 = 2; /* No such file or directory */
+pub const ESRCH: i32 = 3; /* No such process */
+pub const EINTR: i32 = 4; /* Interrupted system call */
+pub const EIO: i32 = 5; /* I/O error */
+pub const ENXIO: i32 = 6; /* No such device or address */
+pub const E2BIG: i32 = 7; /* Argument list too long */
+pub const ENOEXEC: i32 = 8; /* Exec format error */
+pub const EBADF: i32 = 9; /* Bad file number */
+pub const ECHILD: i32 = 10; /* No child processes */
+pub const EAGAIN: i32 = 11; /* Try again */
+pub const ENOMEM: i32 = 12; /* Out of memory */
+pub const EACCES: i32 = 13; /* Permission denied */
+pub const EFAULT: i32 = 14; /* Bad address */
+pub const ENOTBLK: i32 = 15; /* Block device required */
+pub const EBUSY: i32 = 16; /* Device or resource busy */
+pub const EEXIST: i32 = 17; /* File exists */
+pub const EXDEV: i32 = 18; /* Cross-device link */
+pub const ENODEV: i32 = 19; /* No such device */
+pub const ENOTDIR: i32 = 20; /* Not a directory */
+pub const EISDIR: i32 = 21; /* Is a directory */
+pub const EINVAL: i32 = 22; /* Invalid argument */
+pub const ENFILE: i32 = 23; /* File table overflow */
+pub const EMFILE: i32 = 24; /* Too many open files */
+pub const ENOTTY: i32 = 25; /* Not a typewriter */
+pub const ETXTBSY: i32 = 26; /* Text file busy */
+pub const EFBIG: i32 = 27; /* File too large */
+pub const ENOSPC: i32 = 28; /* No space left on device */
+pub const ESPIPE: i32 = 29; /* Illegal seek */
+pub const EROFS: i32 = 30; /* Read-only file system */
+pub const EMLINK: i32 = 31; /* Too many links */
+pub const EPIPE: i32 = 32; /* Broken pipe */
+pub const EDOM: i32 = 33; /* Math argument out of domain of func */
+pub const ERANGE: i32 = 34; /* Math result not representable */
+pub const EDEADLK: i32 = 35; /* Resource deadlock would occur */
+pub const ENAMETOOLONG: i32 = 36; /* File name too long */
+pub const ENOLCK: i32 = 37; /* No record locks available */
+pub const ENOSYS: i32 = 38; /* Function not implemented */
+pub const ENOTEMPTY: i32 = 39; /* Directory not empty */
+pub const ELOOP: i32 = 40; /* Too many symbolic links encountered */
+pub const EWOULDBLOCK: i32 = 41; /* Operation would block */
+pub const ENOMSG: i32 = 42; /* No message of desired type */
+pub const EIDRM: i32 = 43; /* Identifier removed */
+pub const ECHRNG: i32 = 44; /* Channel number out of range */
+pub const EL2NSYNC: i32 = 45; /* Level 2 not synchronized */
+pub const EL3HLT: i32 = 46; /* Level 3 halted */
+pub const EL3RST: i32 = 47; /* Level 3 reset */
+pub const ELNRNG: i32 = 48; /* Link number out of range */
+pub const EUNATCH: i32 = 49; /* Protocol driver not attached */
+pub const ENOCSI: i32 = 50; /* No CSI structure available */
+pub const EL2HLT: i32 = 51; /* Level 2 halted */
+pub const EBADE: i32 = 52; /* Invalid exchange */
+pub const EBADR: i32 = 53; /* Invalid request descriptor */
+pub const EXFULL: i32 = 54; /* Exchange full */
+pub const ENOANO: i32 = 55; /* No anode */
+pub const EBADRQC: i32 = 56; /* Invalid request code */
+pub const EBADSLT: i32 = 57; /* Invalid slot */
+pub const EDEADLOCK: i32 = 58; /* Resource deadlock would occur */
+pub const EBFONT: i32 = 59; /* Bad font file format */
+pub const ENOSTR: i32 = 60; /* Device not a stream */
+pub const ENODATA: i32 = 61; /* No data available */
+pub const ETIME: i32 = 62; /* Timer expired */
+pub const ENOSR: i32 = 63; /* Out of streams resources */
+pub const ENONET: i32 = 64; /* Machine is not on the network */
+pub const ENOPKG: i32 = 65; /* Package not installed */
+pub const EREMOTE: i32 = 66; /* Object is remote */
+pub const ENOLINK: i32 = 67; /* Link has been severed */
+pub const EADV: i32 = 68; /* Advertise error */
+pub const ESRMNT: i32 = 69; /* Srmount error */
+pub const ECOMM: i32 = 70; /* Communication error on send */
+pub const EPROTO: i32 = 71; /* Protocol error */
+pub const EMULTIHOP: i32 = 72; /* Multihop attempted */
+pub const EDOTDOT: i32 = 73; /* RFS specific error */
+pub const EBADMSG: i32 = 74; /* Not a data message */
+pub const EOVERFLOW: i32 = 75; /* Value too large for defined data type */
+pub const ENOTUNIQ: i32 = 76; /* Name not unique on network */
+pub const EBADFD: i32 = 77; /* File descriptor in bad state */
+pub const EREMCHG: i32 = 78; /* Remote address changed */
+pub const ELIBACC: i32 = 79; /* Can not access a needed shared library */
+pub const ELIBBAD: i32 = 80; /* Accessing a corrupted shared library */
+pub const ELIBSCN: i32 = 81; /* .lib section in a.out corrupted */
+pub const ELIBMAX: i32 = 82; /* Attempting to link in too many shared libraries */
+pub const ELIBEXEC: i32 = 83; /* Cannot exec a shared library directly */
+pub const EILSEQ: i32 = 84; /* Illegal byte sequence */
+pub const ERESTART: i32 = 85; /* Interrupted system call should be restarted */
+pub const ESTRPIPE: i32 = 86; /* Streams pipe error */
+pub const EUSERS: i32 = 87; /* Too many users */
+pub const ENOTSOCK: i32 = 88; /* Socket operation on non-socket */
+pub const EDESTADDRREQ: i32 = 89; /* Destination address required */
+pub const EMSGSIZE: i32 = 90; /* Message too long */
+pub const EPROTOTYPE: i32 = 91; /* Protocol wrong type for socket */
+pub const ENOPROTOOPT: i32 = 92; /* Protocol not available */
+pub const EPROTONOSUPPORT: i32 = 93; /* Protocol not supported */
+pub const ESOCKTNOSUPPORT: i32 = 94; /* Socket type not supported */
+pub const EOPNOTSUPP: i32 = 95; /* Operation not supported on transport endpoint */
+pub const EPFNOSUPPORT: i32 = 96; /* Protocol family not supported */
+pub const EAFNOSUPPORT: i32 = 97; /* Address family not supported by protocol */
+pub const EADDRINUSE: i32 = 98; /* Address already in use */
+pub const EADDRNOTAVAIL: i32 = 99; /* Cannot assign requested address */
+pub const ENETDOWN: i32 = 100; /* Network is down */
+pub const ENETUNREACH: i32 = 101; /* Network is unreachable */
+pub const ENETRESET: i32 = 102; /* Network dropped connection because of reset */
+pub const ECONNABORTED: i32 = 103; /* Software caused connection abort */
+pub const ECONNRESET: i32 = 104; /* Connection reset by peer */
+pub const ENOBUFS: i32 = 105; /* No buffer space available */
+pub const EISCONN: i32 = 106; /* Transport endpoint is already connected */
+pub const ENOTCONN: i32 = 107; /* Transport endpoint is not connected */
+pub const ESHUTDOWN: i32 = 108; /* Cannot send after transport endpoint shutdown */
+pub const ETOOMANYREFS: i32 = 109; /* Too many references: cannot splice */
+pub const ETIMEDOUT: i32 = 110; /* Connection timed out */
+pub const ECONNREFUSED: i32 = 111; /* Connection refused */
+pub const EHOSTDOWN: i32 = 112; /* Host is down */
+pub const EHOSTUNREACH: i32 = 113; /* No route to host */
+pub const EALREADY: i32 = 114; /* Operation already in progress */
+pub const EINPROGRESS: i32 = 115; /* Operation now in progress */
+pub const ESTALE: i32 = 116; /* Stale NFS file handle */
+pub const EUCLEAN: i32 = 117; /* Structure needs cleaning */
+pub const ENOTNAM: i32 = 118; /* Not a XENIX named type file */
+pub const ENAVAIL: i32 = 119; /* No XENIX semaphores available */
+pub const EISNAM: i32 = 120; /* Is a named type file */
+pub const EREMOTEIO: i32 = 121; /* Remote I/O error */
+pub const EDQUOT: i32 = 122; /* Quota exceeded */
+pub const ENOMEDIUM: i32 = 123; /* No medium found */
+pub const EMEDIUMTYPE: i32 = 124; /* Wrong medium type */
+pub const ECANCELED: i32 = 125; /* Operation Canceled */
+pub const ENOKEY: i32 = 126; /* Required key not available */
+pub const EKEYEXPIRED: i32 = 127; /* Key has expired */
+pub const EKEYREVOKED: i32 = 128; /* Key has been revoked */
+pub const EKEYREJECTED: i32 = 129; /* Key was rejected by service */
+pub const EOWNERDEAD: i32 = 130; /* Owner died */
+pub const ENOTRECOVERABLE: i32 = 131; /* State not recoverable */
+
+pub static STR_ERROR: [&'static str; 132] = ["Success",
+ "Operation not permitted",
+ "No such file or directory",
+ "No such process",
+ "Interrupted system call",
+ "I/O error",
+ "No such device or address",
+ "Argument list too long",
+ "Exec format error",
+ "Bad file number",
+ "No child processes",
+ "Try again",
+ "Out of memory",
+ "Permission denied",
+ "Bad address",
+ "Block device required",
+ "Device or resource busy",
+ "File exists",
+ "Cross-device link",
+ "No such device",
+ "Not a directory",
+ "Is a directory",
+ "Invalid argument",
+ "File table overflow",
+ "Too many open files",
+ "Not a typewriter",
+ "Text file busy",
+ "File too large",
+ "No space left on device",
+ "Illegal seek",
+ "Read-only file system",
+ "Too many links",
+ "Broken pipe",
+ "Math argument out of domain of func",
+ "Math result not representable",
+ "Resource deadlock would occur",
+ "File name too long",
+ "No record locks available",
+ "Function not implemented",
+ "Directory not empty",
+ "Too many symbolic links encountered",
+ "Operation would block",
+ "No message of desired type",
+ "Identifier removed",
+ "Channel number out of range",
+ "Level 2 not synchronized",
+ "Level 3 halted",
+ "Level 3 reset",
+ "Link number out of range",
+ "Protocol driver not attached",
+ "No CSI structure available",
+ "Level 2 halted",
+ "Invalid exchange",
+ "Invalid request descriptor",
+ "Exchange full",
+ "No anode",
+ "Invalid request code",
+ "Invalid slot",
+ "Resource deadlock would occur",
+ "Bad font file format",
+ "Device not a stream",
+ "No data available",
+ "Timer expired",
+ "Out of streams resources",
+ "Machine is not on the network",
+ "Package not installed",
+ "Object is remote",
+ "Link has been severed",
+ "Advertise error",
+ "Srmount error",
+ "Communication error on send",
+ "Protocol error",
+ "Multihop attempted",
+ "RFS specific error",
+ "Not a data message",
+ "Value too large for defined data type",
+ "Name not unique on network",
+ "File descriptor in bad state",
+ "Remote address changed",
+ "Can not access a needed shared library",
+ "Accessing a corrupted shared library",
+ ".lib section in a.out corrupted",
+ "Attempting to link in too many shared libraries",
+ "Cannot exec a shared library directly",
+ "Illegal byte sequence",
+ "Interrupted system call should be restarted",
+ "Streams pipe error",
+ "Too many users",
+ "Socket operation on non-socket",
+ "Destination address required",
+ "Message too long",
+ "Protocol wrong type for socket",
+ "Protocol not available",
+ "Protocol not supported",
+ "Socket type not supported",
+ "Operation not supported on transport endpoint",
+ "Protocol family not supported",
+ "Address family not supported by protocol",
+ "Address already in use",
+ "Cannot assign requested address",
+ "Network is down",
+ "Network is unreachable",
+ "Network dropped connection because of reset",
+ "Software caused connection abort",
+ "Connection reset by peer",
+ "No buffer space available",
+ "Transport endpoint is already connected",
+ "Transport endpoint is not connected",
+ "Cannot send after transport endpoint shutdown",
+ "Too many references: cannot splice",
+ "Connection timed out",
+ "Connection refused",
+ "Host is down",
+ "No route to host",
+ "Operation already in progress",
+ "Operation now in progress",
+ "Stale NFS file handle",
+ "Structure needs cleaning",
+ "Not a XENIX named type file",
+ "No XENIX semaphores available",
+ "Is a named type file",
+ "Remote I/O error",
+ "Quota exceeded",
+ "No medium found",
+ "Wrong medium type",
+ "Operation Canceled",
+ "Required key not available",
+ "Key has expired",
+ "Key has been revoked",
+ "Key was rejected by service",
+ "Owner died",
+ "State not recoverable"];
diff --git a/ctr-std/src/sys/redox/syscall/flag.rs b/ctr-std/src/sys/redox/syscall/flag.rs
new file mode 100644
index 0000000..0f61b9f
--- /dev/null
+++ b/ctr-std/src/sys/redox/syscall/flag.rs
@@ -0,0 +1,116 @@
+// 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.
+
+pub const CLONE_VM: usize = 0x100;
+pub const CLONE_FS: usize = 0x200;
+pub const CLONE_FILES: usize = 0x400;
+pub const CLONE_SIGHAND: usize = 0x800;
+pub const CLONE_VFORK: usize = 0x4000;
+pub const CLONE_THREAD: usize = 0x10000;
+
+pub const CLOCK_REALTIME: usize = 1;
+pub const CLOCK_MONOTONIC: usize = 4;
+
+pub const EVENT_NONE: usize = 0;
+pub const EVENT_READ: usize = 1;
+pub const EVENT_WRITE: usize = 2;
+
+pub const F_DUPFD: usize = 0;
+pub const F_GETFD: usize = 1;
+pub const F_SETFD: usize = 2;
+pub const F_GETFL: usize = 3;
+pub const F_SETFL: usize = 4;
+
+pub const FUTEX_WAIT: usize = 0;
+pub const FUTEX_WAKE: usize = 1;
+pub const FUTEX_REQUEUE: usize = 2;
+
+pub const MAP_WRITE: usize = 1;
+pub const MAP_WRITE_COMBINE: usize = 2;
+
+pub const MODE_TYPE: u16 = 0xF000;
+pub const MODE_DIR: u16 = 0x4000;
+pub const MODE_FILE: u16 = 0x8000;
+pub const MODE_SYMLINK: u16 = 0xA000;
+pub const MODE_FIFO: u16 = 0x1000;
+pub const MODE_CHR: u16 = 0x2000;
+
+pub const MODE_PERM: u16 = 0x0FFF;
+pub const MODE_SETUID: u16 = 0o4000;
+pub const MODE_SETGID: u16 = 0o2000;
+
+pub const O_RDONLY: usize = 0x0001_0000;
+pub const O_WRONLY: usize = 0x0002_0000;
+pub const O_RDWR: usize = 0x0003_0000;
+pub const O_NONBLOCK: usize = 0x0004_0000;
+pub const O_APPEND: usize = 0x0008_0000;
+pub const O_SHLOCK: usize = 0x0010_0000;
+pub const O_EXLOCK: usize = 0x0020_0000;
+pub const O_ASYNC: usize = 0x0040_0000;
+pub const O_FSYNC: usize = 0x0080_0000;
+pub const O_CLOEXEC: usize = 0x0100_0000;
+pub const O_CREAT: usize = 0x0200_0000;
+pub const O_TRUNC: usize = 0x0400_0000;
+pub const O_EXCL: usize = 0x0800_0000;
+pub const O_DIRECTORY: usize = 0x1000_0000;
+pub const O_STAT: usize = 0x2000_0000;
+pub const O_SYMLINK: usize = 0x4000_0000;
+pub const O_NOFOLLOW: usize = 0x8000_0000;
+pub const O_ACCMODE: usize = O_RDONLY | O_WRONLY | O_RDWR;
+
+pub const SEEK_SET: usize = 0;
+pub const SEEK_CUR: usize = 1;
+pub const SEEK_END: usize = 2;
+
+pub const SIGHUP: usize = 1;
+pub const SIGINT: usize = 2;
+pub const SIGQUIT: usize = 3;
+pub const SIGILL: usize = 4;
+pub const SIGTRAP: usize = 5;
+pub const SIGABRT: usize = 6;
+pub const SIGBUS: usize = 7;
+pub const SIGFPE: usize = 8;
+pub const SIGKILL: usize = 9;
+pub const SIGUSR1: usize = 10;
+pub const SIGSEGV: usize = 11;
+pub const SIGUSR2: usize = 12;
+pub const SIGPIPE: usize = 13;
+pub const SIGALRM: usize = 14;
+pub const SIGTERM: usize = 15;
+pub const SIGSTKFLT: usize= 16;
+pub const SIGCHLD: usize = 17;
+pub const SIGCONT: usize = 18;
+pub const SIGSTOP: usize = 19;
+pub const SIGTSTP: usize = 20;
+pub const SIGTTIN: usize = 21;
+pub const SIGTTOU: usize = 22;
+pub const SIGURG: usize = 23;
+pub const SIGXCPU: usize = 24;
+pub const SIGXFSZ: usize = 25;
+pub const SIGVTALRM: usize= 26;
+pub const SIGPROF: usize = 27;
+pub const SIGWINCH: usize = 28;
+pub const SIGIO: usize = 29;
+pub const SIGPWR: usize = 30;
+pub const SIGSYS: usize = 31;
+
+pub const SIG_DFL: usize = 0;
+pub const SIG_IGN: usize = 1;
+
+pub const SA_NOCLDSTOP: usize = 0x00000001;
+pub const SA_NOCLDWAIT: usize = 0x00000002;
+pub const SA_SIGINFO: usize = 0x00000004;
+pub const SA_RESTORER: usize = 0x04000000;
+pub const SA_ONSTACK: usize = 0x08000000;
+pub const SA_RESTART: usize = 0x10000000;
+pub const SA_NODEFER: usize = 0x40000000;
+pub const SA_RESETHAND: usize = 0x80000000;
+
+pub const WNOHANG: usize = 1;
diff --git a/ctr-std/src/sys/redox/syscall/mod.rs b/ctr-std/src/sys/redox/syscall/mod.rs
new file mode 100644
index 0000000..ce789c2
--- /dev/null
+++ b/ctr-std/src/sys/redox/syscall/mod.rs
@@ -0,0 +1,43 @@
+// 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.
+
+pub use self::arch::*;
+pub use self::call::*;
+pub use self::data::*;
+pub use self::error::*;
+pub use self::flag::*;
+pub use self::number::*;
+
+#[cfg(target_arch = "arm")]
+#[path="arch/arm.rs"]
+mod arch;
+
+#[cfg(target_arch = "x86")]
+#[path="arch/x86.rs"]
+mod arch;
+
+#[cfg(target_arch = "x86_64")]
+#[path="arch/x86_64.rs"]
+mod arch;
+
+/// Function definitions
+pub mod call;
+
+/// Complex structures that are used for some system calls
+pub mod data;
+
+/// All errors that can be generated by a system call
+pub mod error;
+
+/// Flags used as an argument to many system calls
+pub mod flag;
+
+/// Call numbers used by each system call
+pub mod number;
diff --git a/ctr-std/src/sys/redox/syscall/number.rs b/ctr-std/src/sys/redox/syscall/number.rs
new file mode 100644
index 0000000..45cb40e
--- /dev/null
+++ b/ctr-std/src/sys/redox/syscall/number.rs
@@ -0,0 +1,83 @@
+// 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.
+
+pub const SYS_CLASS: usize = 0xF000_0000;
+pub const SYS_CLASS_PATH: usize=0x1000_0000;
+pub const SYS_CLASS_FILE: usize=0x2000_0000;
+
+pub const SYS_ARG: usize = 0x0F00_0000;
+pub const SYS_ARG_SLICE: usize =0x0100_0000;
+pub const SYS_ARG_MSLICE: usize=0x0200_0000;
+pub const SYS_ARG_PATH: usize = 0x0300_0000;
+
+pub const SYS_RET: usize = 0x00F0_0000;
+pub const SYS_RET_FILE: usize = 0x0010_0000;
+
+pub const SYS_LINK: usize = SYS_CLASS_PATH | SYS_ARG_PATH | 9;
+pub const SYS_OPEN: usize = SYS_CLASS_PATH | SYS_RET_FILE | 5;
+pub const SYS_CHMOD: usize = SYS_CLASS_PATH | 15;
+pub const SYS_RMDIR: usize = SYS_CLASS_PATH | 84;
+pub const SYS_UNLINK: usize = SYS_CLASS_PATH | 10;
+
+pub const SYS_CLOSE: usize = SYS_CLASS_FILE | 6;
+pub const SYS_DUP: usize = SYS_CLASS_FILE | SYS_RET_FILE | 41;
+pub const SYS_DUP2: usize = SYS_CLASS_FILE | SYS_RET_FILE | 63;
+pub const SYS_READ: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 3;
+pub const SYS_WRITE: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 4;
+pub const SYS_LSEEK: usize = SYS_CLASS_FILE | 19;
+pub const SYS_FCHMOD: usize = SYS_CLASS_FILE | 94;
+pub const SYS_FCHOWN: usize = SYS_CLASS_FILE | 207;
+pub const SYS_FCNTL: usize = SYS_CLASS_FILE | 55;
+pub const SYS_FEVENT: usize = SYS_CLASS_FILE | 927;
+pub const SYS_FMAP: usize = SYS_CLASS_FILE | 90;
+pub const SYS_FUNMAP: usize = SYS_CLASS_FILE | 91;
+pub const SYS_FPATH: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 928;
+pub const SYS_FRENAME: usize = SYS_CLASS_FILE | SYS_ARG_PATH | 38;
+pub const SYS_FSTAT: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 28;
+pub const SYS_FSTATVFS: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 100;
+pub const SYS_FSYNC: usize = SYS_CLASS_FILE | 118;
+pub const SYS_FTRUNCATE: usize =SYS_CLASS_FILE | 93;
+pub const SYS_FUTIMENS: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 320;
+
+pub const SYS_BRK: usize = 45;
+pub const SYS_CHDIR: usize = 12;
+pub const SYS_CLOCK_GETTIME: usize = 265;
+pub const SYS_CLONE: usize = 120;
+pub const SYS_EXECVE: usize = 11;
+pub const SYS_EXIT: usize = 1;
+pub const SYS_FUTEX: usize = 240;
+pub const SYS_GETCWD: usize = 183;
+pub const SYS_GETEGID: usize = 202;
+pub const SYS_GETENS: usize = 951;
+pub const SYS_GETEUID: usize = 201;
+pub const SYS_GETGID: usize = 200;
+pub const SYS_GETNS: usize = 950;
+pub const SYS_GETPID: usize = 20;
+pub const SYS_GETPGID: usize = 132;
+pub const SYS_GETPPID: usize = 64;
+pub const SYS_GETUID: usize = 199;
+pub const SYS_IOPL: usize = 110;
+pub const SYS_KILL: usize = 37;
+pub const SYS_MKNS: usize = 984;
+pub const SYS_NANOSLEEP: usize =162;
+pub const SYS_PHYSALLOC: usize =945;
+pub const SYS_PHYSFREE: usize = 946;
+pub const SYS_PHYSMAP: usize = 947;
+pub const SYS_PHYSUNMAP: usize =948;
+pub const SYS_VIRTTOPHYS: usize=949;
+pub const SYS_PIPE2: usize = 331;
+pub const SYS_SETPGID: usize = 57;
+pub const SYS_SETREGID: usize = 204;
+pub const SYS_SETRENS: usize = 952;
+pub const SYS_SETREUID: usize = 203;
+pub const SYS_SIGACTION: usize =67;
+pub const SYS_SIGRETURN: usize =119;
+pub const SYS_WAITPID: usize = 7;
+pub const SYS_YIELD: usize = 158;
diff --git a/ctr-std/src/sys/redox/thread.rs b/ctr-std/src/sys/redox/thread.rs
new file mode 100644
index 0000000..110d46c
--- /dev/null
+++ b/ctr-std/src/sys/redox/thread.rs
@@ -0,0 +1,95 @@
+// 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 boxed::FnBox;
+use ffi::CStr;
+use io;
+use mem;
+use sys_common::thread::start_thread;
+use sys::{cvt, syscall};
+use time::Duration;
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
+
+pub struct Thread {
+ id: usize,
+}
+
+// Some platforms may have pthread_t as a pointer in which case we still want
+// a thread to be Send/Sync
+unsafe impl Send for Thread {}
+unsafe impl Sync for Thread {}
+
+impl Thread {
+ pub unsafe fn new<'a>(_stack: usize, p: Box<FnBox() + 'a>) -> io::Result<Thread> {
+ let p = box p;
+
+ let id = cvt(syscall::clone(syscall::CLONE_VM | syscall::CLONE_FS | syscall::CLONE_FILES))?;
+ if id == 0 {
+ start_thread(&*p as *const _ as *mut _);
+ let _ = syscall::exit(0);
+ panic!("thread failed to exit");
+ } else {
+ mem::forget(p);
+ Ok(Thread { id: id })
+ }
+ }
+
+ pub fn yield_now() {
+ let ret = syscall::sched_yield().expect("failed to sched_yield");
+ debug_assert_eq!(ret, 0);
+ }
+
+ pub fn set_name(_name: &CStr) {
+
+ }
+
+ pub fn sleep(dur: Duration) {
+ let mut secs = dur.as_secs();
+ let mut nsecs = dur.subsec_nanos() as i32;
+
+ // If we're awoken with a signal then the return value will be -1 and
+ // nanosleep will fill in `ts` with the remaining time.
+ while secs > 0 || nsecs > 0 {
+ let req = syscall::TimeSpec {
+ tv_sec: secs as i64,
+ tv_nsec: nsecs,
+ };
+ secs -= req.tv_sec as u64;
+ let mut rem = syscall::TimeSpec::default();
+ if syscall::nanosleep(&req, &mut rem).is_err() {
+ secs += rem.tv_sec as u64;
+ nsecs = rem.tv_nsec;
+ } else {
+ nsecs = 0;
+ }
+ }
+ }
+
+ pub fn join(self) {
+ let mut status = 0;
+ syscall::waitpid(self.id, &mut status, 0).unwrap();
+ }
+
+ pub fn id(&self) -> usize { self.id }
+
+ pub fn into_id(self) -> usize {
+ let id = self.id;
+ mem::forget(self);
+ id
+ }
+}
+
+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/redox/thread_local.rs b/ctr-std/src/sys/redox/thread_local.rs
new file mode 100644
index 0000000..cacd84e
--- /dev/null
+++ b/ctr-std/src/sys/redox/thread_local.rs
@@ -0,0 +1,71 @@
+// 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.
+
+#![allow(dead_code)] // not used on all platforms
+
+use collections::BTreeMap;
+use ptr;
+use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
+
+pub type Key = usize;
+
+type Dtor = unsafe extern fn(*mut u8);
+
+static NEXT_KEY: AtomicUsize = ATOMIC_USIZE_INIT;
+
+static mut KEYS: *mut BTreeMap<Key, Option<Dtor>> = ptr::null_mut();
+
+#[thread_local]
+static mut LOCALS: *mut BTreeMap<Key, *mut u8> = ptr::null_mut();
+
+unsafe fn keys() -> &'static mut BTreeMap<Key, Option<Dtor>> {
+ if KEYS == ptr::null_mut() {
+ KEYS = Box::into_raw(Box::new(BTreeMap::new()));
+ }
+ &mut *KEYS
+}
+
+unsafe fn locals() -> &'static mut BTreeMap<Key, *mut u8> {
+ if LOCALS == ptr::null_mut() {
+ LOCALS = Box::into_raw(Box::new(BTreeMap::new()));
+ }
+ &mut *LOCALS
+}
+
+#[inline]
+pub unsafe fn create(dtor: Option<Dtor>) -> Key {
+ let key = NEXT_KEY.fetch_add(1, Ordering::SeqCst);
+ keys().insert(key, dtor);
+ key
+}
+
+#[inline]
+pub unsafe fn get(key: Key) -> *mut u8 {
+ if let Some(&entry) = locals().get(&key) {
+ entry
+ } else {
+ ptr::null_mut()
+ }
+}
+
+#[inline]
+pub unsafe fn set(key: Key, value: *mut u8) {
+ locals().insert(key, value);
+}
+
+#[inline]
+pub unsafe fn destroy(key: Key) {
+ keys().remove(&key);
+}
+
+#[inline]
+pub fn requires_synchronized_create() -> bool {
+ false
+}
diff --git a/ctr-std/src/sys/redox/time.rs b/ctr-std/src/sys/redox/time.rs
new file mode 100644
index 0000000..5c49111
--- /dev/null
+++ b/ctr-std/src/sys/redox/time.rs
@@ -0,0 +1,214 @@
+// 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 cmp::Ordering;
+use fmt;
+use sys::{cvt, syscall};
+use time::Duration;
+use convert::TryInto;
+use core::hash::{Hash, Hasher};
+
+const NSEC_PER_SEC: u64 = 1_000_000_000;
+
+#[derive(Copy, Clone)]
+struct Timespec {
+ t: syscall::TimeSpec,
+}
+
+impl Timespec {
+ fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
+ if self >= other {
+ Ok(if self.t.tv_nsec >= other.t.tv_nsec {
+ Duration::new((self.t.tv_sec - other.t.tv_sec) as u64,
+ (self.t.tv_nsec - other.t.tv_nsec) as u32)
+ } else {
+ Duration::new((self.t.tv_sec - 1 - other.t.tv_sec) as u64,
+ self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) -
+ other.t.tv_nsec as u32)
+ })
+ } else {
+ match other.sub_timespec(self) {
+ Ok(d) => Err(d),
+ Err(d) => Ok(d),
+ }
+ }
+ }
+
+ fn add_duration(&self, other: &Duration) -> Timespec {
+ let mut secs = other
+ .as_secs()
+ .try_into() // <- target type would be `i64`
+ .ok()
+ .and_then(|secs| self.t.tv_sec.checked_add(secs))
+ .expect("overflow when adding duration to time");
+
+ // Nano calculations can't overflow because nanos are <1B which fit
+ // in a u32.
+ let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32;
+ if nsec >= NSEC_PER_SEC as u32 {
+ nsec -= NSEC_PER_SEC as u32;
+ secs = secs.checked_add(1).expect("overflow when adding \
+ duration to time");
+ }
+ Timespec {
+ t: syscall::TimeSpec {
+ tv_sec: secs,
+ tv_nsec: nsec as i32,
+ },
+ }
+ }
+
+ fn sub_duration(&self, other: &Duration) -> Timespec {
+ let mut secs = other
+ .as_secs()
+ .try_into() // <- target type would be `i64`
+ .ok()
+ .and_then(|secs| self.t.tv_sec.checked_sub(secs))
+ .expect("overflow when subtracting duration from time");
+
+ // Similar to above, nanos can't overflow.
+ let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;
+ if nsec < 0 {
+ nsec += NSEC_PER_SEC as i32;
+ secs = secs.checked_sub(1).expect("overflow when subtracting \
+ duration from time");
+ }
+ Timespec {
+ t: syscall::TimeSpec {
+ tv_sec: secs,
+ tv_nsec: nsec as i32,
+ },
+ }
+ }
+}
+
+impl PartialEq for Timespec {
+ fn eq(&self, other: &Timespec) -> bool {
+ self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec
+ }
+}
+
+impl Eq for Timespec {}
+
+impl PartialOrd for Timespec {
+ fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Timespec {
+ fn cmp(&self, other: &Timespec) -> Ordering {
+ let me = (self.t.tv_sec, self.t.tv_nsec);
+ let other = (other.t.tv_sec, other.t.tv_nsec);
+ me.cmp(&other)
+ }
+}
+
+impl Hash for Timespec {
+ fn hash<H : Hasher>(&self, state: &mut H) {
+ self.t.tv_sec.hash(state);
+ self.t.tv_nsec.hash(state);
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Instant {
+ t: Timespec,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct SystemTime {
+ t: Timespec,
+}
+
+pub const UNIX_EPOCH: SystemTime = SystemTime {
+ t: Timespec {
+ t: syscall::TimeSpec {
+ tv_sec: 0,
+ tv_nsec: 0,
+ },
+ },
+};
+
+impl Instant {
+ pub fn now() -> Instant {
+ Instant { t: now(syscall::CLOCK_MONOTONIC) }
+ }
+
+ pub fn sub_instant(&self, other: &Instant) -> Duration {
+ self.t.sub_timespec(&other.t).unwrap_or_else(|_| {
+ panic!("specified instant was later than self")
+ })
+ }
+
+ pub fn add_duration(&self, other: &Duration) -> Instant {
+ Instant { t: self.t.add_duration(other) }
+ }
+
+ pub fn sub_duration(&self, other: &Duration) -> Instant {
+ Instant { t: self.t.sub_duration(other) }
+ }
+}
+
+impl fmt::Debug for Instant {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Instant")
+ .field("tv_sec", &self.t.t.tv_sec)
+ .field("tv_nsec", &self.t.t.tv_nsec)
+ .finish()
+ }
+}
+
+impl SystemTime {
+ pub fn now() -> SystemTime {
+ SystemTime { t: now(syscall::CLOCK_REALTIME) }
+ }
+
+ pub fn sub_time(&self, other: &SystemTime)
+ -> Result<Duration, Duration> {
+ self.t.sub_timespec(&other.t)
+ }
+
+ pub fn add_duration(&self, other: &Duration) -> SystemTime {
+ SystemTime { t: self.t.add_duration(other) }
+ }
+
+ pub fn sub_duration(&self, other: &Duration) -> SystemTime {
+ SystemTime { t: self.t.sub_duration(other) }
+ }
+}
+
+impl From<syscall::TimeSpec> for SystemTime {
+ fn from(t: syscall::TimeSpec) -> SystemTime {
+ SystemTime { t: Timespec { t: t } }
+ }
+}
+
+impl fmt::Debug for SystemTime {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("SystemTime")
+ .field("tv_sec", &self.t.t.tv_sec)
+ .field("tv_nsec", &self.t.t.tv_nsec)
+ .finish()
+ }
+}
+
+pub type clock_t = usize;
+
+fn now(clock: clock_t) -> Timespec {
+ let mut t = Timespec {
+ t: syscall::TimeSpec {
+ tv_sec: 0,
+ tv_nsec: 0,
+ }
+ };
+ cvt(syscall::clock_gettime(clock, &mut t.t)).unwrap();
+ t
+}
diff --git a/ctr-std/src/sys/unix/android.rs b/ctr-std/src/sys/unix/android.rs
new file mode 100644
index 0000000..1043672
--- /dev/null
+++ b/ctr-std/src/sys/unix/android.rs
@@ -0,0 +1,170 @@
+// 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.
+
+//! Android ABI-compatibility module
+//!
+//! The ABI of Android has changed quite a bit over time, and libstd attempts to
+//! be both forwards and backwards compatible as much as possible. We want to
+//! always work with the most recent version of Android, but we also want to
+//! work with older versions of Android for whenever projects need to.
+//!
+//! Our current minimum supported Android version is `android-9`, e.g. Android
+//! with API level 9. We then in theory want to work on that and all future
+//! versions of Android!
+//!
+//! Some of the detection here is done at runtime via `dlopen` and
+//! introspection. Other times no detection is performed at all and we just
+//! provide a fallback implementation as some versions of Android we support
+//! don't have the function.
+//!
+//! You'll find more details below about why each compatibility shim is needed.
+
+#![cfg(target_os = "android")]
+
+use libc::{c_int, c_void, sighandler_t, size_t, ssize_t};
+use libc::{ftruncate, pread, pwrite};
+
+use io;
+use super::{cvt, cvt_r};
+
+// The `log2` and `log2f` functions apparently appeared in android-18, or at
+// least you can see they're not present in the android-17 header [1] and they
+// are present in android-18 [2].
+//
+// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
+// /android-17/arch-arm/usr/include/math.h
+// [2]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
+// /android-18/arch-arm/usr/include/math.h
+//
+// Note that these shims are likely less precise than directly calling `log2`,
+// but hopefully that should be enough for now...
+//
+// Note that mathematically, for any arbitrary `y`:
+//
+// log_2(x) = log_y(x) / log_y(2)
+// = log_y(x) / (1 / log_2(y))
+// = log_y(x) * log_2(y)
+//
+// Hence because `ln` (log_e) is available on all Android we just choose `y = e`
+// and get:
+//
+// log_2(x) = ln(x) * log_2(e)
+
+#[cfg(not(test))]
+pub fn log2f32(f: f32) -> f32 {
+ f.ln() * ::f32::consts::LOG2_E
+}
+
+#[cfg(not(test))]
+pub fn log2f64(f: f64) -> f64 {
+ f.ln() * ::f64::consts::LOG2_E
+}
+
+// Back in the day [1] the `signal` function was just an inline wrapper
+// around `bsd_signal`, but starting in API level android-20 the `signal`
+// symbols was introduced [2]. Finally, in android-21 the API `bsd_signal` was
+// removed [3].
+//
+// Basically this means that if we want to be binary compatible with multiple
+// Android releases (oldest being 9 and newest being 21) then we need to check
+// for both symbols and not actually link against either.
+//
+// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
+// /android-18/arch-arm/usr/include/signal.h
+// [2]: https://chromium.googlesource.com/android_tools/+/fbd420/ndk_experimental
+// /platforms/android-20/arch-arm
+// /usr/include/signal.h
+// [3]: https://chromium.googlesource.com/android_tools/+/20ee6d/ndk/platforms
+// /android-21/arch-arm/usr/include/signal.h
+pub unsafe fn signal(signum: c_int, handler: sighandler_t) -> sighandler_t {
+ weak!(fn signal(c_int, sighandler_t) -> sighandler_t);
+ weak!(fn bsd_signal(c_int, sighandler_t) -> sighandler_t);
+
+ let f = signal.get().or_else(|| bsd_signal.get());
+ let f = f.expect("neither `signal` nor `bsd_signal` symbols found");
+ f(signum, handler)
+}
+
+// The `ftruncate64` symbol apparently appeared in android-12, so we do some
+// dynamic detection to see if we can figure out whether `ftruncate64` exists.
+//
+// If it doesn't we just fall back to `ftruncate`, generating an error for
+// too-large values.
+#[cfg(target_pointer_width = "32")]
+pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> {
+ weak!(fn ftruncate64(c_int, i64) -> c_int);
+
+ unsafe {
+ match ftruncate64.get() {
+ Some(f) => cvt_r(|| f(fd, size as i64)).map(|_| ()),
+ None => {
+ if size > i32::max_value() as u64 {
+ Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot truncate >2GB"))
+ } else {
+ cvt_r(|| ftruncate(fd, size as i32)).map(|_| ())
+ }
+ }
+ }
+ }
+}
+
+#[cfg(target_pointer_width = "64")]
+pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> {
+ unsafe {
+ cvt_r(|| ftruncate(fd, size as i64)).map(|_| ())
+ }
+}
+
+#[cfg(target_pointer_width = "32")]
+pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i64)
+ -> io::Result<ssize_t>
+{
+ use convert::TryInto;
+ weak!(fn pread64(c_int, *mut c_void, size_t, i64) -> ssize_t);
+ pread64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| {
+ if let Ok(o) = offset.try_into() {
+ cvt(pread(fd, buf, count, o))
+ } else {
+ Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot pread >2GB"))
+ }
+ })
+}
+
+#[cfg(target_pointer_width = "32")]
+pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: i64)
+ -> io::Result<ssize_t>
+{
+ use convert::TryInto;
+ weak!(fn pwrite64(c_int, *const c_void, size_t, i64) -> ssize_t);
+ pwrite64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| {
+ if let Ok(o) = offset.try_into() {
+ cvt(pwrite(fd, buf, count, o))
+ } else {
+ Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot pwrite >2GB"))
+ }
+ })
+}
+
+#[cfg(target_pointer_width = "64")]
+pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i64)
+ -> io::Result<ssize_t>
+{
+ cvt(pread(fd, buf, count, offset))
+}
+
+#[cfg(target_pointer_width = "64")]
+pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: i64)
+ -> io::Result<ssize_t>
+{
+ cvt(pwrite(fd, buf, count, offset))
+}
diff --git a/ctr-std/src/sys/unix/args.rs b/ctr-std/src/sys/unix/args.rs
index 84d0c85..e1c7ffc 100644
--- a/ctr-std/src/sys/unix/args.rs
+++ b/ctr-std/src/sys/unix/args.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -8,22 +8,26 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+//! Global initialization and retrieval of command line arguments.
+//!
+//! On some platforms these are stored during runtime startup,
+//! and on some they are retrieved from the system on demand.
+
+#![allow(dead_code)] // runtime init functions not used during testing
+
use ffi::OsString;
use marker::PhantomData;
use vec;
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
- // Currently null because we haven't implemented args yet
-}
+/// One-time global initialization.
+pub unsafe fn init(argc: isize, argv: *const *const u8) { imp::init(argc, argv) }
-pub unsafe fn cleanup() {
-}
+/// One-time global cleanup.
+pub unsafe fn cleanup() { imp::cleanup() }
+/// Returns the command line arguments
pub fn args() -> Args {
- return Args {
- iter: Vec::new().into_iter(),
- _dont_send_or_sync_me: PhantomData,
- }
+ imp::args()
}
pub struct Args {
@@ -39,22 +43,178 @@ impl Args {
impl Iterator for Args {
type Item = OsString;
- fn next(&mut self) -> Option<OsString> {
- self.iter.next()
- }
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.iter.size_hint()
- }
+ fn next(&mut self) -> Option<OsString> { self.iter.next() }
+ fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
}
impl ExactSizeIterator for Args {
- fn len(&self) -> usize {
- self.iter.len()
- }
+ fn len(&self) -> usize { self.iter.len() }
}
impl DoubleEndedIterator for Args {
- fn next_back(&mut self) -> Option<OsString> {
- self.iter.next_back()
+ fn next_back(&mut self) -> Option<OsString> { self.iter.next_back() }
+}
+
+#[cfg(any(target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "bitrig",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris",
+ target_os = "emscripten",
+ target_os = "haiku",
+ target_os = "l4re",
+ target_os = "fuchsia"))]
+mod imp {
+ use os::unix::prelude::*;
+ use ptr;
+ use ffi::{CStr, OsString};
+ use marker::PhantomData;
+ use libc;
+ use super::Args;
+
+ use sys_common::mutex::Mutex;
+
+ static mut ARGC: isize = 0;
+ static mut ARGV: *const *const u8 = ptr::null();
+ static LOCK: Mutex = Mutex::new();
+
+ pub unsafe fn init(argc: isize, argv: *const *const u8) {
+ LOCK.lock();
+ ARGC = argc;
+ ARGV = argv;
+ LOCK.unlock();
+ }
+
+ pub unsafe fn cleanup() {
+ LOCK.lock();
+ ARGC = 0;
+ ARGV = ptr::null();
+ LOCK.unlock();
+ }
+
+ pub fn args() -> Args {
+ Args {
+ iter: clone().into_iter(),
+ _dont_send_or_sync_me: PhantomData
+ }
+ }
+
+ fn clone() -> Vec<OsString> {
+ unsafe {
+ LOCK.lock();
+ let ret = (0..ARGC).map(|i| {
+ let cstr = CStr::from_ptr(*ARGV.offset(i) as *const libc::c_char);
+ OsStringExt::from_vec(cstr.to_bytes().to_vec())
+ }).collect();
+ LOCK.unlock();
+ return ret
+ }
+ }
+}
+
+#[cfg(any(target_os = "macos",
+ target_os = "ios"))]
+mod imp {
+ use ffi::CStr;
+ use marker::PhantomData;
+ use libc;
+ use super::Args;
+
+ pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
+ }
+
+ pub fn cleanup() {
+ }
+
+ #[cfg(target_os = "macos")]
+ pub fn args() -> Args {
+ use os::unix::prelude::*;
+ extern {
+ // These functions are in crt_externs.h.
+ fn _NSGetArgc() -> *mut libc::c_int;
+ fn _NSGetArgv() -> *mut *mut *mut libc::c_char;
+ }
+
+ let vec = unsafe {
+ let (argc, argv) = (*_NSGetArgc() as isize,
+ *_NSGetArgv() as *const *const libc::c_char);
+ (0.. argc as isize).map(|i| {
+ let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec();
+ OsStringExt::from_vec(bytes)
+ }).collect::<Vec<_>>()
+ };
+ Args {
+ iter: vec.into_iter(),
+ _dont_send_or_sync_me: PhantomData,
+ }
+ }
+
+ // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs
+ // and use underscores in their names - they're most probably
+ // are considered private and therefore should be avoided
+ // Here is another way to get arguments using Objective C
+ // runtime
+ //
+ // In general it looks like:
+ // res = Vec::new()
+ // let args = [[NSProcessInfo processInfo] arguments]
+ // for i in (0..[args count])
+ // res.push([args objectAtIndex:i])
+ // res
+ #[cfg(target_os = "ios")]
+ pub fn args() -> Args {
+ use ffi::OsString;
+ use mem;
+ use str;
+
+ extern {
+ fn sel_registerName(name: *const libc::c_uchar) -> Sel;
+ fn objc_getClass(class_name: *const libc::c_uchar) -> NsId;
+ }
+
+ #[cfg(target_arch="aarch64")]
+ extern {
+ fn objc_msgSend(obj: NsId, sel: Sel) -> NsId;
+ #[link_name="objc_msgSend"]
+ fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId;
+ }
+
+ #[cfg(not(target_arch="aarch64"))]
+ extern {
+ fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId;
+ #[link_name="objc_msgSend"]
+ fn objc_msgSend_ul(obj: NsId, sel: Sel, ...) -> NsId;
+ }
+
+ type Sel = *const libc::c_void;
+ type NsId = *const libc::c_void;
+
+ let mut res = Vec::new();
+
+ unsafe {
+ let process_info_sel = sel_registerName("processInfo\0".as_ptr());
+ let arguments_sel = sel_registerName("arguments\0".as_ptr());
+ let utf8_sel = sel_registerName("UTF8String\0".as_ptr());
+ let count_sel = sel_registerName("count\0".as_ptr());
+ let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr());
+
+ let klass = objc_getClass("NSProcessInfo\0".as_ptr());
+ let info = objc_msgSend(klass, process_info_sel);
+ let args = objc_msgSend(info, arguments_sel);
+
+ let cnt: usize = mem::transmute(objc_msgSend(args, count_sel));
+ for i in 0..cnt {
+ let tmp = objc_msgSend_ul(args, object_at_sel, i as libc::c_ulong);
+ let utf_c_str: *const libc::c_char =
+ mem::transmute(objc_msgSend(tmp, utf8_sel));
+ let bytes = CStr::from_ptr(utf_c_str).to_bytes();
+ res.push(OsString::from(str::from_utf8(bytes).unwrap()))
+ }
+ }
+
+ Args { iter: res.into_iter(), _dont_send_or_sync_me: PhantomData }
}
}
diff --git a/ctr-std/src/sys/unix/backtrace/mod.rs b/ctr-std/src/sys/unix/backtrace/mod.rs
new file mode 100644
index 0000000..b5bf20c
--- /dev/null
+++ b/ctr-std/src/sys/unix/backtrace/mod.rs
@@ -0,0 +1,119 @@
+// 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.
+
+/// Backtrace support built on libgcc with some extra OS-specific support
+///
+/// Some methods of getting a backtrace:
+///
+/// * The backtrace() functions on unix. It turns out this doesn't work very
+/// well for green threads on macOS, and the address to symbol portion of it
+/// suffers problems that are described below.
+///
+/// * Using libunwind. This is more difficult than it sounds because libunwind
+/// isn't installed everywhere by default. It's also a bit of a hefty library,
+/// so possibly not the best option. When testing, libunwind was excellent at
+/// getting both accurate backtraces and accurate symbols across platforms.
+/// This route was not chosen in favor of the next option, however.
+///
+/// * We're already using libgcc_s for exceptions in rust (triggering thread
+/// unwinding and running destructors on the stack), and it turns out that it
+/// conveniently comes with a function that also gives us a backtrace. All of
+/// these functions look like _Unwind_*, but it's not quite the full
+/// repertoire of the libunwind API. Due to it already being in use, this was
+/// the chosen route of getting a backtrace.
+///
+/// After choosing libgcc_s for backtraces, the sad part is that it will only
+/// give us a stack trace of instruction pointers. Thankfully these instruction
+/// pointers are accurate (they work for green and native threads), but it's
+/// then up to us again to figure out how to translate these addresses to
+/// symbols. As with before, we have a few options. Before, that, a little bit
+/// of an interlude about symbols. This is my very limited knowledge about
+/// symbol tables, and this information is likely slightly wrong, but the
+/// general idea should be correct.
+///
+/// When talking about symbols, it's helpful to know a few things about where
+/// symbols are located. Some symbols are located in the dynamic symbol table
+/// of the executable which in theory means that they're available for dynamic
+/// linking and lookup. Other symbols end up only in the local symbol table of
+/// the file. This loosely corresponds to pub and priv functions in Rust.
+///
+/// Armed with this knowledge, we know that our solution for address to symbol
+/// translation will need to consult both the local and dynamic symbol tables.
+/// With that in mind, here's our options of translating an address to
+/// a symbol.
+///
+/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr()
+/// behind the scenes to translate, and this is why backtrace() was not used.
+/// Conveniently, this method works fantastically on macOS. It appears dladdr()
+/// uses magic to consult the local symbol table, or we're putting everything
+/// in the dynamic symbol table anyway. Regardless, for macOS, this is the
+/// method used for translation. It's provided by the system and easy to do.o
+///
+/// Sadly, all other systems have a dladdr() implementation that does not
+/// consult the local symbol table. This means that most functions are blank
+/// because they don't have symbols. This means that we need another solution.
+///
+/// * Use unw_get_proc_name(). This is part of the libunwind api (not the
+/// libgcc_s version of the libunwind api), but involves taking a dependency
+/// to libunwind. We may pursue this route in the future if we bundle
+/// libunwind, but libunwind was unwieldy enough that it was not chosen at
+/// this time to provide this functionality.
+///
+/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a
+/// semi-reasonable solution. The stdlib already knows how to spawn processes,
+/// so in theory it could invoke readelf, parse the output, and consult the
+/// local/dynamic symbol tables from there. This ended up not getting chosen
+/// due to the craziness of the idea plus the advent of the next option.
+///
+/// * Use `libbacktrace`. It turns out that this is a small library bundled in
+/// the gcc repository which provides backtrace and symbol translation
+/// functionality. All we really need from it is the backtrace functionality,
+/// and we only really need this on everything that's not macOS, so this is the
+/// chosen route for now.
+///
+/// In summary, the current situation uses libgcc_s to get a trace of stack
+/// pointers, and we use dladdr() or libbacktrace to translate these addresses
+/// to symbols. This is a bit of a hokey implementation as-is, but it works for
+/// all unix platforms we support right now, so it at least gets the job done.
+
+pub use self::tracing::unwind_backtrace;
+pub use self::printing::{foreach_symbol_fileline, resolve_symname};
+
+// tracing impls:
+mod tracing;
+// symbol resolvers:
+mod printing;
+
+#[cfg(not(target_os = "emscripten"))]
+pub mod gnu {
+ use io;
+ use fs;
+ use libc::c_char;
+
+ #[cfg(not(any(target_os = "macos", target_os = "ios")))]
+ pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> {
+ Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
+ }
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> {
+ use env;
+ use os::unix::ffi::OsStrExt;
+
+ let filename = env::current_exe()?;
+ let file = fs::File::open(&filename)?;
+ let mut filename_cstr: Vec<_> = filename.as_os_str().as_bytes().iter()
+ .map(|&x| x as c_char).collect();
+ filename_cstr.push(0); // Null terminate
+ Ok((filename_cstr, file))
+ }
+}
+
+pub struct BacktraceContext;
diff --git a/ctr-std/src/sys/unix/backtrace/printing/dladdr.rs b/ctr-std/src/sys/unix/backtrace/printing/dladdr.rs
new file mode 100644
index 0000000..bc56fd6
--- /dev/null
+++ b/ctr-std/src/sys/unix/backtrace/printing/dladdr.rs
@@ -0,0 +1,45 @@
+// 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.
+
+use io;
+use intrinsics;
+use ffi::CStr;
+use libc;
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
+
+pub fn resolve_symname<F>(frame: Frame,
+ callback: F,
+ _: &BacktraceContext) -> io::Result<()>
+ where F: FnOnce(Option<&str>) -> io::Result<()>
+{
+ unsafe {
+ let mut info: Dl_info = intrinsics::init();
+ let symname = if dladdr(frame.exact_position as *mut _, &mut info) == 0 ||
+ info.dli_sname.is_null() {
+ None
+ } else {
+ CStr::from_ptr(info.dli_sname).to_str().ok()
+ };
+ callback(symname)
+ }
+}
+
+#[repr(C)]
+struct Dl_info {
+ dli_fname: *const libc::c_char,
+ dli_fbase: *mut libc::c_void,
+ dli_sname: *const libc::c_char,
+ dli_saddr: *mut libc::c_void,
+}
+
+extern {
+ fn dladdr(addr: *const libc::c_void, info: *mut Dl_info) -> libc::c_int;
+}
diff --git a/ctr-std/src/sys/unix/backtrace/printing/mod.rs b/ctr-std/src/sys/unix/backtrace/printing/mod.rs
new file mode 100644
index 0000000..caa6071
--- /dev/null
+++ b/ctr-std/src/sys/unix/backtrace/printing/mod.rs
@@ -0,0 +1,43 @@
+// Copyright 2014-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.
+
+mod dladdr;
+
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
+use io;
+
+#[cfg(target_os = "emscripten")]
+pub use self::dladdr::resolve_symname;
+
+#[cfg(target_os = "emscripten")]
+pub fn foreach_symbol_fileline<F>(_: Frame, _: F, _: &BacktraceContext) -> io::Result<bool>
+where
+ F: FnMut(&[u8], u32) -> io::Result<()>
+{
+ Ok(false)
+}
+
+#[cfg(not(target_os = "emscripten"))]
+pub use sys_common::gnu::libbacktrace::foreach_symbol_fileline;
+
+#[cfg(not(target_os = "emscripten"))]
+pub fn resolve_symname<F>(frame: Frame, callback: F, bc: &BacktraceContext) -> io::Result<()>
+where
+ F: FnOnce(Option<&str>) -> io::Result<()>
+{
+ ::sys_common::gnu::libbacktrace::resolve_symname(frame, |symname| {
+ if symname.is_some() {
+ callback(symname)
+ } else {
+ dladdr::resolve_symname(frame, callback, bc)
+ }
+ }, bc)
+}
diff --git a/ctr-std/src/sys/unix/backtrace/tracing/backtrace_fn.rs b/ctr-std/src/sys/unix/backtrace/tracing/backtrace_fn.rs
new file mode 100644
index 0000000..6293eeb
--- /dev/null
+++ b/ctr-std/src/sys/unix/backtrace/tracing/backtrace_fn.rs
@@ -0,0 +1,49 @@
+// 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.
+
+/// As always - iOS on arm uses SjLj exceptions and
+/// _Unwind_Backtrace is even not available there. Still,
+/// backtraces could be extracted using a backtrace function,
+/// which thanks god is public
+///
+/// As mentioned in a huge comment block in `super::super`, backtrace
+/// doesn't play well with green threads, so while it is extremely nice and
+/// simple to use it should be used only on iOS devices as the only viable
+/// option.
+
+use io;
+use libc;
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
+
+#[inline(never)] // if we know this is a function call, we can skip it when
+ // tracing
+pub fn unwind_backtrace(frames: &mut [Frame])
+ -> io::Result<(usize, BacktraceContext)>
+{
+ const FRAME_LEN: usize = 100;
+ assert!(FRAME_LEN >= frames.len());
+ let mut raw_frames = [::ptr::null_mut(); FRAME_LEN];
+ let nb_frames = unsafe {
+ backtrace(raw_frames.as_mut_ptr(), raw_frames.len() as libc::c_int)
+ } as usize;
+ for (from, to) in raw_frames.iter().zip(frames.iter_mut()).take(nb_frames) {
+ *to = Frame {
+ exact_position: *from as *mut u8,
+ symbol_addr: *from as *mut u8,
+ inline_context: 0,
+ };
+ }
+ Ok((nb_frames as usize, BacktraceContext))
+}
+
+extern {
+ fn backtrace(buf: *mut *mut libc::c_void, sz: libc::c_int) -> libc::c_int;
+}
diff --git a/ctr-std/src/sys/unix/backtrace/tracing/gcc_s.rs b/ctr-std/src/sys/unix/backtrace/tracing/gcc_s.rs
new file mode 100644
index 0000000..1b92fc0
--- /dev/null
+++ b/ctr-std/src/sys/unix/backtrace/tracing/gcc_s.rs
@@ -0,0 +1,107 @@
+// 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.
+
+use error::Error;
+use io;
+use libc;
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
+
+use unwind as uw;
+
+struct Context<'a> {
+ idx: usize,
+ frames: &'a mut [Frame],
+}
+
+#[derive(Debug)]
+struct UnwindError(uw::_Unwind_Reason_Code);
+
+impl Error for UnwindError {
+ fn description(&self) -> &'static str {
+ "unexpected return value while unwinding"
+ }
+}
+
+impl ::fmt::Display for UnwindError {
+ fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
+ write!(f, "{}: {:?}", self.description(), self.0)
+ }
+}
+
+#[inline(never)] // if we know this is a function call, we can skip it when
+ // tracing
+pub fn unwind_backtrace(frames: &mut [Frame])
+ -> io::Result<(usize, BacktraceContext)>
+{
+ let mut cx = Context {
+ idx: 0,
+ frames,
+ };
+ let result_unwind = unsafe {
+ uw::_Unwind_Backtrace(trace_fn,
+ &mut cx as *mut Context
+ as *mut libc::c_void)
+ };
+ // See libunwind:src/unwind/Backtrace.c for the return values.
+ // No, there is no doc.
+ match result_unwind {
+ // These return codes seem to be benign and need to be ignored for backtraces
+ // to show up properly on all tested platforms.
+ uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => {
+ Ok((cx.idx, BacktraceContext))
+ }
+ _ => {
+ Err(io::Error::new(io::ErrorKind::Other,
+ UnwindError(result_unwind)))
+ }
+ }
+}
+
+extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
+ arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
+ let cx = unsafe { &mut *(arg as *mut Context) };
+ let mut ip_before_insn = 0;
+ let mut ip = unsafe {
+ uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void
+ };
+ if !ip.is_null() && ip_before_insn == 0 {
+ // this is a non-signaling frame, so `ip` refers to the address
+ // after the calling instruction. account for that.
+ ip = (ip as usize - 1) as *mut _;
+ }
+
+ // dladdr() on osx gets whiny when we use FindEnclosingFunction, and
+ // it appears to work fine without it, so we only use
+ // FindEnclosingFunction on non-osx platforms. In doing so, we get a
+ // slightly more accurate stack trace in the process.
+ //
+ // This is often because panic involves the last instruction of a
+ // function being "call std::rt::begin_unwind", with no ret
+ // instructions after it. This means that the return instruction
+ // pointer points *outside* of the calling function, and by
+ // unwinding it we go back to the original function.
+ let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
+ ip
+ } else {
+ unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
+ };
+
+ if cx.idx < cx.frames.len() {
+ cx.frames[cx.idx] = Frame {
+ symbol_addr: symaddr as *mut u8,
+ exact_position: ip as *mut u8,
+ inline_context: 0,
+ };
+ cx.idx += 1;
+ }
+
+ uw::_URC_NO_REASON
+}
diff --git a/ctr-std/src/sys/unix/backtrace/tracing/mod.rs b/ctr-std/src/sys/unix/backtrace/tracing/mod.rs
new file mode 100644
index 0000000..c9c8e26
--- /dev/null
+++ b/ctr-std/src/sys/unix/backtrace/tracing/mod.rs
@@ -0,0 +1,18 @@
+// 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.
+
+pub use self::imp::*;
+
+#[cfg(not(all(target_os = "ios", target_arch = "arm")))]
+#[path = "gcc_s.rs"]
+mod imp;
+#[cfg(all(target_os = "ios", target_arch = "arm"))]
+#[path = "backtrace_fn.rs"]
+mod imp;
diff --git a/ctr-std/src/sys/unix/condvar.rs b/ctr-std/src/sys/unix/condvar.rs
index bfff16b..4f878d8 100644
--- a/ctr-std/src/sys/unix/condvar.rs
+++ b/ctr-std/src/sys/unix/condvar.rs
@@ -1,4 +1,4 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -8,132 +8,180 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-// *Implementation adapted from `/sys/redox/condvar.rs`
-
use cell::UnsafeCell;
-use intrinsics::atomic_cxchg;
-use ptr;
-use time::Duration;
-
+use libc;
use sys::mutex::{self, Mutex};
+use time::Duration;
-pub struct Condvar {
- lock: UnsafeCell<*mut ::libctru::LightLock>,
-}
+pub struct Condvar { inner: UnsafeCell<libc::pthread_cond_t> }
unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}
+const TIMESPEC_MAX: libc::timespec = libc::timespec {
+ tv_sec: <libc::time_t>::max_value(),
+ tv_nsec: 1_000_000_000 - 1,
+};
+
+fn saturating_cast_to_time_t(value: u64) -> libc::time_t {
+ if value > <libc::time_t>::max_value() as u64 {
+ <libc::time_t>::max_value()
+ } else {
+ value as libc::time_t
+ }
+}
+
impl Condvar {
pub const fn new() -> Condvar {
- Condvar {
- lock: UnsafeCell::new(ptr::null_mut()),
- }
+ // Might be moved and address is changing it is better to avoid
+ // initialization of potentially opaque OS data before it landed
+ Condvar { inner: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER) }
}
- #[inline]
- pub unsafe fn init(&self) {
- *self.lock.get() = ptr::null_mut();
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "l4re",
+ target_os = "android"))]
+ pub unsafe fn init(&mut self) {}
+
+ #[cfg(not(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "l4re",
+ target_os = "android")))]
+ pub unsafe fn init(&mut self) {
+ use mem;
+ let mut attr: libc::pthread_condattr_t = mem::uninitialized();
+ let r = libc::pthread_condattr_init(&mut attr);
+ assert_eq!(r, 0);
+ let r = libc::pthread_condattr_setclock(&mut attr, libc::CLOCK_MONOTONIC);
+ assert_eq!(r, 0);
+ let r = libc::pthread_cond_init(self.inner.get(), &attr);
+ assert_eq!(r, 0);
+ let r = libc::pthread_condattr_destroy(&mut attr);
+ assert_eq!(r, 0);
}
#[inline]
- pub fn notify_one(&self) {
- unsafe {
- let arbiter = ::libctru::__sync_get_arbiter();
-
- ::libctru::svcArbitrateAddress(arbiter,
- *self.lock.get() as u32,
- ::libctru::ARBITRATION_SIGNAL,
- 1,
- 0);
- }
+ pub unsafe fn notify_one(&self) {
+ let r = libc::pthread_cond_signal(self.inner.get());
+ debug_assert_eq!(r, 0);
}
#[inline]
- pub fn notify_all(&self) {
- unsafe {
- let lock = self.lock.get();
-
- if *lock == ptr::null_mut() {
- return;
- }
-
- let arbiter = ::libctru::__sync_get_arbiter();
-
- ::libctru::svcArbitrateAddress(arbiter,
- *self.lock.get() as u32,
- ::libctru::ARBITRATION_SIGNAL,
- -1,
- 0);
- }
+ pub unsafe fn notify_all(&self) {
+ let r = libc::pthread_cond_broadcast(self.inner.get());
+ debug_assert_eq!(r, 0);
}
#[inline]
- pub fn wait(&self, mutex: &Mutex) {
- unsafe {
- let lock = self.lock.get();
-
- if *lock != mutex::raw(mutex) {
- if *lock != ptr::null_mut() {
- panic!("Condvar used with more than one Mutex");
- }
-
- atomic_cxchg(lock as *mut usize, 0, mutex::raw(mutex) as usize);
- }
+ pub unsafe fn wait(&self, mutex: &Mutex) {
+ let r = libc::pthread_cond_wait(self.inner.get(), mutex::raw(mutex));
+ debug_assert_eq!(r, 0);
+ }
- mutex.unlock();
+ // This implementation is used on systems that support pthread_condattr_setclock
+ // where we configure condition variable to use monotonic clock (instead of
+ // default system clock). This approach avoids all problems that result
+ // from changes made to the system time.
+ #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+ use mem;
+
+ let mut now: libc::timespec = mem::zeroed();
+ let r = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now);
+ assert_eq!(r, 0);
+
+ // Nanosecond calculations can't overflow because both values are below 1e9.
+ let nsec = dur.subsec_nanos() + now.tv_nsec as u32;
+
+ let sec = saturating_cast_to_time_t(dur.as_secs())
+ .checked_add((nsec / 1_000_000_000) as libc::time_t)
+ .and_then(|s| s.checked_add(now.tv_sec));
+ let nsec = nsec % 1_000_000_000;
+
+ let timeout = sec.map(|s| {
+ libc::timespec { tv_sec: s, tv_nsec: nsec as _}
+ }).unwrap_or(TIMESPEC_MAX);
+
+ let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex),
+ &timeout);
+ assert!(r == libc::ETIMEDOUT || r == 0);
+ r == 0
+ }
- let arbiter = ::libctru::__sync_get_arbiter();
- ::libctru::svcArbitrateAddress(arbiter,
- *self.lock.get() as u32,
- ::libctru::ARBITRATION_WAIT_IF_LESS_THAN,
- 2,
- 0);
+ // This implementation is modeled after libcxx's condition_variable
+ // https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46
+ // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367
+ #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, mut dur: Duration) -> bool {
+ use ptr;
+ use time::Instant;
- mutex.lock();
+ // 1000 years
+ let max_dur = Duration::from_secs(1000 * 365 * 86400);
+
+ if dur > max_dur {
+ // OSX implementation of `pthread_cond_timedwait` is buggy
+ // with super long durations. When duration is greater than
+ // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait`
+ // in macOS Sierra return error 316.
+ //
+ // This program demonstrates the issue:
+ // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c
+ //
+ // To work around this issue, and possible bugs of other OSes, timeout
+ // is clamped to 1000 years, which is allowable per the API of `wait_timeout`
+ // because of spurious wakeups.
+
+ dur = max_dur;
}
+
+ // First, figure out what time it currently is, in both system and
+ // stable time. pthread_cond_timedwait uses system time, but we want to
+ // report timeout based on stable time.
+ let mut sys_now = libc::timeval { tv_sec: 0, tv_usec: 0 };
+ let stable_now = Instant::now();
+ let r = libc::gettimeofday(&mut sys_now, ptr::null_mut());
+ debug_assert_eq!(r, 0);
+
+ let nsec = dur.subsec_nanos() as libc::c_long +
+ (sys_now.tv_usec * 1000) as libc::c_long;
+ let extra = (nsec / 1_000_000_000) as libc::time_t;
+ let nsec = nsec % 1_000_000_000;
+ let seconds = saturating_cast_to_time_t(dur.as_secs());
+
+ let timeout = sys_now.tv_sec.checked_add(extra).and_then(|s| {
+ s.checked_add(seconds)
+ }).map(|s| {
+ libc::timespec { tv_sec: s, tv_nsec: nsec }
+ }).unwrap_or(TIMESPEC_MAX);
+
+ // And wait!
+ let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex),
+ &timeout);
+ debug_assert!(r == libc::ETIMEDOUT || r == 0);
+
+ // ETIMEDOUT is not a totally reliable method of determining timeout due
+ // to clock shifts, so do the check ourselves
+ stable_now.elapsed() < dur
}
#[inline]
- pub fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
- use time::Instant;
-
- unsafe {
- let lock = self.lock.get();
-
- if *lock != mutex::raw(mutex) {
- if *lock != ptr::null_mut() {
- panic!("Condvar used with more than one Mutex");
- }
-
- atomic_cxchg(lock as *mut usize, 0, mutex::raw(mutex) as usize);
- }
-
- let now = Instant::now();
-
- let nanos = dur.as_secs()
- .saturating_mul(1_000_000_000)
- .saturating_add(dur.subsec_nanos() as u64);
-
- mutex.unlock();
-
- let arbiter = ::libctru::__sync_get_arbiter();
-
- ::libctru::svcArbitrateAddress(arbiter,
- *self.lock.get() as u32,
- ::libctru::ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT,
- 2,
- nanos as i64);
-
- mutex.lock();
-
- now.elapsed() < dur
- }
+ #[cfg(not(target_os = "dragonfly"))]
+ pub unsafe fn destroy(&self) {
+ let r = libc::pthread_cond_destroy(self.inner.get());
+ debug_assert_eq!(r, 0);
}
#[inline]
+ #[cfg(target_os = "dragonfly")]
pub unsafe fn destroy(&self) {
- *self.lock.get() = ptr::null_mut();
+ let r = libc::pthread_cond_destroy(self.inner.get());
+ // On DragonFly pthread_cond_destroy() returns EINVAL if called on
+ // a condvar that was just initialized with
+ // libc::PTHREAD_COND_INITIALIZER. Once it is used or
+ // pthread_cond_init() is called, this behaviour no longer occurs.
+ debug_assert!(r == 0 || r == libc::EINVAL);
}
}
diff --git a/ctr-std/src/sys/unix/env.rs b/ctr-std/src/sys/unix/env.rs
index 393bdfe..00cf7ec 100644
--- a/ctr-std/src/sys/unix/env.rs
+++ b/ctr-std/src/sys/unix/env.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -8,12 +8,167 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+#[cfg(target_os = "linux")]
pub mod os {
- pub const FAMILY: &'static str = "nintendo";
- pub const OS: &'static str = "horizon";
- pub const DLL_PREFIX: &'static str = "";
- pub const DLL_SUFFIX: &'static str = ".cro";
- pub const DLL_EXTENSION: &'static str = ".cro";
- pub const EXE_SUFFIX: &'static str = ".3dsx";
- pub const EXE_EXTENSION: &'static str = "3dsx";
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "linux";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "macos")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "macos";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".dylib";
+ pub const DLL_EXTENSION: &'static str = "dylib";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "ios")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "ios";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".dylib";
+ pub const DLL_EXTENSION: &'static str = "dylib";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "freebsd")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "freebsd";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "dragonfly")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "dragonfly";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "bitrig")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "bitrig";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "netbsd")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "netbsd";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "openbsd")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "openbsd";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "android")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "android";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "solaris")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "solaris";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "haiku")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "haiku";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(all(target_os = "emscripten", target_arch = "asmjs"))]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "emscripten";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = ".js";
+ pub const EXE_EXTENSION: &'static str = "js";
+}
+
+#[cfg(all(target_os = "emscripten", target_arch = "wasm32"))]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "emscripten";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = ".js";
+ pub const EXE_EXTENSION: &'static str = "js";
+}
+
+#[cfg(target_os = "fuchsia")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "fuchsia";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "l4re")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "l4re";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
}
diff --git a/ctr-std/src/sys/unix/ext/ffi.rs b/ctr-std/src/sys/unix/ext/ffi.rs
index fb9984c..8347145 100644
--- a/ctr-std/src/sys/unix/ext/ffi.rs
+++ b/ctr-std/src/sys/unix/ext/ffi.rs
@@ -17,7 +17,9 @@ use mem;
use sys::os_str::Buf;
use sys_common::{FromInner, IntoInner, AsInner};
-/// Unix-specific extensions to `OsString`.
+/// Unix-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 byte vector.
@@ -66,7 +68,9 @@ impl OsStringExt for OsString {
}
}
-/// Unix-specific extensions to `OsStr`.
+/// Unix-specific extensions to [`OsStr`].
+///
+/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html
#[stable(feature = "rust1", since = "1.0.0")]
pub trait OsStrExt {
#[stable(feature = "rust1", since = "1.0.0")]
diff --git a/ctr-std/src/sys/unix/ext/fs.rs b/ctr-std/src/sys/unix/ext/fs.rs
new file mode 100644
index 0000000..4e98101
--- /dev/null
+++ b/ctr-std/src/sys/unix/ext/fs.rs
@@ -0,0 +1,736 @@
+// 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.
+
+//! Unix-specific extensions to primitives in the `std::fs` module.
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+use fs::{self, Permissions, OpenOptions};
+use io;
+use libc;
+use path::Path;
+use sys;
+use sys_common::{FromInner, AsInner, AsInnerMut};
+use sys::platform::fs::MetadataExt as UnixMetadataExt;
+
+/// Unix-specific extensions to [`File`].
+///
+/// [`File`]: ../../../../std/fs/struct.File.html
+#[stable(feature = "file_offset", since = "1.15.0")]
+pub trait FileExt {
+ /// Reads a number of bytes starting from a given offset.
+ ///
+ /// 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 file cursor is not affected by this function.
+ ///
+ /// Note that similar to [`File::read`], it is not an error to return with a
+ /// short read.
+ ///
+ /// [`File::read`]: ../../../../std/fs/struct.File.html#method.read
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::io;
+ /// use std::fs::File;
+ /// use std::os::unix::prelude::FileExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let mut buf = [0u8; 8];
+ /// let file = File::open("foo.txt")?;
+ ///
+ /// // We now read 8 bytes from the offset 10.
+ /// let num_bytes_read = file.read_at(&mut buf, 10)?;
+ /// println!("read {} bytes: {:?}", num_bytes_read, buf);
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_offset", since = "1.15.0")]
+ fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
+
+ /// Writes a number of bytes starting from a given offset.
+ ///
+ /// 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 file cursor is not affected by this function.
+ ///
+ /// When writing beyond the end of the file, the file is appropriately
+ /// extended and the intermediate bytes are initialized with the value 0.
+ ///
+ /// Note that similar to [`File::write`], it is not an error to return a
+ /// short write.
+ ///
+ /// [`File::write`]: ../../../../std/fs/struct.File.html#write.v
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::File;
+ /// use std::io;
+ /// use std::os::unix::prelude::FileExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let file = File::open("foo.txt")?;
+ ///
+ /// // We now write at the offset 10.
+ /// file.write_at(b"sushi", 10)?;
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_offset", since = "1.15.0")]
+ fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
+}
+
+#[stable(feature = "file_offset", since = "1.15.0")]
+impl FileExt for fs::File {
+ fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
+ self.as_inner().read_at(buf, offset)
+ }
+ fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
+ self.as_inner().write_at(buf, offset)
+ }
+}
+
+/// Unix-specific extensions to [`fs::Permissions`].
+///
+/// [`fs::Permissions`]: ../../../../std/fs/struct.Permissions.html
+#[stable(feature = "fs_ext", since = "1.1.0")]
+pub trait PermissionsExt {
+ /// Returns the underlying raw `st_mode` bits that contain the standard
+ /// Unix permissions for this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::File;
+ /// use std::os::unix::fs::PermissionsExt;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let f = File::create("foo.txt")?;
+ /// let metadata = f.metadata()?;
+ /// let permissions = metadata.permissions();
+ ///
+ /// println!("permissions: {}", permissions.mode());
+ /// Ok(()) }
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn mode(&self) -> u32;
+
+ /// Sets the underlying raw bits for this set of permissions.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::File;
+ /// use std::os::unix::fs::PermissionsExt;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let f = File::create("foo.txt")?;
+ /// let metadata = f.metadata()?;
+ /// let mut permissions = metadata.permissions();
+ ///
+ /// permissions.set_mode(0o644); // Read/write for owner and read for others.
+ /// assert_eq!(permissions.mode(), 0o644);
+ /// Ok(()) }
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn set_mode(&mut self, mode: u32);
+
+ /// Creates a new instance of `Permissions` from the given set of Unix
+ /// permission bits.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::fs::Permissions;
+ /// use std::os::unix::fs::PermissionsExt;
+ ///
+ /// // Read/write for owner and read for others.
+ /// let permissions = Permissions::from_mode(0o644);
+ /// assert_eq!(permissions.mode(), 0o644);
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn from_mode(mode: u32) -> Self;
+}
+
+#[stable(feature = "fs_ext", since = "1.1.0")]
+impl PermissionsExt for Permissions {
+ fn mode(&self) -> u32 {
+ self.as_inner().mode()
+ }
+
+ fn set_mode(&mut self, mode: u32) {
+ *self = Permissions::from_inner(FromInner::from_inner(mode));
+ }
+
+ fn from_mode(mode: u32) -> Permissions {
+ Permissions::from_inner(FromInner::from_inner(mode))
+ }
+}
+
+/// Unix-specific extensions to [`fs::OpenOptions`].
+///
+/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html
+#[stable(feature = "fs_ext", since = "1.1.0")]
+pub trait OpenOptionsExt {
+ /// Sets the mode bits that a new file will be created with.
+ ///
+ /// If a new file is created as part of a `File::open_opts` call then this
+ /// specified `mode` will be used as the permission bits for the new file.
+ /// If no `mode` is set, the default of `0o666` will be used.
+ /// The operating system masks out bits with the systems `umask`, to produce
+ /// the final permissions.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::OpenOptions;
+ /// use std::os::unix::fs::OpenOptionsExt;
+ ///
+ /// # fn main() {
+ /// let mut options = OpenOptions::new();
+ /// options.mode(0o644); // Give read/write for owner and read for others.
+ /// let file = options.open("foo.txt");
+ /// # }
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn mode(&mut self, mode: u32) -> &mut Self;
+
+ /// Pass custom flags to the `flags` argument of `open`.
+ ///
+ /// The bits that define the access mode are masked out with `O_ACCMODE`, to
+ /// ensure they do not interfere with the access mode set by Rusts options.
+ ///
+ /// Custom flags can only set flags, not remove flags set by Rusts options.
+ /// This options overwrites any previously set custom flags.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// # #![feature(libc)]
+ /// extern crate libc;
+ /// use std::fs::OpenOptions;
+ /// use std::os::unix::fs::OpenOptionsExt;
+ ///
+ /// # fn main() {
+ /// let mut options = OpenOptions::new();
+ /// options.write(true);
+ /// if cfg!(unix) {
+ /// options.custom_flags(libc::O_NOFOLLOW);
+ /// }
+ /// let file = options.open("foo.txt");
+ /// # }
+ /// ```
+ #[stable(feature = "open_options_ext", since = "1.10.0")]
+ fn custom_flags(&mut self, flags: i32) -> &mut Self;
+}
+
+#[stable(feature = "fs_ext", since = "1.1.0")]
+impl OpenOptionsExt for OpenOptions {
+ fn mode(&mut self, mode: u32) -> &mut OpenOptions {
+ self.as_inner_mut().mode(mode); self
+ }
+
+ fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions {
+ self.as_inner_mut().custom_flags(flags); self
+ }
+}
+
+/// Unix-specific extensions to [`fs::Metadata`].
+///
+/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+pub trait MetadataExt {
+ /// Returns the ID of the device containing the file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::io;
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let dev_id = meta.dev();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn dev(&self) -> u64;
+ /// Returns the inode number.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let inode = meta.ino();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn ino(&self) -> u64;
+ /// Returns the rights applied to this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let mode = meta.mode();
+ /// let user_has_write_access = mode & 0o200;
+ /// let user_has_read_write_access = mode & 0o600;
+ /// let group_has_read_access = mode & 0o040;
+ /// let others_have_exec_access = mode & 0o001;
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn mode(&self) -> u32;
+ /// Returns the number of hard links pointing to this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let nb_hard_links = meta.nlink();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn nlink(&self) -> u64;
+ /// Returns the user ID of the owner of this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let user_id = meta.uid();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn uid(&self) -> u32;
+ /// Returns the group ID of the owner of this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let group_id = meta.gid();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn gid(&self) -> u32;
+ /// Returns the device ID of this file (if it is a special one).
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let device_id = meta.rdev();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn rdev(&self) -> u64;
+ /// Returns the total size of this file in bytes.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let file_size = meta.size();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn size(&self) -> u64;
+ /// Returns the time of the last access to the file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let last_access_time = meta.atime();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn atime(&self) -> i64;
+ /// Returns the time of the last access to the file in nanoseconds.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let nano_last_access_time = meta.atime_nsec();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn atime_nsec(&self) -> i64;
+ /// Returns the time of the last modification of the file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let last_modification_time = meta.mtime();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn mtime(&self) -> i64;
+ /// Returns the time of the last modification of the file in nanoseconds.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let nano_last_modification_time = meta.mtime_nsec();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn mtime_nsec(&self) -> i64;
+ /// Returns the time of the last status change of the file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let last_status_change_time = meta.ctime();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn ctime(&self) -> i64;
+ /// Returns the time of the last status change of the file in nanoseconds.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let nano_last_status_change_time = meta.ctime_nsec();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn ctime_nsec(&self) -> i64;
+ /// Returns the blocksize for filesystem I/O.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let blocksize = meta.blksize();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn blksize(&self) -> u64;
+ /// Returns the number of blocks allocated to the file, in 512-byte units.
+ ///
+ /// Please note that this may be smaller than `st_size / 512` when the file has holes.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let blocks = meta.blocks();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn blocks(&self) -> u64;
+}
+
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+impl MetadataExt for fs::Metadata {
+ fn dev(&self) -> u64 { self.st_dev() }
+ fn ino(&self) -> u64 { self.st_ino() }
+ fn mode(&self) -> u32 { self.st_mode() }
+ fn nlink(&self) -> u64 { self.st_nlink() }
+ fn uid(&self) -> u32 { self.st_uid() }
+ fn gid(&self) -> u32 { self.st_gid() }
+ fn rdev(&self) -> u64 { self.st_rdev() }
+ fn size(&self) -> u64 { self.st_size() }
+ fn atime(&self) -> i64 { self.st_atime() }
+ fn atime_nsec(&self) -> i64 { self.st_atime_nsec() }
+ fn mtime(&self) -> i64 { self.st_mtime() }
+ fn mtime_nsec(&self) -> i64 { self.st_mtime_nsec() }
+ fn ctime(&self) -> i64 { self.st_ctime() }
+ fn ctime_nsec(&self) -> i64 { self.st_ctime_nsec() }
+ fn blksize(&self) -> u64 { self.st_blksize() }
+ fn blocks(&self) -> u64 { self.st_blocks() }
+}
+
+/// Unix-specific extensions for [`FileType`].
+///
+/// Adds support for special Unix file types such as block/character devices,
+/// pipes, and sockets.
+///
+/// [`FileType`]: ../../../../std/fs/struct.FileType.html
+#[stable(feature = "file_type_ext", since = "1.5.0")]
+pub trait FileTypeExt {
+ /// Returns whether this file type is a block device.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::FileTypeExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("block_device_file")?;
+ /// let file_type = meta.file_type();
+ /// assert!(file_type.is_block_device());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_block_device(&self) -> bool;
+ /// Returns whether this file type is a char device.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::FileTypeExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("char_device_file")?;
+ /// let file_type = meta.file_type();
+ /// assert!(file_type.is_char_device());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_char_device(&self) -> bool;
+ /// Returns whether this file type is a fifo.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::FileTypeExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("fifo_file")?;
+ /// let file_type = meta.file_type();
+ /// assert!(file_type.is_fifo());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_fifo(&self) -> bool;
+ /// Returns whether this file type is a socket.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::FileTypeExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("unix.socket")?;
+ /// let file_type = meta.file_type();
+ /// assert!(file_type.is_socket());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_socket(&self) -> bool;
+}
+
+#[stable(feature = "file_type_ext", since = "1.5.0")]
+impl FileTypeExt for fs::FileType {
+ fn is_block_device(&self) -> bool { self.as_inner().is(libc::S_IFBLK) }
+ fn is_char_device(&self) -> bool { self.as_inner().is(libc::S_IFCHR) }
+ fn is_fifo(&self) -> bool { self.as_inner().is(libc::S_IFIFO) }
+ fn is_socket(&self) -> bool { self.as_inner().is(libc::S_IFSOCK) }
+}
+
+/// Unix-specific extension methods for [`fs::DirEntry`].
+///
+/// [`fs::DirEntry`]: ../../../../std/fs/struct.DirEntry.html
+#[stable(feature = "dir_entry_ext", since = "1.1.0")]
+pub trait DirEntryExt {
+ /// Returns the underlying `d_ino` field in the contained `dirent`
+ /// structure.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::fs;
+ /// use std::os::unix::fs::DirEntryExt;
+ ///
+ /// if let Ok(entries) = fs::read_dir(".") {
+ /// for entry in entries {
+ /// if let Ok(entry) = entry {
+ /// // Here, `entry` is a `DirEntry`.
+ /// println!("{:?}: {}", entry.file_name(), entry.ino());
+ /// }
+ /// }
+ /// }
+ /// ```
+ #[stable(feature = "dir_entry_ext", since = "1.1.0")]
+ fn ino(&self) -> u64;
+}
+
+#[stable(feature = "dir_entry_ext", since = "1.1.0")]
+impl DirEntryExt for fs::DirEntry {
+ fn ino(&self) -> u64 { self.as_inner().ino() }
+}
+
+/// Creates a new symbolic link on the filesystem.
+///
+/// The `dst` path will be a symbolic link pointing to the `src` path.
+///
+/// # Note
+///
+/// On Windows, you must specify whether a symbolic link points to a file
+/// or directory. Use `os::windows::fs::symlink_file` to create a
+/// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a
+/// symbolic link to a directory. Additionally, the process must have
+/// `SeCreateSymbolicLinkPrivilege` in order to be able to create a
+/// symbolic link.
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::os::unix::fs;
+///
+/// fn main() -> std::io::Result<()> {
+/// fs::symlink("a.txt", "b.txt")?;
+/// Ok(())
+/// }
+/// ```
+#[stable(feature = "symlink", since = "1.1.0")]
+pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()>
+{
+ sys::fs::symlink(src.as_ref(), dst.as_ref())
+}
+
+/// Unix-specific extensions to [`fs::DirBuilder`].
+///
+/// [`fs::DirBuilder`]: ../../../../std/fs/struct.DirBuilder.html
+#[stable(feature = "dir_builder", since = "1.6.0")]
+pub trait DirBuilderExt {
+ /// Sets the mode to create new directories with. This option defaults to
+ /// 0o777.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::DirBuilder;
+ /// use std::os::unix::fs::DirBuilderExt;
+ ///
+ /// let mut builder = DirBuilder::new();
+ /// builder.mode(0o755);
+ /// ```
+ #[stable(feature = "dir_builder", since = "1.6.0")]
+ fn mode(&mut self, mode: u32) -> &mut Self;
+}
+
+#[stable(feature = "dir_builder", since = "1.6.0")]
+impl DirBuilderExt for fs::DirBuilder {
+ fn mode(&mut self, mode: u32) -> &mut fs::DirBuilder {
+ self.as_inner_mut().set_mode(mode);
+ self
+ }
+}
diff --git a/ctr-std/src/sys/unix/ext/mod.rs b/ctr-std/src/sys/unix/ext/mod.rs
index 7b0d175..c221f7c 100644
--- a/ctr-std/src/sys/unix/ext/mod.rs
+++ b/ctr-std/src/sys/unix/ext/mod.rs
@@ -38,11 +38,11 @@
pub mod io;
pub mod ffi;
-//pub mod fs;
-//pub mod process;
+pub mod fs;
+pub mod process;
pub mod raw;
-//pub mod thread;
-//pub mod net;
+pub mod thread;
+pub mod net;
/// A prelude for conveniently writing platform-specific code.
///
@@ -53,14 +53,14 @@ pub mod prelude {
pub use super::io::{RawFd, AsRawFd, FromRawFd, IntoRawFd};
#[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::{PermissionsExt, OpenOptionsExt, MetadataExt, FileTypeExt};
- //#[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")]
- //pub use super::fs::DirEntryExt;
- //#[doc(no_inline)] #[stable(feature = "file_offset", since = "1.15.0")]
- //pub use super::fs::FileExt;
- //#[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")]
- //pub use super::thread::JoinHandleExt;
- //#[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")]
- //pub use super::process::{CommandExt, ExitStatusExt};
+ #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")]
+ pub use super::fs::{PermissionsExt, OpenOptionsExt, MetadataExt, FileTypeExt};
+ #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")]
+ pub use super::fs::DirEntryExt;
+ #[doc(no_inline)] #[stable(feature = "file_offset", since = "1.15.0")]
+ pub use super::fs::FileExt;
+ #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")]
+ pub use super::thread::JoinHandleExt;
+ #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")]
+ pub use super::process::{CommandExt, ExitStatusExt};
}
diff --git a/ctr-std/src/sys/unix/ext/net.rs b/ctr-std/src/sys/unix/ext/net.rs
new file mode 100644
index 0000000..e277b1a
--- /dev/null
+++ b/ctr-std/src/sys/unix/ext/net.rs
@@ -0,0 +1,1818 @@
+// 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.
+
+#![stable(feature = "unix_socket", since = "1.10.0")]
+
+//! Unix-specific networking functionality
+
+#[cfg(unix)]
+use libc;
+
+// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here?
+#[cfg(not(unix))]
+mod libc {
+ pub use libc::c_int;
+ pub type socklen_t = u32;
+ pub struct sockaddr;
+ #[derive(Clone)]
+ pub struct sockaddr_un;
+}
+
+use ascii;
+use ffi::OsStr;
+use fmt;
+use io::{self, Initializer};
+use mem;
+use net::{self, Shutdown};
+use os::unix::ffi::OsStrExt;
+use os::unix::io::{RawFd, AsRawFd, FromRawFd, IntoRawFd};
+use path::Path;
+use time::Duration;
+use sys::{self, cvt};
+use sys::net::Socket;
+use sys_common::{self, AsInner, FromInner, IntoInner};
+
+#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "dragonfly", target_os = "freebsd",
+ target_os = "openbsd", target_os = "netbsd",
+ target_os = "haiku", target_os = "bitrig"))]
+use libc::MSG_NOSIGNAL;
+#[cfg(not(any(target_os = "linux", target_os = "android",
+ target_os = "dragonfly", target_os = "freebsd",
+ target_os = "openbsd", target_os = "netbsd",
+ target_os = "haiku", target_os = "bitrig")))]
+const MSG_NOSIGNAL: libc::c_int = 0x0;
+
+fn sun_path_offset() -> usize {
+ // Work with an actual instance of the type since using a null pointer is UB
+ let addr: libc::sockaddr_un = unsafe { mem::uninitialized() };
+ let base = &addr as *const _ as usize;
+ let path = &addr.sun_path as *const _ as usize;
+ path - base
+}
+
+unsafe fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> {
+ let mut addr: libc::sockaddr_un = mem::zeroed();
+ addr.sun_family = libc::AF_UNIX as libc::sa_family_t;
+
+ let bytes = path.as_os_str().as_bytes();
+
+ if bytes.contains(&0) {
+ return Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "paths may not contain interior null bytes"));
+ }
+
+ if bytes.len() >= addr.sun_path.len() {
+ return Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "path must be shorter than SUN_LEN"));
+ }
+ for (dst, src) in addr.sun_path.iter_mut().zip(bytes.iter()) {
+ *dst = *src as libc::c_char;
+ }
+ // null byte for pathname addresses is already there because we zeroed the
+ // struct
+
+ let mut len = sun_path_offset() + bytes.len();
+ match bytes.get(0) {
+ Some(&0) | None => {}
+ Some(_) => len += 1,
+ }
+ Ok((addr, len as libc::socklen_t))
+}
+
+enum AddressKind<'a> {
+ Unnamed,
+ Pathname(&'a Path),
+ Abstract(&'a [u8]),
+}
+
+/// An address associated with a Unix socket.
+///
+/// # Examples
+///
+/// ```
+/// use std::os::unix::net::UnixListener;
+///
+/// let socket = match UnixListener::bind("/tmp/sock") {
+/// Ok(sock) => sock,
+/// Err(e) => {
+/// println!("Couldn't bind: {:?}", e);
+/// return
+/// }
+/// };
+/// let addr = socket.local_addr().expect("Couldn't get local address");
+/// ```
+#[derive(Clone)]
+#[stable(feature = "unix_socket", since = "1.10.0")]
+pub struct SocketAddr {
+ addr: libc::sockaddr_un,
+ len: libc::socklen_t,
+}
+
+impl SocketAddr {
+ fn new<F>(f: F) -> io::Result<SocketAddr>
+ where F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int
+ {
+ unsafe {
+ let mut addr: libc::sockaddr_un = mem::zeroed();
+ let mut len = mem::size_of::<libc::sockaddr_un>() as libc::socklen_t;
+ cvt(f(&mut addr as *mut _ as *mut _, &mut len))?;
+ SocketAddr::from_parts(addr, len)
+ }
+ }
+
+ fn from_parts(addr: libc::sockaddr_un, mut len: libc::socklen_t) -> io::Result<SocketAddr> {
+ if len == 0 {
+ // When there is a datagram from unnamed unix socket
+ // linux returns zero bytes of address
+ len = sun_path_offset() as libc::socklen_t; // i.e. zero-length address
+ } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t {
+ return Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "file descriptor did not correspond to a Unix socket"));
+ }
+
+ Ok(SocketAddr {
+ addr,
+ len,
+ })
+ }
+
+ /// Returns true if and only if the address is unnamed.
+ ///
+ /// # Examples
+ ///
+ /// A named address:
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ ///
+ /// let socket = UnixListener::bind("/tmp/sock").unwrap();
+ /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// assert_eq!(addr.is_unnamed(), false);
+ /// ```
+ ///
+ /// An unnamed address:
+ ///
+ /// ```
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let socket = UnixDatagram::unbound().unwrap();
+ /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// assert_eq!(addr.is_unnamed(), true);
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn is_unnamed(&self) -> bool {
+ if let AddressKind::Unnamed = self.address() {
+ true
+ } else {
+ false
+ }
+ }
+
+ /// Returns the contents of this address if it is a `pathname` address.
+ ///
+ /// # Examples
+ ///
+ /// With a pathname:
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ /// use std::path::Path;
+ ///
+ /// let socket = UnixListener::bind("/tmp/sock").unwrap();
+ /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock")));
+ /// ```
+ ///
+ /// Without a pathname:
+ ///
+ /// ```
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let socket = UnixDatagram::unbound().unwrap();
+ /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// assert_eq!(addr.as_pathname(), None);
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn as_pathname(&self) -> Option<&Path> {
+ if let AddressKind::Pathname(path) = self.address() {
+ Some(path)
+ } else {
+ None
+ }
+ }
+
+ fn address<'a>(&'a self) -> AddressKind<'a> {
+ let len = self.len as usize - sun_path_offset();
+ let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) };
+
+ // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses
+ if len == 0
+ || (cfg!(not(any(target_os = "linux", target_os = "android")))
+ && self.addr.sun_path[0] == 0)
+ {
+ AddressKind::Unnamed
+ } else if self.addr.sun_path[0] == 0 {
+ AddressKind::Abstract(&path[1..len])
+ } else {
+ AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref())
+ }
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl fmt::Debug for SocketAddr {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ match self.address() {
+ AddressKind::Unnamed => write!(fmt, "(unnamed)"),
+ AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)),
+ AddressKind::Pathname(path) => write!(fmt, "{:?} (pathname)", path),
+ }
+ }
+}
+
+struct AsciiEscaped<'a>(&'a [u8]);
+
+impl<'a> fmt::Display for AsciiEscaped<'a> {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ write!(fmt, "\"")?;
+ for byte in self.0.iter().cloned().flat_map(ascii::escape_default) {
+ write!(fmt, "{}", byte as char)?;
+ }
+ write!(fmt, "\"")
+ }
+}
+
+/// A Unix stream socket.
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::os::unix::net::UnixStream;
+/// use std::io::prelude::*;
+///
+/// let mut stream = UnixStream::connect("/path/to/my/socket").unwrap();
+/// stream.write_all(b"hello world").unwrap();
+/// let mut response = String::new();
+/// stream.read_to_string(&mut response).unwrap();
+/// println!("{}", response);
+/// ```
+#[stable(feature = "unix_socket", since = "1.10.0")]
+pub struct UnixStream(Socket);
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl fmt::Debug for UnixStream {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let mut builder = fmt.debug_struct("UnixStream");
+ builder.field("fd", self.0.as_inner());
+ if let Ok(addr) = self.local_addr() {
+ builder.field("local", &addr);
+ }
+ if let Ok(addr) = self.peer_addr() {
+ builder.field("peer", &addr);
+ }
+ builder.finish()
+ }
+}
+
+impl UnixStream {
+ /// Connects to the socket named by `path`.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// let socket = match UnixStream::connect("/tmp/sock") {
+ /// Ok(sock) => sock,
+ /// Err(e) => {
+ /// println!("Couldn't connect: {:?}", e);
+ /// return
+ /// }
+ /// };
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> {
+ fn inner(path: &Path) -> io::Result<UnixStream> {
+ unsafe {
+ let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
+ let (addr, len) = sockaddr_un(path)?;
+
+ cvt(libc::connect(*inner.as_inner(), &addr as *const _ as *const _, len))?;
+ Ok(UnixStream(inner))
+ }
+ }
+ inner(path.as_ref())
+ }
+
+ /// Creates an unnamed pair of connected sockets.
+ ///
+ /// Returns two `UnixStream`s which are connected to each other.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// let (sock1, sock2) = match UnixStream::pair() {
+ /// Ok((sock1, sock2)) => (sock1, sock2),
+ /// Err(e) => {
+ /// println!("Couldn't create a pair of sockets: {:?}", e);
+ /// return
+ /// }
+ /// };
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn pair() -> io::Result<(UnixStream, UnixStream)> {
+ let (i1, i2) = Socket::new_pair(libc::AF_UNIX, libc::SOCK_STREAM)?;
+ Ok((UnixStream(i1), UnixStream(i2)))
+ }
+
+ /// Creates a new independently owned handle to the underlying socket.
+ ///
+ /// The returned `UnixStream` is a reference to the same stream that this
+ /// object references. Both handles will read and write the same stream of
+ /// data, and options set on one stream will be propagated to the other
+ /// stream.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// let sock_copy = socket.try_clone().expect("Couldn't clone socket");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn try_clone(&self) -> io::Result<UnixStream> {
+ self.0.duplicate().map(UnixStream)
+ }
+
+ /// Returns the socket address of the local half of this connection.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn local_addr(&self) -> io::Result<SocketAddr> {
+ SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) })
+ }
+
+ /// Returns the socket address of the remote half of this connection.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// let addr = socket.peer_addr().expect("Couldn't get peer address");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) })
+ }
+
+ /// Sets the read timeout for the socket.
+ ///
+ /// If the provided value is [`None`], then [`read`] calls will block
+ /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is passed to this
+ /// method.
+ ///
+ /// [`None`]: ../../../../std/option/enum.Option.html#variant.None
+ /// [`Err`]: ../../../../std/result/enum.Result.html#variant.Err
+ /// [`read`]: ../../../../std/io/trait.Read.html#tymethod.read
+ /// [`Duration`]: ../../../../std/time/struct.Duration.html
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout");
+ /// ```
+ ///
+ /// An [`Err`] is returned if the zero [`Duration`] is passed to this
+ /// method:
+ ///
+ /// ```no_run
+ /// use std::io;
+ /// use std::os::unix::net::UnixStream;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// let result = socket.set_read_timeout(Some(Duration::new(0, 0)));
+ /// let err = result.unwrap_err();
+ /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
+ self.0.set_timeout(timeout, libc::SO_RCVTIMEO)
+ }
+
+ /// Sets the write timeout for the socket.
+ ///
+ /// If the provided value is [`None`], then [`write`] calls will block
+ /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is
+ /// passed to this method.
+ ///
+ /// [`None`]: ../../../../std/option/enum.Option.html#variant.None
+ /// [`Err`]: ../../../../std/result/enum.Result.html#variant.Err
+ /// [`write`]: ../../../../std/io/trait.Write.html#tymethod.write
+ /// [`Duration`]: ../../../../std/time/struct.Duration.html
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout");
+ /// ```
+ ///
+ /// An [`Err`] is returned if the zero [`Duration`] is passed to this
+ /// method:
+ ///
+ /// ```no_run
+ /// use std::io;
+ /// use std::net::UdpSocket;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UdpSocket::bind("127.0.0.1:34254").unwrap();
+ /// let result = socket.set_write_timeout(Some(Duration::new(0, 0)));
+ /// let err = result.unwrap_err();
+ /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
+ self.0.set_timeout(timeout, libc::SO_SNDTIMEO)
+ }
+
+ /// Returns the read timeout of this socket.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout");
+ /// assert_eq!(socket.read_timeout().unwrap(), Some(Duration::new(1, 0)));
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ self.0.timeout(libc::SO_RCVTIMEO)
+ }
+
+ /// Returns the write timeout of this socket.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout");
+ /// assert_eq!(socket.write_timeout().unwrap(), Some(Duration::new(1, 0)));
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ self.0.timeout(libc::SO_SNDTIMEO)
+ }
+
+ /// Moves the socket into or out of nonblocking mode.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// socket.set_nonblocking(true).expect("Couldn't set nonblocking");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ self.0.set_nonblocking(nonblocking)
+ }
+
+ /// Returns the value of the `SO_ERROR` option.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// if let Ok(Some(err)) = socket.take_error() {
+ /// println!("Got error: {:?}", err);
+ /// }
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ self.0.take_error()
+ }
+
+ /// Shuts down the read, write, or both halves of this connection.
+ ///
+ /// This function will cause all pending and future I/O calls on the
+ /// specified portions to immediately return with an appropriate value
+ /// (see the documentation of [`Shutdown`]).
+ ///
+ /// [`Shutdown`]: ../../../../std/net/enum.Shutdown.html
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ /// use std::net::Shutdown;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// socket.shutdown(Shutdown::Both).expect("shutdown function failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+ self.0.shutdown(how)
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl io::Read for UnixStream {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ io::Read::read(&mut &*self, buf)
+ }
+
+ #[inline]
+ unsafe fn initializer(&self) -> Initializer {
+ Initializer::nop()
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl<'a> io::Read for &'a UnixStream {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+
+ #[inline]
+ unsafe fn initializer(&self) -> Initializer {
+ Initializer::nop()
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl io::Write for UnixStream {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ io::Write::write(&mut &*self, buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ io::Write::flush(&mut &*self)
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl<'a> io::Write for &'a UnixStream {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl AsRawFd for UnixStream {
+ fn as_raw_fd(&self) -> RawFd {
+ *self.0.as_inner()
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl FromRawFd for UnixStream {
+ unsafe fn from_raw_fd(fd: RawFd) -> UnixStream {
+ UnixStream(Socket::from_inner(fd))
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl IntoRawFd for UnixStream {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_inner()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl AsRawFd for net::TcpStream {
+ fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl AsRawFd for net::TcpListener {
+ fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl AsRawFd for net::UdpSocket {
+ fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() }
+}
+
+#[stable(feature = "from_raw_os", since = "1.1.0")]
+impl FromRawFd for net::TcpStream {
+ unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream {
+ let socket = sys::net::Socket::from_inner(fd);
+ net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(socket))
+ }
+}
+
+#[stable(feature = "from_raw_os", since = "1.1.0")]
+impl FromRawFd for net::TcpListener {
+ unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener {
+ let socket = sys::net::Socket::from_inner(fd);
+ net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(socket))
+ }
+}
+
+#[stable(feature = "from_raw_os", since = "1.1.0")]
+impl FromRawFd for net::UdpSocket {
+ unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket {
+ let socket = sys::net::Socket::from_inner(fd);
+ net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(socket))
+ }
+}
+
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for net::TcpStream {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_socket().into_inner()
+ }
+}
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for net::TcpListener {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_socket().into_inner()
+ }
+}
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for net::UdpSocket {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_socket().into_inner()
+ }
+}
+
+/// A structure representing a Unix domain socket server.
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::thread;
+/// use std::os::unix::net::{UnixStream, UnixListener};
+///
+/// fn handle_client(stream: UnixStream) {
+/// // ...
+/// }
+///
+/// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+///
+/// // accept connections and process them, spawning a new thread for each one
+/// for stream in listener.incoming() {
+/// match stream {
+/// Ok(stream) => {
+/// /* connection succeeded */
+/// thread::spawn(|| handle_client(stream));
+/// }
+/// Err(err) => {
+/// /* connection failed */
+/// break;
+/// }
+/// }
+/// }
+/// ```
+#[stable(feature = "unix_socket", since = "1.10.0")]
+pub struct UnixListener(Socket);
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl fmt::Debug for UnixListener {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let mut builder = fmt.debug_struct("UnixListener");
+ builder.field("fd", self.0.as_inner());
+ if let Ok(addr) = self.local_addr() {
+ builder.field("local", &addr);
+ }
+ builder.finish()
+ }
+}
+
+impl UnixListener {
+ /// Creates a new `UnixListener` bound to the specified socket.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ ///
+ /// let listener = match UnixListener::bind("/path/to/the/socket") {
+ /// Ok(sock) => sock,
+ /// Err(e) => {
+ /// println!("Couldn't connect: {:?}", e);
+ /// return
+ /// }
+ /// };
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> {
+ fn inner(path: &Path) -> io::Result<UnixListener> {
+ unsafe {
+ let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
+ let (addr, len) = sockaddr_un(path)?;
+
+ cvt(libc::bind(*inner.as_inner(), &addr as *const _ as *const _, len as _))?;
+ cvt(libc::listen(*inner.as_inner(), 128))?;
+
+ Ok(UnixListener(inner))
+ }
+ }
+ inner(path.as_ref())
+ }
+
+ /// Accepts a new incoming connection to this listener.
+ ///
+ /// This function will block the calling thread until a new Unix connection
+ /// is established. When established, the corresponding [`UnixStream`] and
+ /// the remote peer's address will be returned.
+ ///
+ /// [`UnixStream`]: ../../../../std/os/unix/net/struct.UnixStream.html
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ ///
+ /// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+ ///
+ /// match listener.accept() {
+ /// Ok((socket, addr)) => println!("Got a client: {:?}", addr),
+ /// Err(e) => println!("accept function failed: {:?}", e),
+ /// }
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> {
+ let mut storage: libc::sockaddr_un = unsafe { mem::zeroed() };
+ let mut len = mem::size_of_val(&storage) as libc::socklen_t;
+ let sock = self.0.accept(&mut storage as *mut _ as *mut _, &mut len)?;
+ let addr = SocketAddr::from_parts(storage, len)?;
+ Ok((UnixStream(sock), addr))
+ }
+
+ /// Creates a new independently owned handle to the underlying socket.
+ ///
+ /// The returned `UnixListener` is a reference to the same socket that this
+ /// object references. Both handles can be used to accept incoming
+ /// connections and options set on one listener will affect the other.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ ///
+ /// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+ ///
+ /// let listener_copy = listener.try_clone().expect("try_clone failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn try_clone(&self) -> io::Result<UnixListener> {
+ self.0.duplicate().map(UnixListener)
+ }
+
+ /// Returns the local socket address of this listener.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ ///
+ /// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+ ///
+ /// let addr = listener.local_addr().expect("Couldn't get local address");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn local_addr(&self) -> io::Result<SocketAddr> {
+ SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) })
+ }
+
+ /// Moves the socket into or out of nonblocking mode.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ ///
+ /// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+ ///
+ /// listener.set_nonblocking(true).expect("Couldn't set non blocking");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ self.0.set_nonblocking(nonblocking)
+ }
+
+ /// Returns the value of the `SO_ERROR` option.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ ///
+ /// let listener = UnixListener::bind("/tmp/sock").unwrap();
+ ///
+ /// if let Ok(Some(err)) = listener.take_error() {
+ /// println!("Got error: {:?}", err);
+ /// }
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ self.0.take_error()
+ }
+
+ /// Returns an iterator over incoming connections.
+ ///
+ /// The iterator will never return [`None`] and will also not yield the
+ /// peer's [`SocketAddr`] structure.
+ ///
+ /// [`None`]: ../../../../std/option/enum.Option.html#variant.None
+ /// [`SocketAddr`]: struct.SocketAddr.html
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::thread;
+ /// use std::os::unix::net::{UnixStream, UnixListener};
+ ///
+ /// fn handle_client(stream: UnixStream) {
+ /// // ...
+ /// }
+ ///
+ /// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+ ///
+ /// for stream in listener.incoming() {
+ /// match stream {
+ /// Ok(stream) => {
+ /// thread::spawn(|| handle_client(stream));
+ /// }
+ /// Err(err) => {
+ /// break;
+ /// }
+ /// }
+ /// }
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn incoming<'a>(&'a self) -> Incoming<'a> {
+ Incoming { listener: self }
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl AsRawFd for UnixListener {
+ fn as_raw_fd(&self) -> RawFd {
+ *self.0.as_inner()
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl FromRawFd for UnixListener {
+ unsafe fn from_raw_fd(fd: RawFd) -> UnixListener {
+ UnixListener(Socket::from_inner(fd))
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl IntoRawFd for UnixListener {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_inner()
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl<'a> IntoIterator for &'a UnixListener {
+ type Item = io::Result<UnixStream>;
+ type IntoIter = Incoming<'a>;
+
+ fn into_iter(self) -> Incoming<'a> {
+ self.incoming()
+ }
+}
+
+/// An iterator over incoming connections to a [`UnixListener`].
+///
+/// It will never return [`None`].
+///
+/// [`None`]: ../../../../std/option/enum.Option.html#variant.None
+/// [`UnixListener`]: struct.UnixListener.html
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::thread;
+/// use std::os::unix::net::{UnixStream, UnixListener};
+///
+/// fn handle_client(stream: UnixStream) {
+/// // ...
+/// }
+///
+/// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+///
+/// for stream in listener.incoming() {
+/// match stream {
+/// Ok(stream) => {
+/// thread::spawn(|| handle_client(stream));
+/// }
+/// Err(err) => {
+/// break;
+/// }
+/// }
+/// }
+/// ```
+#[derive(Debug)]
+#[stable(feature = "unix_socket", since = "1.10.0")]
+pub struct Incoming<'a> {
+ listener: &'a UnixListener,
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl<'a> Iterator for Incoming<'a> {
+ type Item = io::Result<UnixStream>;
+
+ fn next(&mut self) -> Option<io::Result<UnixStream>> {
+ Some(self.listener.accept().map(|s| s.0))
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ (usize::max_value(), None)
+ }
+}
+
+/// A Unix datagram socket.
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::os::unix::net::UnixDatagram;
+///
+/// let socket = UnixDatagram::bind("/path/to/my/socket").unwrap();
+/// socket.send_to(b"hello world", "/path/to/other/socket").unwrap();
+/// let mut buf = [0; 100];
+/// let (count, address) = socket.recv_from(&mut buf).unwrap();
+/// println!("socket {:?} sent {:?}", address, &buf[..count]);
+/// ```
+#[stable(feature = "unix_socket", since = "1.10.0")]
+pub struct UnixDatagram(Socket);
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl fmt::Debug for UnixDatagram {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let mut builder = fmt.debug_struct("UnixDatagram");
+ builder.field("fd", self.0.as_inner());
+ if let Ok(addr) = self.local_addr() {
+ builder.field("local", &addr);
+ }
+ if let Ok(addr) = self.peer_addr() {
+ builder.field("peer", &addr);
+ }
+ builder.finish()
+ }
+}
+
+impl UnixDatagram {
+ /// Creates a Unix datagram socket bound to the given path.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = match UnixDatagram::bind("/path/to/the/socket") {
+ /// Ok(sock) => sock,
+ /// Err(e) => {
+ /// println!("Couldn't bind: {:?}", e);
+ /// return
+ /// }
+ /// };
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixDatagram> {
+ fn inner(path: &Path) -> io::Result<UnixDatagram> {
+ unsafe {
+ let socket = UnixDatagram::unbound()?;
+ let (addr, len) = sockaddr_un(path)?;
+
+ cvt(libc::bind(*socket.0.as_inner(), &addr as *const _ as *const _, len as _))?;
+
+ Ok(socket)
+ }
+ }
+ inner(path.as_ref())
+ }
+
+ /// Creates a Unix Datagram socket which is not bound to any address.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = match UnixDatagram::unbound() {
+ /// Ok(sock) => sock,
+ /// Err(e) => {
+ /// println!("Couldn't unbound: {:?}", e);
+ /// return
+ /// }
+ /// };
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn unbound() -> io::Result<UnixDatagram> {
+ let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_DGRAM)?;
+ Ok(UnixDatagram(inner))
+ }
+
+ /// Create an unnamed pair of connected sockets.
+ ///
+ /// Returns two `UnixDatagrams`s which are connected to each other.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let (sock1, sock2) = match UnixDatagram::pair() {
+ /// Ok((sock1, sock2)) => (sock1, sock2),
+ /// Err(e) => {
+ /// println!("Couldn't unbound: {:?}", e);
+ /// return
+ /// }
+ /// };
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> {
+ let (i1, i2) = Socket::new_pair(libc::AF_UNIX, libc::SOCK_DGRAM)?;
+ Ok((UnixDatagram(i1), UnixDatagram(i2)))
+ }
+
+ /// Connects the socket to the specified address.
+ ///
+ /// The [`send`] method may be used to send data to the specified address.
+ /// [`recv`] and [`recv_from`] will only receive data from that address.
+ ///
+ /// [`send`]: #method.send
+ /// [`recv`]: #method.recv
+ /// [`recv_from`]: #method.recv_from
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// match sock.connect("/path/to/the/socket") {
+ /// Ok(sock) => sock,
+ /// Err(e) => {
+ /// println!("Couldn't connect: {:?}", e);
+ /// return
+ /// }
+ /// };
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn connect<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
+ fn inner(d: &UnixDatagram, path: &Path) -> io::Result<()> {
+ unsafe {
+ let (addr, len) = sockaddr_un(path)?;
+
+ cvt(libc::connect(*d.0.as_inner(), &addr as *const _ as *const _, len))?;
+
+ Ok(())
+ }
+ }
+ inner(self, path.as_ref())
+ }
+
+ /// Creates a new independently owned handle to the underlying socket.
+ ///
+ /// The returned `UnixDatagram` is a reference to the same socket that this
+ /// object references. Both handles can be used to accept incoming
+ /// connections and options set on one side will affect the other.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap();
+ ///
+ /// let sock_copy = sock.try_clone().expect("try_clone failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn try_clone(&self) -> io::Result<UnixDatagram> {
+ self.0.duplicate().map(UnixDatagram)
+ }
+
+ /// Returns the address of this socket.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap();
+ ///
+ /// let addr = sock.local_addr().expect("Couldn't get local address");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn local_addr(&self) -> io::Result<SocketAddr> {
+ SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) })
+ }
+
+ /// Returns the address of this socket's peer.
+ ///
+ /// The [`connect`] method will connect the socket to a peer.
+ ///
+ /// [`connect`]: #method.connect
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.connect("/path/to/the/socket").unwrap();
+ ///
+ /// let addr = sock.peer_addr().expect("Couldn't get peer address");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) })
+ }
+
+ /// Receives data from the socket.
+ ///
+ /// On success, returns the number of bytes read and the address from
+ /// whence the data came.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// let mut buf = vec![0; 10];
+ /// match sock.recv_from(buf.as_mut_slice()) {
+ /// Ok((size, sender)) => println!("received {} bytes from {:?}", size, sender),
+ /// Err(e) => println!("recv_from function failed: {:?}", e),
+ /// }
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ let mut count = 0;
+ let addr = SocketAddr::new(|addr, len| {
+ unsafe {
+ count = libc::recvfrom(*self.0.as_inner(),
+ buf.as_mut_ptr() as *mut _,
+ buf.len(),
+ 0,
+ addr,
+ len);
+ if count > 0 {
+ 1
+ } else if count == 0 {
+ 0
+ } else {
+ -1
+ }
+ }
+ })?;
+
+ Ok((count as usize, addr))
+ }
+
+ /// Receives data from the socket.
+ ///
+ /// On success, returns the number of bytes read.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap();
+ /// let mut buf = vec![0; 10];
+ /// sock.recv(buf.as_mut_slice()).expect("recv function failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+
+ /// Sends data on the socket to the specified address.
+ ///
+ /// On success, returns the number of bytes written.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.send_to(b"omelette au fromage", "/some/sock").expect("send_to function failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn send_to<P: AsRef<Path>>(&self, buf: &[u8], path: P) -> io::Result<usize> {
+ fn inner(d: &UnixDatagram, buf: &[u8], path: &Path) -> io::Result<usize> {
+ unsafe {
+ let (addr, len) = sockaddr_un(path)?;
+
+ let count = cvt(libc::sendto(*d.0.as_inner(),
+ buf.as_ptr() as *const _,
+ buf.len(),
+ MSG_NOSIGNAL,
+ &addr as *const _ as *const _,
+ len))?;
+ Ok(count as usize)
+ }
+ }
+ inner(self, buf, path.as_ref())
+ }
+
+ /// Sends data on the socket to the socket's peer.
+ ///
+ /// The peer address may be set by the `connect` method, and this method
+ /// will return an error if the socket has not already been connected.
+ ///
+ /// On success, returns the number of bytes written.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.connect("/some/sock").expect("Couldn't connect");
+ /// sock.send(b"omelette au fromage").expect("send_to function failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ /// Sets the read timeout for the socket.
+ ///
+ /// If the provided value is [`None`], then [`recv`] and [`recv_from`] calls will
+ /// block indefinitely. An [`Err`] is returned if the zero [`Duration`]
+ /// is passed to this method.
+ ///
+ /// [`None`]: ../../../../std/option/enum.Option.html#variant.None
+ /// [`Err`]: ../../../../std/result/enum.Result.html#variant.Err
+ /// [`recv`]: #method.recv
+ /// [`recv_from`]: #method.recv_from
+ /// [`Duration`]: ../../../../std/time/struct.Duration.html
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::os::unix::net::UnixDatagram;
+ /// use std::time::Duration;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.set_read_timeout(Some(Duration::new(1, 0))).expect("set_read_timeout function failed");
+ /// ```
+ ///
+ /// An [`Err`] is returned if the zero [`Duration`] is passed to this
+ /// method:
+ ///
+ /// ```no_run
+ /// use std::io;
+ /// use std::os::unix::net::UnixDatagram;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UnixDatagram::unbound().unwrap();
+ /// let result = socket.set_read_timeout(Some(Duration::new(0, 0)));
+ /// let err = result.unwrap_err();
+ /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
+ self.0.set_timeout(timeout, libc::SO_RCVTIMEO)
+ }
+
+ /// Sets the write timeout for the socket.
+ ///
+ /// If the provided value is [`None`], then [`send`] and [`send_to`] calls will
+ /// block indefinitely. An [`Err`] is returned if the zero [`Duration`] is passed to this
+ /// method.
+ ///
+ /// [`None`]: ../../../../std/option/enum.Option.html#variant.None
+ /// [`send`]: #method.send
+ /// [`send_to`]: #method.send_to
+ /// [`Duration`]: ../../../../std/time/struct.Duration.html
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::os::unix::net::UnixDatagram;
+ /// use std::time::Duration;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.set_write_timeout(Some(Duration::new(1, 0)))
+ /// .expect("set_write_timeout function failed");
+ /// ```
+ ///
+ /// An [`Err`] is returned if the zero [`Duration`] is passed to this
+ /// method:
+ ///
+ /// ```no_run
+ /// use std::io;
+ /// use std::os::unix::net::UnixDatagram;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UnixDatagram::unbound().unwrap();
+ /// let result = socket.set_write_timeout(Some(Duration::new(0, 0)));
+ /// let err = result.unwrap_err();
+ /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
+ self.0.set_timeout(timeout, libc::SO_SNDTIMEO)
+ }
+
+ /// Returns the read timeout of this socket.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::os::unix::net::UnixDatagram;
+ /// use std::time::Duration;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.set_read_timeout(Some(Duration::new(1, 0))).expect("set_read_timeout function failed");
+ /// assert_eq!(sock.read_timeout().unwrap(), Some(Duration::new(1, 0)));
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ self.0.timeout(libc::SO_RCVTIMEO)
+ }
+
+ /// Returns the write timeout of this socket.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::os::unix::net::UnixDatagram;
+ /// use std::time::Duration;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.set_write_timeout(Some(Duration::new(1, 0)))
+ /// .expect("set_write_timeout function failed");
+ /// assert_eq!(sock.write_timeout().unwrap(), Some(Duration::new(1, 0)));
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ self.0.timeout(libc::SO_SNDTIMEO)
+ }
+
+ /// Moves the socket into or out of nonblocking mode.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.set_nonblocking(true).expect("set_nonblocking function failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ self.0.set_nonblocking(nonblocking)
+ }
+
+ /// Returns the value of the `SO_ERROR` option.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// if let Ok(Some(err)) = sock.take_error() {
+ /// println!("Got error: {:?}", err);
+ /// }
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ self.0.take_error()
+ }
+
+ /// Shut down the read, write, or both halves of this connection.
+ ///
+ /// This function will cause all pending and future I/O calls on the
+ /// specified portions to immediately return with an appropriate value
+ /// (see the documentation of [`Shutdown`]).
+ ///
+ /// [`Shutdown`]: ../../../../std/net/enum.Shutdown.html
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ /// use std::net::Shutdown;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.shutdown(Shutdown::Both).expect("shutdown function failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+ self.0.shutdown(how)
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl AsRawFd for UnixDatagram {
+ fn as_raw_fd(&self) -> RawFd {
+ *self.0.as_inner()
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl FromRawFd for UnixDatagram {
+ unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram {
+ UnixDatagram(Socket::from_inner(fd))
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl IntoRawFd for UnixDatagram {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_inner()
+ }
+}
+
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod test {
+ use thread;
+ use io::{self, ErrorKind};
+ use io::prelude::*;
+ use time::Duration;
+ use sys_common::io::test::tmpdir;
+
+ use super::*;
+
+ macro_rules! or_panic {
+ ($e:expr) => {
+ match $e {
+ Ok(e) => e,
+ Err(e) => panic!("{}", e),
+ }
+ }
+ }
+
+ #[test]
+ fn basic() {
+ let dir = tmpdir();
+ let socket_path = dir.path().join("sock");
+ let msg1 = b"hello";
+ let msg2 = b"world!";
+
+ let listener = or_panic!(UnixListener::bind(&socket_path));
+ let thread = thread::spawn(move || {
+ let mut stream = or_panic!(listener.accept()).0;
+ let mut buf = [0; 5];
+ or_panic!(stream.read(&mut buf));
+ assert_eq!(&msg1[..], &buf[..]);
+ or_panic!(stream.write_all(msg2));
+ });
+
+ let mut stream = or_panic!(UnixStream::connect(&socket_path));
+ assert_eq!(Some(&*socket_path),
+ stream.peer_addr().unwrap().as_pathname());
+ or_panic!(stream.write_all(msg1));
+ let mut buf = vec![];
+ or_panic!(stream.read_to_end(&mut buf));
+ assert_eq!(&msg2[..], &buf[..]);
+ drop(stream);
+
+ thread.join().unwrap();
+ }
+
+ #[test]
+ fn pair() {
+ let msg1 = b"hello";
+ let msg2 = b"world!";
+
+ let (mut s1, mut s2) = or_panic!(UnixStream::pair());
+ let thread = thread::spawn(move || {
+ // s1 must be moved in or the test will hang!
+ let mut buf = [0; 5];
+ or_panic!(s1.read(&mut buf));
+ assert_eq!(&msg1[..], &buf[..]);
+ or_panic!(s1.write_all(msg2));
+ });
+
+ or_panic!(s2.write_all(msg1));
+ let mut buf = vec![];
+ or_panic!(s2.read_to_end(&mut buf));
+ assert_eq!(&msg2[..], &buf[..]);
+ drop(s2);
+
+ thread.join().unwrap();
+ }
+
+ #[test]
+ fn try_clone() {
+ let dir = tmpdir();
+ let socket_path = dir.path().join("sock");
+ let msg1 = b"hello";
+ let msg2 = b"world";
+
+ let listener = or_panic!(UnixListener::bind(&socket_path));
+ let thread = thread::spawn(move || {
+ let mut stream = or_panic!(listener.accept()).0;
+ or_panic!(stream.write_all(msg1));
+ or_panic!(stream.write_all(msg2));
+ });
+
+ let mut stream = or_panic!(UnixStream::connect(&socket_path));
+ let mut stream2 = or_panic!(stream.try_clone());
+
+ let mut buf = [0; 5];
+ or_panic!(stream.read(&mut buf));
+ assert_eq!(&msg1[..], &buf[..]);
+ or_panic!(stream2.read(&mut buf));
+ assert_eq!(&msg2[..], &buf[..]);
+
+ thread.join().unwrap();
+ }
+
+ #[test]
+ fn iter() {
+ let dir = tmpdir();
+ let socket_path = dir.path().join("sock");
+
+ let listener = or_panic!(UnixListener::bind(&socket_path));
+ let thread = thread::spawn(move || {
+ for stream in listener.incoming().take(2) {
+ let mut stream = or_panic!(stream);
+ let mut buf = [0];
+ or_panic!(stream.read(&mut buf));
+ }
+ });
+
+ for _ in 0..2 {
+ let mut stream = or_panic!(UnixStream::connect(&socket_path));
+ or_panic!(stream.write_all(&[0]));
+ }
+
+ thread.join().unwrap();
+ }
+
+ #[test]
+ fn long_path() {
+ let dir = tmpdir();
+ let socket_path = dir.path()
+ .join("asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfa\
+ sasdfasdfasdasdfasdfasdfadfasdfasdfasdfasdfasdf");
+ match UnixStream::connect(&socket_path) {
+ Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
+ Err(e) => panic!("unexpected error {}", e),
+ Ok(_) => panic!("unexpected success"),
+ }
+
+ match UnixListener::bind(&socket_path) {
+ Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
+ Err(e) => panic!("unexpected error {}", e),
+ Ok(_) => panic!("unexpected success"),
+ }
+
+ match UnixDatagram::bind(&socket_path) {
+ Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
+ Err(e) => panic!("unexpected error {}", e),
+ Ok(_) => panic!("unexpected success"),
+ }
+ }
+
+ #[test]
+ fn timeouts() {
+ let dir = tmpdir();
+ let socket_path = dir.path().join("sock");
+
+ let _listener = or_panic!(UnixListener::bind(&socket_path));
+
+ let stream = or_panic!(UnixStream::connect(&socket_path));
+ let dur = Duration::new(15410, 0);
+
+ assert_eq!(None, or_panic!(stream.read_timeout()));
+
+ or_panic!(stream.set_read_timeout(Some(dur)));
+ assert_eq!(Some(dur), or_panic!(stream.read_timeout()));
+
+ assert_eq!(None, or_panic!(stream.write_timeout()));
+
+ or_panic!(stream.set_write_timeout(Some(dur)));
+ assert_eq!(Some(dur), or_panic!(stream.write_timeout()));
+
+ or_panic!(stream.set_read_timeout(None));
+ assert_eq!(None, or_panic!(stream.read_timeout()));
+
+ or_panic!(stream.set_write_timeout(None));
+ assert_eq!(None, or_panic!(stream.write_timeout()));
+ }
+
+ #[test]
+ fn test_read_timeout() {
+ let dir = tmpdir();
+ let socket_path = dir.path().join("sock");
+
+ let _listener = or_panic!(UnixListener::bind(&socket_path));
+
+ let mut stream = or_panic!(UnixStream::connect(&socket_path));
+ or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
+
+ let mut buf = [0; 10];
+ let kind = stream.read(&mut buf).err().expect("expected error").kind();
+ assert!(kind == io::ErrorKind::WouldBlock || kind == io::ErrorKind::TimedOut);
+ }
+
+ #[test]
+ fn test_read_with_timeout() {
+ let dir = tmpdir();
+ let socket_path = dir.path().join("sock");
+
+ let listener = or_panic!(UnixListener::bind(&socket_path));
+
+ let mut stream = or_panic!(UnixStream::connect(&socket_path));
+ or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
+
+ let mut other_end = or_panic!(listener.accept()).0;
+ or_panic!(other_end.write_all(b"hello world"));
+
+ let mut buf = [0; 11];
+ or_panic!(stream.read(&mut buf));
+ assert_eq!(b"hello world", &buf[..]);
+
+ let kind = stream.read(&mut buf).err().expect("expected error").kind();
+ assert!(kind == io::ErrorKind::WouldBlock || kind == io::ErrorKind::TimedOut);
+ }
+
+ // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
+ // when passed zero Durations
+ #[test]
+ fn test_unix_stream_timeout_zero_duration() {
+ let dir = tmpdir();
+ let socket_path = dir.path().join("sock");
+
+ let listener = or_panic!(UnixListener::bind(&socket_path));
+ let stream = or_panic!(UnixStream::connect(&socket_path));
+
+ let result = stream.set_write_timeout(Some(Duration::new(0, 0)));
+ let err = result.unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+ let result = stream.set_read_timeout(Some(Duration::new(0, 0)));
+ let err = result.unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+ drop(listener);
+ }
+
+ #[test]
+ fn test_unix_datagram() {
+ let dir = tmpdir();
+ let path1 = dir.path().join("sock1");
+ let path2 = dir.path().join("sock2");
+
+ let sock1 = or_panic!(UnixDatagram::bind(&path1));
+ let sock2 = or_panic!(UnixDatagram::bind(&path2));
+
+ let msg = b"hello world";
+ or_panic!(sock1.send_to(msg, &path2));
+ let mut buf = [0; 11];
+ or_panic!(sock2.recv_from(&mut buf));
+ assert_eq!(msg, &buf[..]);
+ }
+
+ #[test]
+ fn test_unnamed_unix_datagram() {
+ let dir = tmpdir();
+ let path1 = dir.path().join("sock1");
+
+ let sock1 = or_panic!(UnixDatagram::bind(&path1));
+ let sock2 = or_panic!(UnixDatagram::unbound());
+
+ let msg = b"hello world";
+ or_panic!(sock2.send_to(msg, &path1));
+ let mut buf = [0; 11];
+ let (usize, addr) = or_panic!(sock1.recv_from(&mut buf));
+ assert_eq!(usize, 11);
+ assert!(addr.is_unnamed());
+ assert_eq!(msg, &buf[..]);
+ }
+
+ #[test]
+ fn test_connect_unix_datagram() {
+ let dir = tmpdir();
+ let path1 = dir.path().join("sock1");
+ let path2 = dir.path().join("sock2");
+
+ let bsock1 = or_panic!(UnixDatagram::bind(&path1));
+ let bsock2 = or_panic!(UnixDatagram::bind(&path2));
+ let sock = or_panic!(UnixDatagram::unbound());
+ or_panic!(sock.connect(&path1));
+
+ // Check send()
+ let msg = b"hello there";
+ or_panic!(sock.send(msg));
+ let mut buf = [0; 11];
+ let (usize, addr) = or_panic!(bsock1.recv_from(&mut buf));
+ assert_eq!(usize, 11);
+ assert!(addr.is_unnamed());
+ assert_eq!(msg, &buf[..]);
+
+ // Changing default socket works too
+ or_panic!(sock.connect(&path2));
+ or_panic!(sock.send(msg));
+ or_panic!(bsock2.recv_from(&mut buf));
+ }
+
+ #[test]
+ fn test_unix_datagram_recv() {
+ let dir = tmpdir();
+ let path1 = dir.path().join("sock1");
+
+ let sock1 = or_panic!(UnixDatagram::bind(&path1));
+ let sock2 = or_panic!(UnixDatagram::unbound());
+ or_panic!(sock2.connect(&path1));
+
+ let msg = b"hello world";
+ or_panic!(sock2.send(msg));
+ let mut buf = [0; 11];
+ let size = or_panic!(sock1.recv(&mut buf));
+ assert_eq!(size, 11);
+ assert_eq!(msg, &buf[..]);
+ }
+
+ #[test]
+ fn datagram_pair() {
+ let msg1 = b"hello";
+ let msg2 = b"world!";
+
+ let (s1, s2) = or_panic!(UnixDatagram::pair());
+ let thread = thread::spawn(move || {
+ // s1 must be moved in or the test will hang!
+ let mut buf = [0; 5];
+ or_panic!(s1.recv(&mut buf));
+ assert_eq!(&msg1[..], &buf[..]);
+ or_panic!(s1.send(msg2));
+ });
+
+ or_panic!(s2.send(msg1));
+ let mut buf = [0; 6];
+ or_panic!(s2.recv(&mut buf));
+ assert_eq!(&msg2[..], &buf[..]);
+ drop(s2);
+
+ thread.join().unwrap();
+ }
+
+ // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
+ // when passed zero Durations
+ #[test]
+ fn test_unix_datagram_timeout_zero_duration() {
+ let dir = tmpdir();
+ let path = dir.path().join("sock");
+
+ let datagram = or_panic!(UnixDatagram::bind(&path));
+
+ let result = datagram.set_write_timeout(Some(Duration::new(0, 0)));
+ let err = result.unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+ let result = datagram.set_read_timeout(Some(Duration::new(0, 0)));
+ let err = result.unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::InvalidInput);
+ }
+
+ #[test]
+ fn abstract_namespace_not_allowed() {
+ assert!(UnixStream::connect("\0asdf").is_err());
+ }
+}
diff --git a/ctr-std/src/sys/unix/ext/process.rs b/ctr-std/src/sys/unix/ext/process.rs
new file mode 100644
index 0000000..21630ae
--- /dev/null
+++ b/ctr-std/src/sys/unix/ext/process.rs
@@ -0,0 +1,203 @@
+// 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.
+
+//! Unix-specific extensions to primitives in the `std::process` module.
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+use io;
+use os::unix::io::{FromRawFd, RawFd, AsRawFd, IntoRawFd};
+use process;
+use sys;
+use sys_common::{AsInnerMut, AsInner, FromInner, IntoInner};
+
+/// Unix-specific extensions to the [`process::Command`] builder.
+///
+/// [`process::Command`]: ../../../../std/process/struct.Command.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub trait CommandExt {
+ /// Sets the child process's user id. This translates to a
+ /// `setuid` call in the child process. Failure in the `setuid`
+ /// call will cause the spawn to fail.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn uid(&mut self, id: u32) -> &mut process::Command;
+
+ /// Similar to `uid`, but sets the group id of the child process. This has
+ /// the same semantics as the `uid` field.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn gid(&mut self, id: u32) -> &mut process::Command;
+
+ /// Schedules a closure to be run just before the `exec` function is
+ /// invoked.
+ ///
+ /// The closure is allowed to return an I/O error whose OS error code will
+ /// be communicated back to the parent and returned as an error from when
+ /// the spawn was requested.
+ ///
+ /// Multiple closures can be registered and they will be called in order of
+ /// their registration. If a closure returns `Err` then no further closures
+ /// will be called and the spawn operation will immediately return with a
+ /// failure.
+ ///
+ /// # Notes
+ ///
+ /// This closure will be run in the context of the child process after a
+ /// `fork`. This primarily means that any modifications made to memory on
+ /// behalf of this closure will **not** be visible to the parent process.
+ /// This is often a very constrained environment where normal operations
+ /// like `malloc` or acquiring a mutex are not guaranteed to work (due to
+ /// other threads perhaps still running when the `fork` was run).
+ ///
+ /// When this closure is run, aspects such as the stdio file descriptors and
+ /// working directory have successfully been changed, so output to these
+ /// locations may not appear where intended.
+ #[stable(feature = "process_exec", since = "1.15.0")]
+ fn before_exec<F>(&mut self, f: F) -> &mut process::Command
+ where F: FnMut() -> io::Result<()> + Send + Sync + 'static;
+
+ /// Performs all the required setup by this `Command`, followed by calling
+ /// the `execvp` syscall.
+ ///
+ /// On success this function will not return, and otherwise it will return
+ /// an error indicating why the exec (or another part of the setup of the
+ /// `Command`) failed.
+ ///
+ /// `exec` not returning has the same implications as calling
+ /// [`process::exit`] – no destructors on the current stack or any other
+ /// thread’s stack will be run. Therefore, it is recommended to only call
+ /// `exec` at a point where it is fine to not run any destructors. Note,
+ /// that the `execvp` syscall independently guarantees that all memory is
+ /// freed and all file descriptors with the `CLOEXEC` option (set by default
+ /// on all file descriptors opened by the standard library) are closed.
+ ///
+ /// This function, unlike `spawn`, will **not** `fork` the process to create
+ /// a new child. Like spawn, however, the default behavior for the stdio
+ /// descriptors will be to inherited from the current process.
+ ///
+ /// [`process::exit`]: ../../../process/fn.exit.html
+ ///
+ /// # Notes
+ ///
+ /// The process may be in a "broken state" if this function returns in
+ /// error. For example the working directory, environment variables, signal
+ /// handling settings, various user/group information, or aspects of stdio
+ /// file descriptors may have changed. If a "transactional spawn" is
+ /// required to gracefully handle errors it is recommended to use the
+ /// cross-platform `spawn` instead.
+ #[stable(feature = "process_exec2", since = "1.9.0")]
+ fn exec(&mut self) -> io::Error;
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl CommandExt for process::Command {
+ fn uid(&mut self, id: u32) -> &mut process::Command {
+ self.as_inner_mut().uid(id);
+ self
+ }
+
+ fn gid(&mut self, id: u32) -> &mut process::Command {
+ self.as_inner_mut().gid(id);
+ self
+ }
+
+ fn before_exec<F>(&mut self, f: F) -> &mut process::Command
+ where F: FnMut() -> io::Result<()> + Send + Sync + 'static
+ {
+ self.as_inner_mut().before_exec(Box::new(f));
+ self
+ }
+
+ fn exec(&mut self) -> io::Error {
+ self.as_inner_mut().exec(sys::process::Stdio::Inherit)
+ }
+}
+
+/// Unix-specific extensions to [`process::ExitStatus`].
+///
+/// [`process::ExitStatus`]: ../../../../std/process/struct.ExitStatus.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub trait ExitStatusExt {
+ /// Creates a new `ExitStatus` from the raw underlying `i32` return value of
+ /// a process.
+ #[stable(feature = "exit_status_from", since = "1.12.0")]
+ fn from_raw(raw: i32) -> Self;
+
+ /// If the process was terminated by a signal, returns that signal.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn signal(&self) -> Option<i32>;
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl ExitStatusExt for process::ExitStatus {
+ fn from_raw(raw: i32) -> Self {
+ process::ExitStatus::from_inner(From::from(raw))
+ }
+
+ fn signal(&self) -> Option<i32> {
+ self.as_inner().signal()
+ }
+}
+
+#[stable(feature = "process_extensions", since = "1.2.0")]
+impl FromRawFd for process::Stdio {
+ unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
+ let fd = sys::fd::FileDesc::new(fd);
+ let io = sys::process::Stdio::Fd(fd);
+ process::Stdio::from_inner(io)
+ }
+}
+
+#[stable(feature = "process_extensions", since = "1.2.0")]
+impl AsRawFd for process::ChildStdin {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_inner().fd().raw()
+ }
+}
+
+#[stable(feature = "process_extensions", since = "1.2.0")]
+impl AsRawFd for process::ChildStdout {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_inner().fd().raw()
+ }
+}
+
+#[stable(feature = "process_extensions", since = "1.2.0")]
+impl AsRawFd for process::ChildStderr {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_inner().fd().raw()
+ }
+}
+
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for process::ChildStdin {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_fd().into_raw()
+ }
+}
+
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for process::ChildStdout {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_fd().into_raw()
+ }
+}
+
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for process::ChildStderr {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_fd().into_raw()
+ }
+}
+
+/// Returns the OS-assigned process identifier associated with this process's parent.
+#[stable(feature = "unix_ppid", since = "1.27.0")]
+pub fn parent_id() -> u32 {
+ ::sys::os::getppid()
+}
diff --git a/ctr-std/src/sys/unix/ext/raw.rs b/ctr-std/src/sys/unix/ext/raw.rs
index a51f5c1..7972990 100644
--- a/ctr-std/src/sys/unix/ext/raw.rs
+++ b/ctr-std/src/sys/unix/ext/raw.rs
@@ -16,13 +16,18 @@
the standard library, the `libc` crate on \
crates.io should be used instead for the correct \
definitions")]
-#![allow(deprecated, warnings)]
+#![allow(deprecated)]
-#[allow(missing_docs)]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type uid_t = u32;
-
-#[allow(missing_docs)]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type gid_t = u32;
-
-#[allow(missing_docs)]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type pid_t = i32;
+
+#[doc(inline)]
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub use sys::platform::raw::pthread_t;
+#[doc(inline)]
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub use sys::platform::raw::{dev_t, ino_t, mode_t, nlink_t, off_t, blksize_t};
+#[doc(inline)]
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub use sys::platform::raw::{blkcnt_t, time_t};
diff --git a/ctr-std/src/sys/unix/ext/thread.rs b/ctr-std/src/sys/unix/ext/thread.rs
new file mode 100644
index 0000000..8dadf29
--- /dev/null
+++ b/ctr-std/src/sys/unix/ext/thread.rs
@@ -0,0 +1,51 @@
+// 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.
+
+//! Unix-specific extensions to primitives in the `std::thread` module.
+
+#![stable(feature = "thread_extensions", since = "1.9.0")]
+
+#[allow(deprecated)]
+use os::unix::raw::pthread_t;
+use sys_common::{AsInner, IntoInner};
+use thread::JoinHandle;
+
+#[stable(feature = "thread_extensions", since = "1.9.0")]
+#[allow(deprecated)]
+pub type RawPthread = pthread_t;
+
+/// Unix-specific extensions to [`thread::JoinHandle`].
+///
+/// [`thread::JoinHandle`]: ../../../../std/thread/struct.JoinHandle.html
+#[stable(feature = "thread_extensions", since = "1.9.0")]
+pub trait JoinHandleExt {
+ /// Extracts the raw pthread_t without taking ownership
+ #[stable(feature = "thread_extensions", since = "1.9.0")]
+ fn as_pthread_t(&self) -> RawPthread;
+
+ /// Consumes the thread, returning the raw pthread_t
+ ///
+ /// This function **transfers ownership** of the underlying pthread_t to
+ /// the caller. Callers are then the unique owners of the pthread_t and
+ /// must either detach or join the pthread_t once it's no longer needed.
+ #[stable(feature = "thread_extensions", since = "1.9.0")]
+ fn into_pthread_t(self) -> RawPthread;
+}
+
+#[stable(feature = "thread_extensions", since = "1.9.0")]
+impl<T> JoinHandleExt for JoinHandle<T> {
+ fn as_pthread_t(&self) -> RawPthread {
+ self.as_inner().id() as RawPthread
+ }
+
+ fn into_pthread_t(self) -> RawPthread {
+ self.into_inner().into_id() as RawPthread
+ }
+}
diff --git a/ctr-std/src/sys/unix/fast_thread_local.rs b/ctr-std/src/sys/unix/fast_thread_local.rs
index 9f0eee0..6cdbe5d 100644
--- a/ctr-std/src/sys/unix/fast_thread_local.rs
+++ b/ctr-std/src/sys/unix/fast_thread_local.rs
@@ -1,4 +1,4 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -11,92 +11,56 @@
#![cfg(target_thread_local)]
#![unstable(feature = "thread_local_internals", issue = "0")]
-use cell::{Cell, UnsafeCell};
-use mem;
-use ptr;
-
-
-pub struct Key<T> {
- inner: UnsafeCell<Option<T>>,
-
- // Metadata to keep track of the state of the destructor. Remember that
- // these variables are thread-local, not global.
- dtor_registered: Cell<bool>,
- dtor_running: Cell<bool>,
-}
-
-unsafe impl<T> ::marker::Sync for Key<T> { }
-
-impl<T> Key<T> {
- pub const fn new() -> Key<T> {
- Key {
- inner: UnsafeCell::new(None),
- dtor_registered: Cell::new(false),
- dtor_running: Cell::new(false)
- }
- }
-
- pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
- unsafe {
- if mem::needs_drop::<T>() && self.dtor_running.get() {
- return None
- }
- self.register_dtor();
- }
- Some(&self.inner)
+// Since what appears to be glibc 2.18 this symbol has been shipped which
+// GCC and clang both use to invoke destructors in thread_local globals, so
+// let's do the same!
+//
+// Note, however, that we run on lots older linuxes, as well as cross
+// compiling from a newer linux to an older linux, so we also have a
+// fallback implementation to use as well.
+//
+// Due to rust-lang/rust#18804, make sure this is not generic!
+#[cfg(target_os = "linux")]
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
+ use libc;
+ use mem;
+ use sys_common::thread_local::register_dtor_fallback;
+
+ extern {
+ #[linkage = "extern_weak"]
+ static __dso_handle: *mut u8;
+ #[linkage = "extern_weak"]
+ static __cxa_thread_atexit_impl: *const libc::c_void;
}
-
- unsafe fn register_dtor(&self) {
- if !mem::needs_drop::<T>() || self.dtor_registered.get() {
- return
- }
-
- register_dtor(self as *const _ as *mut u8,
- destroy_value::<T>);
- self.dtor_registered.set(true);
+ if !__cxa_thread_atexit_impl.is_null() {
+ type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8),
+ arg: *mut u8,
+ dso_handle: *mut u8) -> libc::c_int;
+ mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)
+ (dtor, t, &__dso_handle as *const _ as *mut _);
+ return
}
+ register_dtor_fallback(t, dtor);
}
+// macOS's analog of the above linux function is this _tlv_atexit function.
+// The disassembly of thread_local globals in C++ (at least produced by
+// clang) will have this show up in the output.
+#[cfg(target_os = "macos")]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
- // The fallback implementation uses a vanilla OS-based TLS key to track
- // the list of destructors that need to be run for this thread. The key
- // then has its own destructor which runs all the other destructors.
- //
- // The destructor for DTORS is a little special in that it has a `while`
- // loop to continuously drain the list of registered destructors. It
- // *should* be the case that this loop always terminates because we
- // provide the guarantee that a TLS key cannot be set after it is
- // flagged for destruction.
- use sys_common::thread_local as os;
-
- static DTORS: os::StaticKey = os::StaticKey::new(Some(run_dtors));
- type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>;
- if DTORS.get().is_null() {
- let v: Box<List> = box Vec::new();
- DTORS.set(Box::into_raw(v) as *mut u8);
- }
- let list: &mut List = &mut *(DTORS.get() as *mut List);
- list.push((t, dtor));
-
- unsafe extern fn run_dtors(mut ptr: *mut u8) {
- while !ptr.is_null() {
- let list: Box<List> = Box::from_raw(ptr as *mut List);
- for &(ptr, dtor) in list.iter() {
- dtor(ptr);
- }
- ptr = DTORS.get();
- DTORS.set(ptr::null_mut());
- }
+ extern {
+ fn _tlv_atexit(dtor: unsafe extern fn(*mut u8),
+ arg: *mut u8);
}
+ _tlv_atexit(dtor, t);
}
-pub unsafe extern fn destroy_value<T>(ptr: *mut u8) {
- let ptr = ptr as *mut Key<T>;
- // Right before we run the user destructor be sure to flag the
- // destructor as running for this thread so calls to `get` will return
- // `None`.
- (*ptr).dtor_running.set(true);
+// Just use the thread_local fallback implementation, at least until there's
+// a more direct implementation.
+#[cfg(target_os = "fuchsia")]
+pub use sys_common::thread_local::register_dtor_fallback as register_dtor;
+pub fn requires_move_before_drop() -> bool {
// The macOS implementation of TLS apparently had an odd aspect to it
// where the pointer we have may be overwritten while this destructor
// is running. Specifically if a TLS destructor re-accesses TLS it may
@@ -109,13 +73,5 @@ pub unsafe extern fn destroy_value<T>(ptr: *mut u8) {
//
// Hence, we use `ptr::read` on macOS (to move to a "safe" location)
// instead of drop_in_place.
- if cfg!(target_os = "macos") {
- ptr::read((*ptr).inner.get());
- } else {
- ptr::drop_in_place((*ptr).inner.get());
- }
-}
-
-pub fn requires_move_before_drop() -> bool {
- false
+ cfg!(target_os = "macos")
}
diff --git a/ctr-std/src/sys/unix/fd.rs b/ctr-std/src/sys/unix/fd.rs
index d7913e5..4830e38 100644
--- a/ctr-std/src/sys/unix/fd.rs
+++ b/ctr-std/src/sys/unix/fd.rs
@@ -68,9 +68,31 @@ impl FileDesc {
}
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
+ #[cfg(target_os = "android")]
+ use super::android::cvt_pread64;
+
+ #[cfg(target_os = "emscripten")]
unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64)
-> io::Result<isize>
{
+ use convert::TryInto;
+ use libc::pread64;
+ // pread64 on emscripten actually takes a 32 bit offset
+ if let Ok(o) = offset.try_into() {
+ cvt(pread64(fd, buf, count, o))
+ } else {
+ Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot pread >2GB"))
+ }
+ }
+
+ #[cfg(not(any(target_os = "android", target_os = "emscripten")))]
+ unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64)
+ -> io::Result<isize>
+ {
+ #[cfg(target_os = "linux")]
+ use libc::pread64;
+ #[cfg(not(target_os = "linux"))]
use libc::pread as pread64;
cvt(pread64(fd, buf, count, offset))
}
@@ -94,9 +116,31 @@ impl FileDesc {
}
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
+ #[cfg(target_os = "android")]
+ use super::android::cvt_pwrite64;
+
+ #[cfg(target_os = "emscripten")]
unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64)
-> io::Result<isize>
{
+ use convert::TryInto;
+ use libc::pwrite64;
+ // pwrite64 on emscripten actually takes a 32 bit offset
+ if let Ok(o) = offset.try_into() {
+ cvt(pwrite64(fd, buf, count, o))
+ } else {
+ Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot pwrite >2GB"))
+ }
+ }
+
+ #[cfg(not(any(target_os = "android", target_os = "emscripten")))]
+ unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64)
+ -> io::Result<isize>
+ {
+ #[cfg(target_os = "linux")]
+ use libc::pwrite64;
+ #[cfg(not(target_os = "linux"))]
use libc::pwrite as pwrite64;
cvt(pwrite64(fd, buf, count, offset))
}
@@ -110,11 +154,52 @@ impl FileDesc {
}
}
- // We don't have fork/exec on the 3DS, so this shouldn't need to do anything
+ #[cfg(target_os = "linux")]
+ pub fn get_cloexec(&self) -> io::Result<bool> {
+ unsafe {
+ Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0)
+ }
+ }
+
+ #[cfg(not(any(target_env = "newlib",
+ target_os = "solaris",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "l4re",
+ target_os = "haiku")))]
pub fn set_cloexec(&self) -> io::Result<()> {
- Ok(())
+ unsafe {
+ cvt(libc::ioctl(self.fd, libc::FIOCLEX))?;
+ Ok(())
+ }
+ }
+ #[cfg(any(target_env = "newlib",
+ target_os = "solaris",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "l4re",
+ target_os = "haiku"))]
+ pub fn set_cloexec(&self) -> io::Result<()> {
+ unsafe {
+ let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
+ let new = previous | libc::FD_CLOEXEC;
+ if new != previous {
+ cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?;
+ }
+ Ok(())
+ }
+ }
+
+ #[cfg(target_os = "linux")]
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ unsafe {
+ let v = nonblocking as c_int;
+ cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
+ Ok(())
+ }
}
+ #[cfg(not(target_os = "linux"))]
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
unsafe {
let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
diff --git a/ctr-std/src/sys/unix/fs.rs b/ctr-std/src/sys/unix/fs.rs
index 9dba863..7743403 100644
--- a/ctr-std/src/sys/unix/fs.rs
+++ b/ctr-std/src/sys/unix/fs.rs
@@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![allow(dead_code)]
-
use os::unix::prelude::*;
use ffi::{CString, CStr, OsString, OsStr};
@@ -25,8 +23,24 @@ use sys::time::SystemTime;
use sys::{cvt, cvt_r};
use sys_common::{AsInner, FromInner};
+#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
+use libc::{stat64, fstat64, lstat64, off64_t, ftruncate64, lseek64, dirent64, readdir64_r, open64};
+#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
+use libc::{fstatat, dirfd};
+#[cfg(target_os = "android")]
+use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, lseek64,
+ dirent as dirent64, open as open64};
+#[cfg(not(any(target_os = "linux",
+ target_os = "emscripten",
+ target_os = "l4re",
+ target_os = "android")))]
use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, off_t as off64_t,
ftruncate as ftruncate64, lseek as lseek64, dirent as dirent64, open as open64};
+#[cfg(not(any(target_os = "linux",
+ target_os = "emscripten",
+ target_os = "solaris",
+ target_os = "l4re",
+ target_os = "fuchsia")))]
use libc::{readdir_r as readdir64_r};
pub struct File(FileDesc);
@@ -36,11 +50,15 @@ pub struct FileAttr {
stat: stat64,
}
-pub struct ReadDir {
+// all DirEntry's will have a reference to this struct
+struct InnerReadDir {
dirp: Dir,
- root: Arc<PathBuf>,
+ root: PathBuf,
}
+#[derive(Clone)]
+pub struct ReadDir(Arc<InnerReadDir>);
+
struct Dir(*mut libc::DIR);
unsafe impl Send for Dir {}
@@ -48,8 +66,8 @@ unsafe impl Sync for Dir {}
pub struct DirEntry {
entry: dirent64,
- root: Arc<PathBuf>,
- // We need to store an owned copy of the directory name
+ dir: ReadDir,
+ // We need to store an owned copy of the entry name
// on Solaris and Fuchsia because a) it uses a zero-length
// array to store the name, b) its lifetime between readdir
// calls is not guaranteed.
@@ -91,19 +109,63 @@ impl FileAttr {
}
}
+#[cfg(target_os = "netbsd")]
impl FileAttr {
pub fn modified(&self) -> io::Result<SystemTime> {
- Err(io::Error::new(io::ErrorKind::Other,
- "modification time is not available on this platform \
- currently"))
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_mtime as libc::time_t,
+ tv_nsec: self.stat.st_mtimensec as libc::c_long,
+ }))
}
pub fn accessed(&self) -> io::Result<SystemTime> {
- Err(io::Error::new(io::ErrorKind::Other,
- "access time is not available on this platform \
- currently"))
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_atime as libc::time_t,
+ tv_nsec: self.stat.st_atimensec as libc::c_long,
+ }))
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_birthtime as libc::time_t,
+ tv_nsec: self.stat.st_birthtimensec as libc::c_long,
+ }))
+ }
+}
+
+#[cfg(not(target_os = "netbsd"))]
+impl FileAttr {
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_mtime as libc::time_t,
+ tv_nsec: self.stat.st_mtime_nsec as _,
+ }))
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_atime as libc::time_t,
+ tv_nsec: self.stat.st_atime_nsec as _,
+ }))
}
+ #[cfg(any(target_os = "bitrig",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "macos",
+ target_os = "ios"))]
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_birthtime as libc::time_t,
+ tv_nsec: self.stat.st_birthtime_nsec as libc::c_long,
+ }))
+ }
+
+ #[cfg(not(any(target_os = "bitrig",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "macos",
+ target_os = "ios")))]
pub fn created(&self) -> io::Result<SystemTime> {
Err(io::Error::new(io::ErrorKind::Other,
"creation time is not available on this platform \
@@ -151,7 +213,7 @@ 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("/home")'
- fmt::Debug::fmt(&*self.root, f)
+ fmt::Debug::fmt(&*self.0.root, f)
}
}
@@ -167,7 +229,7 @@ impl Iterator for ReadDir {
// is safe to use in threaded applications and it is generally preferred
// over the readdir_r(3C) function.
super::os::set_errno(0);
- let entry_ptr = libc::readdir(self.dirp.0);
+ let entry_ptr = libc::readdir(self.0.dirp.0);
if entry_ptr.is_null() {
// NULL can mean either the end is reached or an error occurred.
// So we had to clear errno beforehand to check for an error now.
@@ -177,14 +239,14 @@ impl Iterator for ReadDir {
}
}
- let name = (*entry_ptr).d_name.as_ptr() as *const _;
+ let name = (*entry_ptr).d_name.as_ptr();
let namelen = libc::strlen(name) as usize;
let ret = DirEntry {
entry: *entry_ptr,
name: ::slice::from_raw_parts(name as *const u8,
namelen as usize).to_owned().into_boxed_slice(),
- root: self.root.clone()
+ dir: self.clone()
};
if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
return Some(Ok(ret))
@@ -198,11 +260,11 @@ impl Iterator for ReadDir {
unsafe {
let mut ret = DirEntry {
entry: mem::zeroed(),
- root: self.root.clone()
+ dir: self.clone(),
};
let mut entry_ptr = ptr::null_mut();
loop {
- if readdir64_r(self.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 {
+ if readdir64_r(self.0.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 {
return Some(Err(Error::last_os_error()))
}
if entry_ptr.is_null() {
@@ -225,13 +287,27 @@ impl Drop for Dir {
impl DirEntry {
pub fn path(&self) -> PathBuf {
- self.root.join(OsStr::from_bytes(self.name_bytes()))
+ self.dir.0.root.join(OsStr::from_bytes(self.name_bytes()))
}
pub fn file_name(&self) -> OsString {
OsStr::from_bytes(self.name_bytes()).to_os_string()
}
+ #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
+ pub fn metadata(&self) -> io::Result<FileAttr> {
+ let fd = cvt(unsafe {dirfd(self.dir.0.dirp.0)})?;
+ let mut stat: stat64 = unsafe { mem::zeroed() };
+ cvt(unsafe {
+ fstatat(fd,
+ self.entry.d_name.as_ptr(),
+ &mut stat as *mut _ as *mut _,
+ libc::AT_SYMLINK_NOFOLLOW)
+ })?;
+ Ok(FileAttr { stat: stat })
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))]
pub fn metadata(&self) -> io::Result<FileAttr> {
lstat(&self.path())
}
@@ -255,15 +331,56 @@ impl DirEntry {
}
}
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "android",
+ target_os = "solaris",
+ target_os = "haiku",
+ target_os = "l4re",
+ target_os = "fuchsia"))]
pub fn ino(&self) -> u64 {
self.entry.d_ino as u64
}
+ #[cfg(any(target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "bitrig",
+ target_os = "netbsd",
+ target_os = "dragonfly"))]
+ pub fn ino(&self) -> u64 {
+ self.entry.d_fileno as u64
+ }
+
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "bitrig"))]
fn name_bytes(&self) -> &[u8] {
unsafe {
- CStr::from_ptr(self.entry.d_name.as_ptr() as *const _).to_bytes()
+ ::slice::from_raw_parts(self.entry.d_name.as_ptr() as *const u8,
+ self.entry.d_namlen as usize)
}
}
+ #[cfg(any(target_os = "android",
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "l4re",
+ target_os = "haiku"))]
+ fn name_bytes(&self) -> &[u8] {
+ unsafe {
+ CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes()
+ }
+ }
+ #[cfg(any(target_os = "solaris",
+ target_os = "fuchsia"))]
+ fn name_bytes(&self) -> &[u8] {
+ &*self.name
+ }
}
impl OpenOptions {
@@ -338,21 +455,54 @@ impl File {
opts.get_creation_mode()? |
(opts.custom_flags as c_int & !libc::O_ACCMODE);
let fd = cvt_r(|| unsafe {
- open64(path.as_ptr() as *const _, flags, opts.mode as c_int)
+ open64(path.as_ptr(), flags, opts.mode as c_int)
})?;
let fd = FileDesc::new(fd);
// Currently the standard library supports Linux 2.6.18 which did not
// have the O_CLOEXEC flag (passed above). If we're running on an older
- // Linux kernel then the flag is just ignored by the OS, so we continue
- // to explicitly ask for a CLOEXEC fd here.
+ // Linux kernel then the flag is just ignored by the OS. After we open
+ // the first file, we check whether it has CLOEXEC set. If it doesn't,
+ // we will explicitly ask for a CLOEXEC fd for every further file we
+ // open, if it does, we will skip that step.
//
// The CLOEXEC flag, however, is supported on versions of macOS/BSD/etc
// that we support, so we only do this on Linux currently.
- if cfg!(target_os = "linux") {
- fd.set_cloexec()?;
+ #[cfg(target_os = "linux")]
+ fn ensure_cloexec(fd: &FileDesc) -> io::Result<()> {
+ use sync::atomic::{AtomicUsize, Ordering};
+
+ const OPEN_CLOEXEC_UNKNOWN: usize = 0;
+ const OPEN_CLOEXEC_SUPPORTED: usize = 1;
+ const OPEN_CLOEXEC_NOTSUPPORTED: usize = 2;
+ static OPEN_CLOEXEC: AtomicUsize = AtomicUsize::new(OPEN_CLOEXEC_UNKNOWN);
+
+ let need_to_set;
+ match OPEN_CLOEXEC.load(Ordering::Relaxed) {
+ OPEN_CLOEXEC_UNKNOWN => {
+ need_to_set = !fd.get_cloexec()?;
+ OPEN_CLOEXEC.store(if need_to_set {
+ OPEN_CLOEXEC_NOTSUPPORTED
+ } else {
+ OPEN_CLOEXEC_SUPPORTED
+ }, Ordering::Relaxed);
+ },
+ OPEN_CLOEXEC_SUPPORTED => need_to_set = false,
+ OPEN_CLOEXEC_NOTSUPPORTED => need_to_set = true,
+ _ => unreachable!(),
+ }
+ if need_to_set {
+ fd.set_cloexec()?;
+ }
+ Ok(())
+ }
+
+ #[cfg(not(target_os = "linux"))]
+ fn ensure_cloexec(_: &FileDesc) -> io::Result<()> {
+ Ok(())
}
+ ensure_cloexec(&fd)?;
Ok(File(fd))
}
@@ -373,6 +523,15 @@ impl File {
cvt_r(|| unsafe { os_datasync(self.0.raw()) })?;
return Ok(());
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ unsafe fn os_datasync(fd: c_int) -> c_int {
+ libc::fcntl(fd, libc::F_FULLFSYNC)
+ }
+ #[cfg(target_os = "linux")]
+ unsafe fn os_datasync(fd: c_int) -> c_int { libc::fdatasync(fd) }
+ #[cfg(not(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "linux")))]
unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) }
}
@@ -439,7 +598,7 @@ impl DirBuilder {
pub fn mkdir(&self, p: &Path) -> io::Result<()> {
let p = cstr(p)?;
- cvt(unsafe { libc::mkdir(p.as_ptr() as *const _, self.mode) })?;
+ cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?;
Ok(())
}
@@ -475,7 +634,7 @@ impl fmt::Debug for File {
// alternatives. If a better method is invented, it should be used
// instead.
let mut buf = vec![0;libc::PATH_MAX as usize];
- let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr() as *const _) };
+ let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
if n == -1 {
return None;
}
@@ -525,40 +684,41 @@ impl fmt::Debug for File {
}
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
- let root = Arc::new(p.to_path_buf());
+ let root = p.to_path_buf();
let p = cstr(p)?;
unsafe {
- let ptr = libc::opendir(p.as_ptr() as *const _);
+ let ptr = libc::opendir(p.as_ptr());
if ptr.is_null() {
Err(Error::last_os_error())
} else {
- Ok(ReadDir { dirp: Dir(ptr), root: root })
+ let inner = InnerReadDir { dirp: Dir(ptr), root };
+ Ok(ReadDir(Arc::new(inner)))
}
}
}
pub fn unlink(p: &Path) -> io::Result<()> {
let p = cstr(p)?;
- cvt(unsafe { libc::unlink(p.as_ptr() as *const _) })?;
+ cvt(unsafe { libc::unlink(p.as_ptr()) })?;
Ok(())
}
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
let old = cstr(old)?;
let new = cstr(new)?;
- cvt(unsafe { libc::rename(old.as_ptr() as *const _, new.as_ptr() as *const _) })?;
+ cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?;
Ok(())
}
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
let p = cstr(p)?;
- cvt_r(|| unsafe { libc::chmod(p.as_ptr() as *const _, perm.mode) })?;
+ cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?;
Ok(())
}
pub fn rmdir(p: &Path) -> io::Result<()> {
let p = cstr(p)?;
- cvt(unsafe { libc::rmdir(p.as_ptr() as *const _) })?;
+ cvt(unsafe { libc::rmdir(p.as_ptr()) })?;
Ok(())
}
@@ -585,7 +745,7 @@ fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
let c_path = cstr(p)?;
- let p = c_path.as_ptr() as *const _;
+ let p = c_path.as_ptr();
let mut buf = Vec::with_capacity(256);
@@ -612,14 +772,14 @@ pub fn readlink(p: &Path) -> io::Result<PathBuf> {
pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
let src = cstr(src)?;
let dst = cstr(dst)?;
- cvt(unsafe { libc::symlink(src.as_ptr() as *const _, dst.as_ptr() as *const _) })?;
+ cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })?;
Ok(())
}
pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
let src = cstr(src)?;
let dst = cstr(dst)?;
- cvt(unsafe { libc::link(src.as_ptr() as *const _, dst.as_ptr() as *const _) })?;
+ cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?;
Ok(())
}
@@ -627,7 +787,7 @@ pub fn stat(p: &Path) -> io::Result<FileAttr> {
let p = cstr(p)?;
let mut stat: stat64 = unsafe { mem::zeroed() };
cvt(unsafe {
- stat64(p.as_ptr() as *const _, &mut stat as *mut _ as *mut _)
+ stat64(p.as_ptr(), &mut stat as *mut _ as *mut _)
})?;
Ok(FileAttr { stat: stat })
}
@@ -636,7 +796,7 @@ pub fn lstat(p: &Path) -> io::Result<FileAttr> {
let p = cstr(p)?;
let mut stat: stat64 = unsafe { mem::zeroed() };
cvt(unsafe {
- lstat64(p.as_ptr() as *const _, &mut stat as *mut _ as *mut _)
+ lstat64(p.as_ptr(), &mut stat as *mut _ as *mut _)
})?;
Ok(FileAttr { stat: stat })
}
@@ -645,18 +805,19 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
let path = CString::new(p.as_os_str().as_bytes())?;
let buf;
unsafe {
- let r = libc::realpath(path.as_ptr() as *const _, ptr::null_mut());
+ let r = libc::realpath(path.as_ptr(), ptr::null_mut());
if r.is_null() {
return Err(io::Error::last_os_error())
}
- buf = CStr::from_ptr(r as *const _).to_bytes().to_vec();
+ buf = CStr::from_ptr(r).to_bytes().to_vec();
libc::free(r as *mut _);
}
Ok(PathBuf::from(OsString::from_vec(buf)))
}
+#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
- use fs::{File, set_permissions};
+ use fs::File;
if !from.is_file() {
return Err(Error::new(ErrorKind::InvalidInput,
"the source path is not an existing regular file"))
@@ -667,6 +828,100 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
let perm = reader.metadata()?.permissions();
let ret = io::copy(&mut reader, &mut writer)?;
- set_permissions(to, perm)?;
+ writer.set_permissions(perm)?;
Ok(ret)
}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+ use cmp;
+ use fs::File;
+ use sync::atomic::{AtomicBool, Ordering};
+
+ // Kernel prior to 4.5 don't have copy_file_range
+ // We store the availability in a global to avoid unneccessary syscalls
+ static HAS_COPY_FILE_RANGE: AtomicBool = AtomicBool::new(true);
+
+ unsafe fn copy_file_range(
+ fd_in: libc::c_int,
+ off_in: *mut libc::loff_t,
+ fd_out: libc::c_int,
+ off_out: *mut libc::loff_t,
+ len: libc::size_t,
+ flags: libc::c_uint,
+ ) -> libc::c_long {
+ libc::syscall(
+ libc::SYS_copy_file_range,
+ fd_in,
+ off_in,
+ fd_out,
+ off_out,
+ len,
+ flags,
+ )
+ }
+
+ if !from.is_file() {
+ return Err(Error::new(ErrorKind::InvalidInput,
+ "the source path is not an existing regular file"))
+ }
+
+ let mut reader = File::open(from)?;
+ let mut writer = File::create(to)?;
+ let (perm, len) = {
+ let metadata = reader.metadata()?;
+ (metadata.permissions(), metadata.size())
+ };
+
+ let has_copy_file_range = HAS_COPY_FILE_RANGE.load(Ordering::Relaxed);
+ let mut written = 0u64;
+ while written < len {
+ let copy_result = if has_copy_file_range {
+ let bytes_to_copy = cmp::min(len - written, usize::max_value() as u64) as usize;
+ let copy_result = unsafe {
+ // We actually don't have to adjust the offsets,
+ // because copy_file_range adjusts the file offset automatically
+ cvt(copy_file_range(reader.as_raw_fd(),
+ ptr::null_mut(),
+ writer.as_raw_fd(),
+ ptr::null_mut(),
+ bytes_to_copy,
+ 0)
+ )
+ };
+ if let Err(ref copy_err) = copy_result {
+ match copy_err.raw_os_error() {
+ Some(libc::ENOSYS) | Some(libc::EPERM) => {
+ HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed);
+ }
+ _ => {}
+ }
+ }
+ copy_result
+ } else {
+ Err(io::Error::from_raw_os_error(libc::ENOSYS))
+ };
+ match copy_result {
+ Ok(ret) => written += ret as u64,
+ Err(err) => {
+ match err.raw_os_error() {
+ Some(os_err) if os_err == libc::ENOSYS
+ || os_err == libc::EXDEV
+ || os_err == libc::EPERM => {
+ // Try fallback io::copy if either:
+ // - Kernel version is < 4.5 (ENOSYS)
+ // - Files are mounted on different fs (EXDEV)
+ // - copy_file_range is disallowed, for example by seccomp (EPERM)
+ assert_eq!(written, 0);
+ let ret = io::copy(&mut reader, &mut writer)?;
+ writer.set_permissions(perm)?;
+ return Ok(ret)
+ },
+ _ => return Err(err),
+ }
+ }
+ }
+ }
+ writer.set_permissions(perm)?;
+ Ok(written)
+}
diff --git a/ctr-std/src/sys/unix/l4re.rs b/ctr-std/src/sys/unix/l4re.rs
new file mode 100644
index 0000000..2121848
--- /dev/null
+++ b/ctr-std/src/sys/unix/l4re.rs
@@ -0,0 +1,441 @@
+// Copyright 2016-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.
+
+macro_rules! unimpl {
+ () => (return Err(io::Error::new(io::ErrorKind::Other, "No networking available on L4Re."));)
+}
+
+pub mod net {
+ #![allow(warnings)]
+ use fmt;
+ use io;
+ use libc;
+ use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr};
+ use sys_common::{AsInner, FromInner, IntoInner};
+ use sys::fd::FileDesc;
+ use time::Duration;
+
+
+ pub extern crate libc as netc;
+
+ pub struct Socket(FileDesc);
+ impl Socket {
+ pub fn new(_: &SocketAddr, _: libc::c_int) -> io::Result<Socket> {
+ unimpl!();
+ }
+
+ pub fn new_raw(_: libc::c_int, _: libc::c_int) -> io::Result<Socket> {
+ unimpl!();
+ }
+
+ pub fn new_pair(_: libc::c_int, _: libc::c_int) -> io::Result<(Socket, Socket)> {
+ unimpl!();
+ }
+
+ pub fn connect_timeout(&self, _: &SocketAddr, _: Duration) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn accept(&self, _: *mut libc::sockaddr, _: *mut libc::socklen_t)
+ -> io::Result<Socket> {
+ unimpl!();
+ }
+
+ pub fn duplicate(&self) -> io::Result<Socket> {
+ unimpl!();
+ }
+
+ pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unimpl!();
+ }
+
+ pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unimpl!();
+ }
+
+ pub fn write(&self, _: &[u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn set_timeout(&self, _: Option<Duration>, _: libc::c_int) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn timeout(&self, _: libc::c_int) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
+ pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unimpl!();
+ }
+ }
+
+ impl AsInner<libc::c_int> for Socket {
+ fn as_inner(&self) -> &libc::c_int { self.0.as_inner() }
+ }
+
+ impl FromInner<libc::c_int> for Socket {
+ fn from_inner(fd: libc::c_int) -> Socket { Socket(FileDesc::new(fd)) }
+ }
+
+ impl IntoInner<libc::c_int> for Socket {
+ fn into_inner(self) -> libc::c_int { self.0.into_raw() }
+ }
+
+ pub struct TcpStream {
+ inner: Socket,
+ }
+
+ impl TcpStream {
+ pub fn connect(_: &SocketAddr) -> io::Result<TcpStream> {
+ unimpl!();
+ }
+
+ pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
+ unimpl!();
+ }
+
+ pub fn socket(&self) -> &Socket { &self.inner }
+
+ pub fn into_socket(self) -> Socket { self.inner }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn write(&self, _: &[u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ unimpl!();
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unimpl!();
+ }
+
+ pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpStream> {
+ unimpl!();
+ }
+
+ pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unimpl!();
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unimpl!();
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+ }
+
+ impl FromInner<Socket> for TcpStream {
+ fn from_inner(socket: Socket) -> TcpStream {
+ TcpStream { inner: socket }
+ }
+ }
+
+ impl fmt::Debug for TcpStream {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "No networking support available on L4Re")
+ }
+ }
+
+ pub struct TcpListener {
+ inner: Socket,
+ }
+
+ impl TcpListener {
+ pub fn bind(_: &SocketAddr) -> io::Result<TcpListener> {
+ unimpl!();
+ }
+
+ pub fn socket(&self) -> &Socket { &self.inner }
+
+ pub fn into_socket(self) -> Socket { self.inner }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unimpl!();
+ }
+
+ pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+ unimpl!();
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpListener> {
+ unimpl!();
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unimpl!();
+ }
+
+ pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn only_v6(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unimpl!();
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+ }
+
+ impl FromInner<Socket> for TcpListener {
+ fn from_inner(socket: Socket) -> TcpListener {
+ TcpListener { inner: socket }
+ }
+ }
+
+ impl fmt::Debug for TcpListener {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "No networking support available on L4Re.")
+ }
+ }
+
+ pub struct UdpSocket {
+ inner: Socket,
+ }
+
+ impl UdpSocket {
+ pub fn bind(_: &SocketAddr) -> io::Result<UdpSocket> {
+ unimpl!();
+ }
+
+ pub fn socket(&self) -> &Socket { &self.inner }
+
+ pub fn into_socket(self) -> Socket { self.inner }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unimpl!();
+ }
+
+ pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unimpl!();
+ }
+
+ pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unimpl!();
+ }
+
+ pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn duplicate(&self) -> io::Result<UdpSocket> {
+ unimpl!();
+ }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
+ pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn broadcast(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn multicast_loop_v4(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
+ unimpl!();
+ }
+
+ pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn multicast_loop_v6(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr)
+ -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32)
+ -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr)
+ -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32)
+ -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unimpl!();
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unimpl!();
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn send(&self, _: &[u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn connect(&self, _: &SocketAddr) -> io::Result<()> {
+ unimpl!();
+ }
+ }
+
+ impl FromInner<Socket> for UdpSocket {
+ fn from_inner(socket: Socket) -> UdpSocket {
+ UdpSocket { inner: socket }
+ }
+ }
+
+ impl fmt::Debug for UdpSocket {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "No networking support on L4Re available.")
+ }
+ }
+
+ pub struct LookupHost {
+ original: *mut libc::addrinfo,
+ cur: *mut libc::addrinfo,
+ }
+
+ impl Iterator for LookupHost {
+ type Item = SocketAddr;
+ fn next(&mut self) -> Option<SocketAddr> {
+ None
+ }
+ }
+
+ unsafe impl Sync for LookupHost {}
+ unsafe impl Send for LookupHost {}
+
+ pub fn lookup_host(_: &str) -> io::Result<LookupHost> {
+ unimpl!();
+ }
+}
+
diff --git a/ctr-std/src/sys/unix/memchr.rs b/ctr-std/src/sys/unix/memchr.rs
index 71d9111..f49adc2 100644
--- a/ctr-std/src/sys/unix/memchr.rs
+++ b/ctr-std/src/sys/unix/memchr.rs
@@ -1,13 +1,3 @@
-// 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.
-
// 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.
@@ -38,6 +28,27 @@ pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
}
pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> {
+
+ #[cfg(target_os = "linux")]
+ fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
+ use libc;
+
+ // GNU's memrchr() will - unlike memchr() - error if haystack is empty.
+ if haystack.is_empty() {return None}
+ let p = unsafe {
+ libc::memrchr(
+ haystack.as_ptr() as *const libc::c_void,
+ needle as libc::c_int,
+ haystack.len())
+ };
+ if p.is_null() {
+ None
+ } else {
+ Some(p as usize - (haystack.as_ptr() as usize))
+ }
+ }
+
+ #[cfg(not(target_os = "linux"))]
fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
::core::slice::memchr::memrchr(needle, haystack)
}
diff --git a/ctr-std/src/sys/unix/mod.rs b/ctr-std/src/sys/unix/mod.rs
index 829f6f0..c1298e5 100644
--- a/ctr-std/src/sys/unix/mod.rs
+++ b/ctr-std/src/sys/unix/mod.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -8,15 +8,35 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-//! System bindings for the Nintendo 3DS
+#![allow(missing_docs, bad_style)]
use io::{self, ErrorKind};
use libc;
+#[cfg(any(dox, target_os = "linux"))] pub use os::linux as platform;
+
+#[cfg(all(not(dox), target_os = "android"))] pub use os::android as platform;
+#[cfg(all(not(dox), target_os = "bitrig"))] pub use os::bitrig as platform;
+#[cfg(all(not(dox), target_os = "dragonfly"))] pub use os::dragonfly as platform;
+#[cfg(all(not(dox), target_os = "freebsd"))] pub use os::freebsd as platform;
+#[cfg(all(not(dox), target_os = "haiku"))] pub use os::haiku as platform;
+#[cfg(all(not(dox), target_os = "ios"))] pub use os::ios as platform;
+#[cfg(all(not(dox), target_os = "macos"))] pub use os::macos as platform;
+#[cfg(all(not(dox), target_os = "netbsd"))] pub use os::netbsd as platform;
+#[cfg(all(not(dox), target_os = "openbsd"))] pub use os::openbsd as platform;
+#[cfg(all(not(dox), target_os = "solaris"))] pub use os::solaris as platform;
+#[cfg(all(not(dox), target_os = "emscripten"))] pub use os::emscripten as platform;
+#[cfg(all(not(dox), target_os = "fuchsia"))] pub use os::fuchsia as platform;
+#[cfg(all(not(dox), target_os = "l4re"))] pub use os::linux as platform;
+
pub use self::rand::hashmap_random_keys;
pub use libc::strlen;
+#[macro_use]
+pub mod weak;
+
pub mod args;
+pub mod android;
#[cfg(feature = "backtrace")]
pub mod backtrace;
pub mod cmath;
@@ -28,7 +48,12 @@ pub mod fd;
pub mod fs;
pub mod memchr;
pub mod mutex;
+#[cfg(not(target_os = "l4re"))]
pub mod net;
+#[cfg(target_os = "l4re")]
+mod l4re;
+#[cfg(target_os = "l4re")]
+pub use self::l4re::net;
pub mod os;
pub mod os_str;
pub mod path;
@@ -44,16 +69,29 @@ pub mod stdio;
#[cfg(not(test))]
pub fn init() {
-}
+ // By default, some platforms will send a *signal* when an EPIPE error
+ // would otherwise be delivered. This runtime doesn't install a SIGPIPE
+ // handler, causing it to kill the program, which isn't exactly what we
+ // want!
+ //
+ // Hence, we set SIGPIPE to ignore when the program starts up in order
+ // to prevent this problem.
+ unsafe {
+ reset_sigpipe();
+ }
-pub fn unsupported<T>() -> io::Result<T> {
- Err(unsupported_err())
+ #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
+ unsafe fn reset_sigpipe() {
+ assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
+ }
+ #[cfg(any(target_os = "emscripten", target_os = "fuchsia"))]
+ unsafe fn reset_sigpipe() {}
}
-pub fn unsupported_err() -> io::Error {
- io::Error::new(io::ErrorKind::Other,
- "operation not supported on 3DS yet")
-}
+#[cfg(target_os = "android")]
+pub use sys::android::signal;
+#[cfg(not(target_os = "android"))]
+pub use libc::signal;
pub fn decode_error_kind(errno: i32) -> ErrorKind {
match errno as libc::c_int {
@@ -81,11 +119,6 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
}
}
-// This enum is used as the storage for a bunch of types which can't actually
-// exist.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
-pub enum Void {}
-
#[doc(hidden)]
pub trait IsMinusOne {
fn is_minus_one(&self) -> bool;
diff --git a/ctr-std/src/sys/unix/mutex.rs b/ctr-std/src/sys/unix/mutex.rs
index 95c74a4..52cf3f9 100644
--- a/ctr-std/src/sys/unix/mutex.rs
+++ b/ctr-std/src/sys/unix/mutex.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -9,54 +9,93 @@
// except according to those terms.
use cell::UnsafeCell;
+use libc;
use mem;
-pub struct Mutex {
- inner: UnsafeCell<::libctru::LightLock>,
-}
+pub struct Mutex { inner: UnsafeCell<libc::pthread_mutex_t> }
#[inline]
-pub unsafe fn raw(m: &Mutex) -> *mut ::libctru::LightLock {
+pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t {
m.inner.get()
}
unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}
+#[allow(dead_code)] // sys isn't exported yet
impl Mutex {
pub const fn new() -> Mutex {
- Mutex { inner: UnsafeCell::new(0) }
+ // Might be moved and address is changing it is better to avoid
+ // initialization of potentially opaque OS data before it landed
+ Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
}
-
#[inline]
pub unsafe fn init(&mut self) {
- ::libctru::LightLock_Init(self.inner.get());
+ // Issue #33770
+ //
+ // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have
+ // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you
+ // try to re-lock it from the same thread when you already hold a lock.
+ //
+ // In practice, glibc takes advantage of this undefined behavior to
+ // implement hardware lock elision, which uses hardware transactional
+ // memory to avoid acquiring the lock. While a transaction is in
+ // progress, the lock appears to be unlocked. This isn't a problem for
+ // other threads since the transactional memory will abort if a conflict
+ // is detected, however no abort is generated if re-locking from the
+ // same thread.
+ //
+ // Since locking the same mutex twice will result in two aliasing &mut
+ // references, we instead create the mutex with type
+ // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to
+ // re-lock it from the same thread, thus avoiding undefined behavior.
+ //
+ // We can't do anything for StaticMutex, but that type is deprecated
+ // anyways.
+ let mut attr: libc::pthread_mutexattr_t = mem::uninitialized();
+ let r = libc::pthread_mutexattr_init(&mut attr);
+ debug_assert_eq!(r, 0);
+ let r = libc::pthread_mutexattr_settype(&mut attr, libc::PTHREAD_MUTEX_NORMAL);
+ debug_assert_eq!(r, 0);
+ let r = libc::pthread_mutex_init(self.inner.get(), &attr);
+ debug_assert_eq!(r, 0);
+ let r = libc::pthread_mutexattr_destroy(&mut attr);
+ debug_assert_eq!(r, 0);
}
-
#[inline]
pub unsafe fn lock(&self) {
- ::libctru::LightLock_Lock(self.inner.get());
+ let r = libc::pthread_mutex_lock(self.inner.get());
+ debug_assert_eq!(r, 0);
}
-
#[inline]
pub unsafe fn unlock(&self) {
- ::libctru::LightLock_Unlock(self.inner.get());
+ let r = libc::pthread_mutex_unlock(self.inner.get());
+ debug_assert_eq!(r, 0);
}
-
#[inline]
pub unsafe fn try_lock(&self) -> bool {
- match ::libctru::LightLock_TryLock(self.inner.get()) {
- 0 => false,
- _ => true,
- }
+ libc::pthread_mutex_trylock(self.inner.get()) == 0
}
-
#[inline]
+ #[cfg(not(target_os = "dragonfly"))]
pub unsafe fn destroy(&self) {
+ let r = libc::pthread_mutex_destroy(self.inner.get());
+ debug_assert_eq!(r, 0);
+ }
+ #[inline]
+ #[cfg(target_os = "dragonfly")]
+ pub unsafe fn destroy(&self) {
+ use libc;
+ let r = libc::pthread_mutex_destroy(self.inner.get());
+ // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a
+ // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER.
+ // Once it is used (locked/unlocked) or pthread_mutex_init() is called,
+ // this behaviour no longer occurs.
+ debug_assert!(r == 0 || r == libc::EINVAL);
}
}
-pub struct ReentrantMutex { inner: UnsafeCell<::libctru::RecursiveLock> }
+pub struct ReentrantMutex { inner: UnsafeCell<libc::pthread_mutex_t> }
unsafe impl Send for ReentrantMutex {}
unsafe impl Sync for ReentrantMutex {}
@@ -67,24 +106,35 @@ impl ReentrantMutex {
}
pub unsafe fn init(&mut self) {
- ::libctru::RecursiveLock_Init(self.inner.get());
+ let mut attr: libc::pthread_mutexattr_t = mem::uninitialized();
+ let result = libc::pthread_mutexattr_init(&mut attr as *mut _);
+ debug_assert_eq!(result, 0);
+ let result = libc::pthread_mutexattr_settype(&mut attr as *mut _,
+ libc::PTHREAD_MUTEX_RECURSIVE);
+ debug_assert_eq!(result, 0);
+ let result = libc::pthread_mutex_init(self.inner.get(), &attr as *const _);
+ debug_assert_eq!(result, 0);
+ let result = libc::pthread_mutexattr_destroy(&mut attr as *mut _);
+ debug_assert_eq!(result, 0);
}
pub unsafe fn lock(&self) {
- ::libctru::RecursiveLock_Lock(self.inner.get());
+ let result = libc::pthread_mutex_lock(self.inner.get());
+ debug_assert_eq!(result, 0);
}
#[inline]
pub unsafe fn try_lock(&self) -> bool {
- match ::libctru::RecursiveLock_TryLock(self.inner.get()) {
- 0 => false,
- _ => true,
- }
+ libc::pthread_mutex_trylock(self.inner.get()) == 0
}
pub unsafe fn unlock(&self) {
- ::libctru::RecursiveLock_Unlock(self.inner.get());
+ let result = libc::pthread_mutex_unlock(self.inner.get());
+ debug_assert_eq!(result, 0);
}
- pub unsafe fn destroy(&self) {}
+ pub unsafe fn destroy(&self) {
+ let result = libc::pthread_mutex_destroy(self.inner.get());
+ debug_assert_eq!(result, 0);
+ }
}
diff --git a/ctr-std/src/sys/unix/net.rs b/ctr-std/src/sys/unix/net.rs
index 94f757d..04d9f0b 100644
--- a/ctr-std/src/sys/unix/net.rs
+++ b/ctr-std/src/sys/unix/net.rs
@@ -8,11 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![allow(dead_code)]
-
use ffi::CStr;
use io;
-use libc::{self, c_int, c_void, size_t, sockaddr, socklen_t, EAI_NONAME as EAI_SYSTEM, MSG_PEEK};
+use libc::{self, c_int, c_void, size_t, sockaddr, socklen_t, EAI_SYSTEM, MSG_PEEK};
use mem;
use net::{SocketAddr, Shutdown};
use str;
@@ -25,7 +23,6 @@ use cmp;
pub use sys::{cvt, cvt_r};
pub extern crate libc as netc;
-#[allow(non_camel_case_types)]
pub type wrlen_t = size_t;
// See below for the usage of SOCK_CLOEXEC, but this constant is only defined on
@@ -54,12 +51,16 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> {
if err == 0 {
return Ok(())
}
+
+ // We may need to trigger a glibc workaround. See on_resolver_failure() for details.
+ on_resolver_failure();
+
if err == EAI_SYSTEM {
return Err(io::Error::last_os_error())
}
let detail = unsafe {
- str::from_utf8(CStr::from_ptr(libc::gai_strerror(err) as *const _).to_bytes()).unwrap()
+ str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap()
.to_owned()
};
Err(io::Error::new(io::ErrorKind::Other,
@@ -143,7 +144,7 @@ impl Socket {
let mut pollfd = libc::pollfd {
fd: self.0.raw(),
- events: 0x10, //libc::POLLOUT; value in the `libc` crate is currently incorrect
+ events: libc::POLLOUT,
revents: 0,
};
@@ -181,9 +182,7 @@ impl Socket {
_ => {
// linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look
// for POLLHUP rather than read readiness
- //
- // libc::POLLHUP should be 0x4. the `libc` crate incorrectly lists it as 0x10
- if pollfd.revents & 0x4 != 0 {
+ if pollfd.revents & libc::POLLHUP != 0 {
let e = self.take_error()?
.unwrap_or_else(|| {
io::Error::new(io::ErrorKind::Other, "no error set after POLLHUP")
@@ -199,6 +198,26 @@ impl Socket {
pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t)
-> io::Result<Socket> {
+ // Unfortunately the only known way right now to accept a socket and
+ // atomically set the CLOEXEC flag is to use the `accept4` syscall on
+ // Linux. This was added in 2.6.28, however, and because we support
+ // 2.6.18 we must detect this support dynamically.
+ if cfg!(target_os = "linux") {
+ weak! {
+ fn accept4(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int
+ }
+ if let Some(accept) = accept4.get() {
+ let res = cvt_r(|| unsafe {
+ accept(self.0.raw(), storage, len, SOCK_CLOEXEC)
+ });
+ match res {
+ Ok(fd) => return Ok(Socket(FileDesc::new(fd))),
+ Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {}
+ Err(e) => return Err(e),
+ }
+ }
+ }
+
let fd = cvt_r(|| unsafe {
libc::accept(self.0.raw(), storage, len)
})?;
@@ -320,7 +339,8 @@ impl Socket {
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
- self.0.set_nonblocking(nonblocking)
+ let mut nonblocking = nonblocking as libc::c_int;
+ cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(|_| ())
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
@@ -362,36 +382,21 @@ impl IntoInner<c_int> for Socket {
// against glibc version < 2.26. (That is, when we both know its needed and
// believe it's thread-safe).
#[cfg(target_env = "gnu")]
-pub fn res_init_if_glibc_before_2_26() -> io::Result<()> {
+fn on_resolver_failure() {
+ use sys;
+
// If the version fails to parse, we treat it the same as "not glibc".
- if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) {
- if let Some(version) = parse_glibc_version(version_str) {
- if version < (2, 26) {
- let ret = unsafe { libc::res_init() };
- if ret != 0 {
- return Err(io::Error::last_os_error());
- }
- }
+ if let Some(version) = sys::os::glibc_version() {
+ if version < (2, 26) {
+ unsafe { libc::res_init() };
}
}
- Ok(())
}
-fn glibc_version_cstr() -> Option<&'static CStr> {
- None
-}
-
-// Returns Some((major, minor)) if the string is a valid "x.y" version,
-// ignoring any extra dot-separated parts. Otherwise return None.
-fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
- let mut parsed_ints = version.split(".").map(str::parse::<usize>).fuse();
- match (parsed_ints.next(), parsed_ints.next()) {
- (Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)),
- _ => None
- }
-}
+#[cfg(not(target_env = "gnu"))]
+fn on_resolver_failure() {}
-#[cfg(test)]
+#[cfg(all(test, taget_env = "gnu"))]
mod test {
use super::*;
diff --git a/ctr-std/src/sys/unix/os.rs b/ctr-std/src/sys/unix/os.rs
index 3a944dc..4c86fdd 100644
--- a/ctr-std/src/sys/unix/os.rs
+++ b/ctr-std/src/sys/unix/os.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -8,35 +8,86 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+//! Implementation of `std::os` functionality for unix systems
+
+#![allow(unused_imports)] // lots of cfg code here
+
+use os::unix::prelude::*;
+
use error::Error as StdError;
use ffi::{CString, CStr, OsString, OsStr};
use fmt;
use io;
use iter;
-use libc::{self, c_int, c_char};
+use libc::{self, c_int, c_char, c_void};
+use marker::PhantomData;
+use mem;
+use memchr;
use path::{self, PathBuf};
+use ptr;
use slice;
use str;
-use sys::{unsupported, Void};
-use sys::unix::ext::ffi::{OsStrExt, OsStringExt};
+use sys_common::mutex::Mutex;
+use sys::cvt;
+use sys::fd;
+use vec;
const TMPBUF_SZ: usize = 128;
+static ENV_LOCK: Mutex = Mutex::new();
+
-extern "C" {
- fn __errno() -> *mut c_int;
+extern {
+ #[cfg(not(target_os = "dragonfly"))]
+ #[cfg_attr(any(target_os = "linux",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "l4re"),
+ link_name = "__errno_location")]
+ #[cfg_attr(any(target_os = "bitrig",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "android",
+ target_env = "newlib"),
+ link_name = "__errno")]
+ #[cfg_attr(target_os = "solaris", link_name = "___errno")]
+ #[cfg_attr(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd"),
+ link_name = "__error")]
+ #[cfg_attr(target_os = "haiku", link_name = "_errnop")]
+ fn errno_location() -> *mut c_int;
}
/// Returns the platform-specific value of errno
+#[cfg(not(target_os = "dragonfly"))]
pub fn errno() -> i32 {
unsafe {
- (*__errno()) as i32
+ (*errno_location()) as i32
+ }
+}
+
+/// Sets the platform-specific value of errno
+#[cfg(any(target_os = "solaris", target_os = "fuchsia"))] // only needed for readdir so far
+pub fn set_errno(e: i32) {
+ unsafe {
+ *errno_location() = e as c_int
}
}
+#[cfg(target_os = "dragonfly")]
+pub fn errno() -> i32 {
+ extern {
+ #[thread_local]
+ static errno: c_int;
+ }
+
+ unsafe { errno as i32 }
+}
+
/// Gets a detailed string description for the given error number.
pub fn error_string(errno: i32) -> String {
extern {
- #[cfg_attr(any(target_os = "linux", target_env = "newlib"),
+ #[cfg_attr(any(target_os = "linux", target_env = "newlib"),
link_name = "__xpg_strerror_r")]
fn strerror_r(errnum: c_int, buf: *mut c_char,
buflen: libc::size_t) -> c_int;
@@ -61,7 +112,7 @@ pub fn getcwd() -> io::Result<PathBuf> {
unsafe {
let ptr = buf.as_mut_ptr() as *mut libc::c_char;
if !libc::getcwd(ptr, buf.capacity()).is_null() {
- let len = CStr::from_ptr(buf.as_ptr() as *const _).to_bytes().len();
+ let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len();
buf.set_len(len);
buf.shrink_to_fit();
return Ok(PathBuf::from(OsString::from_vec(buf)));
@@ -78,14 +129,14 @@ pub fn getcwd() -> io::Result<PathBuf> {
buf.set_len(cap);
buf.reserve(1);
}
- }
+ }
}
pub fn chdir(p: &path::Path) -> io::Result<()> {
let p: &OsStr = p.as_ref();
let p = CString::new(p.as_bytes())?;
unsafe {
- match libc::chdir(p.as_ptr() as *const _) == (0 as c_int) {
+ match libc::chdir(p.as_ptr()) == (0 as c_int) {
true => Ok(()),
false => Err(io::Error::last_os_error()),
}
@@ -145,41 +196,343 @@ impl StdError for JoinPathsError {
fn description(&self) -> &str { "failed to join paths" }
}
+#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub fn current_exe() -> io::Result<PathBuf> {
- unsupported()
+ unsafe {
+ let mut mib = [libc::CTL_KERN as c_int,
+ libc::KERN_PROC as c_int,
+ libc::KERN_PROC_PATHNAME as c_int,
+ -1 as c_int];
+ let mut sz = 0;
+ cvt(libc::sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint,
+ ptr::null_mut(), &mut sz, ptr::null_mut(), 0))?;
+ if sz == 0 {
+ return Err(io::Error::last_os_error())
+ }
+ let mut v: Vec<u8> = Vec::with_capacity(sz);
+ cvt(libc::sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint,
+ v.as_mut_ptr() as *mut libc::c_void, &mut sz,
+ ptr::null_mut(), 0))?;
+ if sz == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ v.set_len(sz - 1); // chop off trailing NUL
+ Ok(PathBuf::from(OsString::from_vec(v)))
+ }
}
-pub struct Env(Void);
+#[cfg(target_os = "netbsd")]
+pub fn current_exe() -> io::Result<PathBuf> {
+ fn sysctl() -> io::Result<PathBuf> {
+ unsafe {
+ let mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, -1, libc::KERN_PROC_PATHNAME];
+ let mut path_len: usize = 0;
+ cvt(libc::sysctl(mib.as_ptr(), mib.len() as ::libc::c_uint,
+ ptr::null_mut(), &mut path_len,
+ ptr::null(), 0))?;
+ if path_len <= 1 {
+ return Err(io::Error::new(io::ErrorKind::Other,
+ "KERN_PROC_PATHNAME sysctl returned zero-length string"))
+ }
+ let mut path: Vec<u8> = Vec::with_capacity(path_len);
+ cvt(libc::sysctl(mib.as_ptr(), mib.len() as ::libc::c_uint,
+ path.as_ptr() as *mut libc::c_void, &mut path_len,
+ ptr::null(), 0))?;
+ path.set_len(path_len - 1); // chop off NUL
+ Ok(PathBuf::from(OsString::from_vec(path)))
+ }
+ }
+ fn procfs() -> io::Result<PathBuf> {
+ let curproc_exe = path::Path::new("/proc/curproc/exe");
+ if curproc_exe.is_file() {
+ return ::fs::read_link(curproc_exe);
+ }
+ Err(io::Error::new(io::ErrorKind::Other,
+ "/proc/curproc/exe doesn't point to regular file."))
+ }
+ sysctl().or_else(|_| procfs())
+}
+
+#[cfg(any(target_os = "bitrig", target_os = "openbsd"))]
+pub fn current_exe() -> io::Result<PathBuf> {
+ unsafe {
+ let mut mib = [libc::CTL_KERN,
+ libc::KERN_PROC_ARGS,
+ libc::getpid(),
+ libc::KERN_PROC_ARGV];
+ let mib = mib.as_mut_ptr();
+ let mut argv_len = 0;
+ cvt(libc::sysctl(mib, 4, ptr::null_mut(), &mut argv_len,
+ ptr::null_mut(), 0))?;
+ let mut argv = Vec::<*const libc::c_char>::with_capacity(argv_len as usize);
+ cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _,
+ &mut argv_len, ptr::null_mut(), 0))?;
+ argv.set_len(argv_len as usize);
+ if argv[0].is_null() {
+ return Err(io::Error::new(io::ErrorKind::Other,
+ "no current exe available"))
+ }
+ let argv0 = CStr::from_ptr(argv[0]).to_bytes();
+ if argv0[0] == b'.' || argv0.iter().any(|b| *b == b'/') {
+ ::fs::canonicalize(OsStr::from_bytes(argv0))
+ } else {
+ Ok(PathBuf::from(OsStr::from_bytes(argv0)))
+ }
+ }
+}
+
+#[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))]
+pub fn current_exe() -> io::Result<PathBuf> {
+ let selfexe = PathBuf::from("/proc/self/exe");
+ if selfexe.exists() {
+ ::fs::read_link(selfexe)
+ } else {
+ Err(io::Error::new(io::ErrorKind::Other, "no /proc/self/exe available. Is /proc mounted?"))
+ }
+}
+
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+pub fn current_exe() -> io::Result<PathBuf> {
+ extern {
+ fn _NSGetExecutablePath(buf: *mut libc::c_char,
+ bufsize: *mut u32) -> libc::c_int;
+ }
+ unsafe {
+ let mut sz: u32 = 0;
+ _NSGetExecutablePath(ptr::null_mut(), &mut sz);
+ if sz == 0 { return Err(io::Error::last_os_error()); }
+ let mut v: Vec<u8> = Vec::with_capacity(sz as usize);
+ let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz);
+ if err != 0 { return Err(io::Error::last_os_error()); }
+ v.set_len(sz as usize - 1); // chop off trailing NUL
+ Ok(PathBuf::from(OsString::from_vec(v)))
+ }
+}
+
+#[cfg(any(target_os = "solaris"))]
+pub fn current_exe() -> io::Result<PathBuf> {
+ extern {
+ fn getexecname() -> *const c_char;
+ }
+ unsafe {
+ let path = getexecname();
+ if path.is_null() {
+ Err(io::Error::last_os_error())
+ } else {
+ let filename = CStr::from_ptr(path).to_bytes();
+ let path = PathBuf::from(<OsStr as OsStrExt>::from_bytes(filename));
+
+ // Prepend a current working directory to the path if
+ // it doesn't contain an absolute pathname.
+ if filename[0] == b'/' {
+ Ok(path)
+ } else {
+ getcwd().map(|cwd| cwd.join(path))
+ }
+ }
+ }
+}
+
+#[cfg(target_os = "haiku")]
+pub fn current_exe() -> io::Result<PathBuf> {
+ // Use Haiku's image info functions
+ #[repr(C)]
+ struct image_info {
+ id: i32,
+ type_: i32,
+ sequence: i32,
+ init_order: i32,
+ init_routine: *mut libc::c_void, // function pointer
+ term_routine: *mut libc::c_void, // function pointer
+ device: libc::dev_t,
+ node: libc::ino_t,
+ name: [libc::c_char; 1024], // MAXPATHLEN
+ text: *mut libc::c_void,
+ data: *mut libc::c_void,
+ text_size: i32,
+ data_size: i32,
+ api_version: i32,
+ abi: i32,
+ }
+
+ unsafe {
+ extern {
+ fn _get_next_image_info(team_id: i32, cookie: *mut i32,
+ info: *mut image_info, size: i32) -> i32;
+ }
+
+ let mut info: image_info = mem::zeroed();
+ let mut cookie: i32 = 0;
+ // the executable can be found at team id 0
+ let result = _get_next_image_info(0, &mut cookie, &mut info,
+ mem::size_of::<image_info>() as i32);
+ if result != 0 {
+ use io::ErrorKind;
+ Err(io::Error::new(ErrorKind::Other, "Error getting executable path"))
+ } else {
+ let name = CStr::from_ptr(info.name.as_ptr()).to_bytes();
+ Ok(PathBuf::from(OsStr::from_bytes(name)))
+ }
+ }
+}
+
+#[cfg(any(target_os = "fuchsia", target_os = "l4re"))]
+pub fn current_exe() -> io::Result<PathBuf> {
+ use io::ErrorKind;
+ Err(io::Error::new(ErrorKind::Other, "Not yet implemented!"))
+}
+
+pub struct Env {
+ iter: vec::IntoIter<(OsString, OsString)>,
+ _dont_send_or_sync_me: PhantomData<*mut ()>,
+}
impl Iterator for Env {
type Item = (OsString, OsString);
- fn next(&mut self) -> Option<(OsString, OsString)> {
- match self.0 {}
- }
+ fn next(&mut self) -> Option<(OsString, OsString)> { self.iter.next() }
+ fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
+}
+
+#[cfg(target_os = "macos")]
+pub unsafe fn environ() -> *mut *const *const c_char {
+ extern { fn _NSGetEnviron() -> *mut *const *const c_char; }
+ _NSGetEnviron()
}
+#[cfg(not(target_os = "macos"))]
+pub unsafe fn environ() -> *mut *const *const c_char {
+ extern { static mut environ: *const *const c_char; }
+ &mut environ
+}
+
+/// Returns a vector of (variable, value) byte-vector pairs for all the
+/// environment variables of the current process.
pub fn env() -> Env {
- panic!("not supported on 3DS yet")
+ unsafe {
+ ENV_LOCK.lock();
+ let mut environ = *environ();
+ if environ == ptr::null() {
+ ENV_LOCK.unlock();
+ panic!("os::env() failure getting env string from OS: {}",
+ io::Error::last_os_error());
+ }
+ let mut result = Vec::new();
+ while *environ != ptr::null() {
+ if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
+ result.push(key_value);
+ }
+ environ = environ.offset(1);
+ }
+ let ret = Env {
+ iter: result.into_iter(),
+ _dont_send_or_sync_me: PhantomData,
+ };
+ ENV_LOCK.unlock();
+ return ret
+ }
+
+ fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
+ // Strategy (copied from glibc): Variable name and value are separated
+ // by an ASCII equals sign '='. Since a variable name must not be
+ // empty, allow variable names starting with an equals sign. Skip all
+ // malformed lines.
+ if input.is_empty() {
+ return None;
+ }
+ let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
+ pos.map(|p| (
+ OsStringExt::from_vec(input[..p].to_vec()),
+ OsStringExt::from_vec(input[p+1..].to_vec()),
+ ))
+ }
}
-pub fn getenv(_k: &OsStr) -> io::Result<Option<OsString>> {
- return Ok(None)
+pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
+ // environment variables with a nul byte can't be set, so their value is
+ // always None as well
+ let k = CString::new(k.as_bytes())?;
+ unsafe {
+ ENV_LOCK.lock();
+ let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
+ let ret = if s.is_null() {
+ None
+ } else {
+ Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
+ };
+ ENV_LOCK.unlock();
+ return Ok(ret)
+ }
+}
+
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+ let k = CString::new(k.as_bytes())?;
+ let v = CString::new(v.as_bytes())?;
+
+ unsafe {
+ ENV_LOCK.lock();
+ let ret = cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(|_| ());
+ ENV_LOCK.unlock();
+ return ret
+ }
}
-pub fn setenv(_k: &OsStr, _v: &OsStr) -> io::Result<()> {
- unsupported()
+pub fn unsetenv(n: &OsStr) -> io::Result<()> {
+ let nbuf = CString::new(n.as_bytes())?;
+
+ unsafe {
+ ENV_LOCK.lock();
+ let ret = cvt(libc::unsetenv(nbuf.as_ptr())).map(|_| ());
+ ENV_LOCK.unlock();
+ return ret
+ }
}
-pub fn unsetenv(_n: &OsStr) -> io::Result<()> {
- unsupported()
+pub fn page_size() -> usize {
+ unsafe {
+ libc::sysconf(libc::_SC_PAGESIZE) as usize
+ }
}
pub fn temp_dir() -> PathBuf {
- PathBuf::from("/tmp")
+ ::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| {
+ if cfg!(target_os = "android") {
+ PathBuf::from("/data/local/tmp")
+ } else {
+ PathBuf::from("/tmp")
+ }
+ })
}
pub fn home_dir() -> Option<PathBuf> {
- None
+ return ::env::var_os("HOME").or_else(|| unsafe {
+ fallback()
+ }).map(PathBuf::from);
+
+ #[cfg(any(target_os = "android",
+ target_os = "ios",
+ target_os = "emscripten"))]
+ unsafe fn fallback() -> Option<OsString> { None }
+ #[cfg(not(any(target_os = "android",
+ target_os = "ios",
+ target_os = "emscripten")))]
+ unsafe fn fallback() -> Option<OsString> {
+ let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
+ n if n < 0 => 512 as usize,
+ n => n as usize,
+ };
+ let mut buf = Vec::with_capacity(amt);
+ let mut passwd: libc::passwd = mem::zeroed();
+ let mut result = ptr::null_mut();
+ match libc::getpwuid_r(libc::getuid(), &mut passwd, buf.as_mut_ptr(),
+ buf.capacity(), &mut result) {
+ 0 if !result.is_null() => {
+ let ptr = passwd.pw_dir as *const _;
+ let bytes = CStr::from_ptr(ptr).to_bytes().to_vec();
+ Some(OsStringExt::from_vec(bytes))
+ },
+ _ => None,
+ }
+ }
}
pub fn exit(code: i32) -> ! {
@@ -187,5 +540,41 @@ pub fn exit(code: i32) -> ! {
}
pub fn getpid() -> u32 {
- panic!("no pids on 3DS")
+ unsafe { libc::getpid() as u32 }
+}
+
+pub fn getppid() -> u32 {
+ unsafe { libc::getppid() as u32 }
+}
+
+#[cfg(target_env = "gnu")]
+pub fn glibc_version() -> Option<(usize, usize)> {
+ if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) {
+ parse_glibc_version(version_str)
+ } else {
+ None
+ }
+}
+
+#[cfg(target_env = "gnu")]
+fn glibc_version_cstr() -> Option<&'static CStr> {
+ weak! {
+ fn gnu_get_libc_version() -> *const libc::c_char
+ }
+ if let Some(f) = gnu_get_libc_version.get() {
+ unsafe { Some(CStr::from_ptr(f())) }
+ } else {
+ None
+ }
+}
+
+// Returns Some((major, minor)) if the string is a valid "x.y" version,
+// ignoring any extra dot-separated parts. Otherwise return None.
+#[cfg(target_env = "gnu")]
+fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
+ let mut parsed_ints = version.split(".").map(str::parse::<usize>).fuse();
+ match (parsed_ints.next(), parsed_ints.next()) {
+ (Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)),
+ _ => None
+ }
}
diff --git a/ctr-std/src/sys/unix/os_str.rs b/ctr-std/src/sys/unix/os_str.rs
index e0da5bd..01c0fb8 100644
--- a/ctr-std/src/sys/unix/os_str.rs
+++ b/ctr-std/src/sys/unix/os_str.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
diff --git a/ctr-std/src/sys/unix/path.rs b/ctr-std/src/sys/unix/path.rs
index d96473b..bf9af7a 100644
--- a/ctr-std/src/sys/unix/path.rs
+++ b/ctr-std/src/sys/unix/path.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -21,16 +21,8 @@ pub fn is_verbatim_sep(b: u8) -> bool {
b == b'/'
}
-pub fn parse_prefix(path: &OsStr) -> Option<Prefix> {
- if let Some(path_str) = path.to_str() {
- if let Some(i) = path_str.find(':') {
- Some(Prefix::Verbatim(OsStr::new(&path_str[..i])))
- } else {
- None
- }
- } else {
- None
- }
+pub fn parse_prefix(_: &OsStr) -> Option<Prefix> {
+ None
}
pub const MAIN_SEP_STR: &'static str = "/";
diff --git a/ctr-std/src/sys/unix/pipe.rs b/ctr-std/src/sys/unix/pipe.rs
index 992e1ac..0a5dccd 100644
--- a/ctr-std/src/sys/unix/pipe.rs
+++ b/ctr-std/src/sys/unix/pipe.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -9,27 +9,123 @@
// except according to those terms.
use io;
-use sys::Void;
+use libc::{self, c_int};
+use mem;
+use sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
+use sys::fd::FileDesc;
+use sys::{cvt, cvt_r};
-pub struct AnonPipe(Void);
+////////////////////////////////////////////////////////////////////////////////
+// Anonymous pipes
+////////////////////////////////////////////////////////////////////////////////
-impl AnonPipe {
- pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
- match self.0 {}
+pub struct AnonPipe(FileDesc);
+
+pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
+ weak! { fn pipe2(*mut c_int, c_int) -> c_int }
+ static INVALID: AtomicBool = ATOMIC_BOOL_INIT;
+
+ let mut fds = [0; 2];
+
+ // Unfortunately the only known way right now to create atomically set the
+ // CLOEXEC flag is to use the `pipe2` syscall on Linux. This was added in
+ // 2.6.27, however, and because we support 2.6.18 we must detect this
+ // support dynamically.
+ if cfg!(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd")) &&
+ !INVALID.load(Ordering::SeqCst)
+ {
+
+ if let Some(pipe) = pipe2.get() {
+ // Note that despite calling a glibc function here we may still
+ // get ENOSYS. Glibc has `pipe2` since 2.9 and doesn't try to
+ // emulate on older kernels, so if you happen to be running on
+ // an older kernel you may see `pipe2` as a symbol but still not
+ // see the syscall.
+ match cvt(unsafe { pipe(fds.as_mut_ptr(), libc::O_CLOEXEC) }) {
+ Ok(_) => {
+ return Ok((AnonPipe(FileDesc::new(fds[0])),
+ AnonPipe(FileDesc::new(fds[1]))));
+ }
+ Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {
+ INVALID.store(true, Ordering::SeqCst);
+ }
+ Err(e) => return Err(e),
+ }
+ }
}
+ cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
+
+ let fd0 = FileDesc::new(fds[0]);
+ let fd1 = FileDesc::new(fds[1]);
+ fd0.set_cloexec()?;
+ fd1.set_cloexec()?;
+ Ok((AnonPipe(fd0), AnonPipe(fd1)))
+}
- pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
- match self.0 {}
+impl AnonPipe {
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
}
- pub fn diverge(&self) -> ! {
- match self.0 {}
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
}
+
+ pub fn fd(&self) -> &FileDesc { &self.0 }
+ pub fn into_fd(self) -> FileDesc { self.0 }
}
pub fn read2(p1: AnonPipe,
- _v1: &mut Vec<u8>,
- _p2: AnonPipe,
- _v2: &mut Vec<u8>) -> io::Result<()> {
- match p1.0 {}
+ v1: &mut Vec<u8>,
+ p2: AnonPipe,
+ v2: &mut Vec<u8>) -> io::Result<()> {
+
+ // Set both pipes into nonblocking mode as we're gonna be reading from both
+ // in the `select` loop below, and we wouldn't want one to block the other!
+ let p1 = p1.into_fd();
+ let p2 = p2.into_fd();
+ p1.set_nonblocking(true)?;
+ p2.set_nonblocking(true)?;
+
+ let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
+ fds[0].fd = p1.raw();
+ fds[0].events = libc::POLLIN;
+ fds[1].fd = p2.raw();
+ fds[1].events = libc::POLLIN;
+ loop {
+ // wait for either pipe to become readable using `poll`
+ cvt_r(|| unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) })?;
+
+ if fds[0].revents != 0 && read(&p1, v1)? {
+ p2.set_nonblocking(false)?;
+ return p2.read_to_end(v2).map(|_| ());
+ }
+ if fds[1].revents != 0 && read(&p2, v2)? {
+ p1.set_nonblocking(false)?;
+ return p1.read_to_end(v1).map(|_| ());
+ }
+ }
+
+ // Read as much as we can from each pipe, ignoring EWOULDBLOCK or
+ // EAGAIN. If we hit EOF, then this will happen because the underlying
+ // reader will return Ok(0), in which case we'll see `Ok` ourselves. In
+ // this case we flip the other fd back into blocking mode and read
+ // whatever's leftover on that file descriptor.
+ fn read(fd: &FileDesc, dst: &mut Vec<u8>) -> Result<bool, io::Error> {
+ match fd.read_to_end(dst) {
+ Ok(_) => Ok(true),
+ Err(e) => {
+ if e.raw_os_error() == Some(libc::EWOULDBLOCK) ||
+ e.raw_os_error() == Some(libc::EAGAIN) {
+ Ok(false)
+ } else {
+ Err(e)
+ }
+ }
+ }
+ }
}
diff --git a/ctr-std/src/sys/unix/process/mod.rs b/ctr-std/src/sys/unix/process/mod.rs
new file mode 100644
index 0000000..d8ac26c
--- /dev/null
+++ b/ctr-std/src/sys/unix/process/mod.rs
@@ -0,0 +1,22 @@
+// 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.
+
+pub use self::process_common::{Command, ExitStatus, ExitCode, Stdio, StdioPipes};
+pub use self::process_inner::Process;
+
+mod process_common;
+#[cfg(not(target_os = "fuchsia"))]
+#[path = "process_unix.rs"]
+mod process_inner;
+#[cfg(target_os = "fuchsia")]
+#[path = "process_fuchsia.rs"]
+mod process_inner;
+#[cfg(target_os = "fuchsia")]
+mod zircon;
diff --git a/ctr-std/src/sys/unix/process/process_common.rs b/ctr-std/src/sys/unix/process/process_common.rs
new file mode 100644
index 0000000..6396bb3
--- /dev/null
+++ b/ctr-std/src/sys/unix/process/process_common.rs
@@ -0,0 +1,510 @@
+// 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::unix::prelude::*;
+
+use ffi::{OsString, OsStr, CString, CStr};
+use fmt;
+use io;
+use libc::{self, c_int, gid_t, uid_t, c_char, EXIT_SUCCESS, EXIT_FAILURE};
+use ptr;
+use sys::fd::FileDesc;
+use sys::fs::{File, OpenOptions};
+use sys::pipe::{self, AnonPipe};
+use sys_common::process::{CommandEnv, DefaultEnvKey};
+use collections::BTreeMap;
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct Command {
+ // Currently we try hard to ensure that the call to `.exec()` doesn't
+ // actually allocate any memory. While many platforms try to ensure that
+ // memory allocation works after a fork in a multithreaded process, it's
+ // been observed to be buggy and somewhat unreliable, so we do our best to
+ // just not do it at all!
+ //
+ // Along those lines, the `argv` and `envp` raw pointers here are exactly
+ // what's gonna get passed to `execvp`. The `argv` array starts with the
+ // `program` and ends with a NULL, and the `envp` pointer, if present, is
+ // also null-terminated.
+ //
+ // Right now we don't support removing arguments, so there's no much fancy
+ // support there, but we support adding and removing environment variables,
+ // so a side table is used to track where in the `envp` array each key is
+ // located. Whenever we add a key we update it in place if it's already
+ // present, and whenever we remove a key we update the locations of all
+ // other keys.
+ program: CString,
+ args: Vec<CString>,
+ argv: Argv,
+ env: CommandEnv<DefaultEnvKey>,
+
+ cwd: Option<CString>,
+ uid: Option<uid_t>,
+ gid: Option<gid_t>,
+ saw_nul: bool,
+ closures: Vec<Box<FnMut() -> io::Result<()> + Send + Sync>>,
+ stdin: Option<Stdio>,
+ stdout: Option<Stdio>,
+ stderr: Option<Stdio>,
+}
+
+// Create a new type for argv, so that we can make it `Send`
+struct Argv(Vec<*const c_char>);
+
+// It is safe to make Argv Send, because it contains pointers to memory owned by `Command.args`
+unsafe impl Send for Argv {}
+
+// passed back to std::process with the pipes connected to the child, if any
+// were requested
+pub struct StdioPipes {
+ pub stdin: Option<AnonPipe>,
+ pub stdout: Option<AnonPipe>,
+ pub stderr: Option<AnonPipe>,
+}
+
+// passed to do_exec() with configuration of what the child stdio should look
+// like
+pub struct ChildPipes {
+ pub stdin: ChildStdio,
+ pub stdout: ChildStdio,
+ pub stderr: ChildStdio,
+}
+
+pub enum ChildStdio {
+ Inherit,
+ Explicit(c_int),
+ Owned(FileDesc),
+}
+
+pub enum Stdio {
+ Inherit,
+ Null,
+ MakePipe,
+ Fd(FileDesc),
+}
+
+impl Command {
+ pub fn new(program: &OsStr) -> Command {
+ let mut saw_nul = false;
+ let program = os2c(program, &mut saw_nul);
+ Command {
+ argv: Argv(vec![program.as_ptr(), ptr::null()]),
+ program,
+ args: Vec::new(),
+ env: Default::default(),
+ cwd: None,
+ uid: None,
+ gid: None,
+ saw_nul,
+ closures: Vec::new(),
+ stdin: None,
+ stdout: None,
+ stderr: None,
+ }
+ }
+
+ pub fn arg(&mut self, arg: &OsStr) {
+ // Overwrite the trailing NULL pointer in `argv` and then add a new null
+ // pointer.
+ let arg = os2c(arg, &mut self.saw_nul);
+ self.argv.0[self.args.len() + 1] = arg.as_ptr();
+ self.argv.0.push(ptr::null());
+
+ // Also make sure we keep track of the owned value to schedule a
+ // destructor for this memory.
+ self.args.push(arg);
+ }
+
+ pub fn cwd(&mut self, dir: &OsStr) {
+ self.cwd = Some(os2c(dir, &mut self.saw_nul));
+ }
+ pub fn uid(&mut self, id: uid_t) {
+ self.uid = Some(id);
+ }
+ pub fn gid(&mut self, id: gid_t) {
+ self.gid = Some(id);
+ }
+
+ pub fn saw_nul(&self) -> bool {
+ self.saw_nul
+ }
+ pub fn get_argv(&self) -> &Vec<*const c_char> {
+ &self.argv.0
+ }
+
+ #[allow(dead_code)]
+ pub fn get_cwd(&self) -> &Option<CString> {
+ &self.cwd
+ }
+ #[allow(dead_code)]
+ pub fn get_uid(&self) -> Option<uid_t> {
+ self.uid
+ }
+ #[allow(dead_code)]
+ pub fn get_gid(&self) -> Option<gid_t> {
+ self.gid
+ }
+
+ pub fn get_closures(&mut self) -> &mut Vec<Box<FnMut() -> io::Result<()> + Send + Sync>> {
+ &mut self.closures
+ }
+
+ pub fn before_exec(&mut self,
+ f: Box<FnMut() -> io::Result<()> + Send + Sync>) {
+ self.closures.push(f);
+ }
+
+ 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 env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> {
+ &mut self.env
+ }
+
+ pub fn capture_env(&mut self) -> Option<CStringArray> {
+ let maybe_env = self.env.capture_if_changed();
+ maybe_env.map(|env| construct_envp(env, &mut self.saw_nul))
+ }
+ #[allow(dead_code)]
+ pub fn env_saw_path(&self) -> bool {
+ self.env.have_changed_path()
+ }
+
+ pub fn setup_io(&self, default: Stdio, needs_stdin: bool)
+ -> io::Result<(StdioPipes, ChildPipes)> {
+ 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 (their_stdin, our_stdin) = stdin.to_child_stdio(true)?;
+ let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?;
+ let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?;
+ let ours = StdioPipes {
+ stdin: our_stdin,
+ stdout: our_stdout,
+ stderr: our_stderr,
+ };
+ let theirs = ChildPipes {
+ stdin: their_stdin,
+ stdout: their_stdout,
+ stderr: their_stderr,
+ };
+ Ok((ours, theirs))
+ }
+}
+
+fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString {
+ CString::new(s.as_bytes()).unwrap_or_else(|_e| {
+ *saw_nul = true;
+ CString::new("<string-with-nul>").unwrap()
+ })
+}
+
+// Helper type to manage ownership of the strings within a C-style array.
+pub struct CStringArray {
+ items: Vec<CString>,
+ ptrs: Vec<*const c_char>
+}
+
+impl CStringArray {
+ pub fn with_capacity(capacity: usize) -> Self {
+ let mut result = CStringArray {
+ items: Vec::with_capacity(capacity),
+ ptrs: Vec::with_capacity(capacity+1)
+ };
+ result.ptrs.push(ptr::null());
+ result
+ }
+ pub fn push(&mut self, item: CString) {
+ let l = self.ptrs.len();
+ self.ptrs[l-1] = item.as_ptr();
+ self.ptrs.push(ptr::null());
+ self.items.push(item);
+ }
+ pub fn as_ptr(&self) -> *const *const c_char {
+ self.ptrs.as_ptr()
+ }
+}
+
+fn construct_envp(env: BTreeMap<DefaultEnvKey, OsString>, saw_nul: &mut bool) -> CStringArray {
+ let mut result = CStringArray::with_capacity(env.len());
+ for (k, v) in env {
+ let mut k: OsString = k.into();
+
+ // Reserve additional space for '=' and null terminator
+ k.reserve_exact(v.len() + 2);
+ k.push("=");
+ k.push(&v);
+
+ // Add the new entry into the array
+ if let Ok(item) = CString::new(k.into_vec()) {
+ result.push(item);
+ } else {
+ *saw_nul = true;
+ }
+ }
+
+ result
+}
+
+impl Stdio {
+ pub fn to_child_stdio(&self, readable: bool)
+ -> io::Result<(ChildStdio, Option<AnonPipe>)> {
+ match *self {
+ Stdio::Inherit => {
+ Ok((ChildStdio::Inherit, None))
+ },
+
+ // Make sure that the source descriptors are not an stdio
+ // descriptor, otherwise the order which we set the child's
+ // descriptors may blow away a descriptor which we are hoping to
+ // save. For example, suppose we want the child's stderr to be the
+ // parent's stdout, and the child's stdout to be the parent's
+ // stderr. No matter which we dup first, the second will get
+ // overwritten prematurely.
+ Stdio::Fd(ref fd) => {
+ if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO {
+ Ok((ChildStdio::Owned(fd.duplicate()?), None))
+ } else {
+ Ok((ChildStdio::Explicit(fd.raw()), None))
+ }
+ }
+
+ Stdio::MakePipe => {
+ let (reader, writer) = pipe::anon_pipe()?;
+ let (ours, theirs) = if readable {
+ (writer, reader)
+ } else {
+ (reader, writer)
+ };
+ Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours)))
+ }
+
+ Stdio::Null => {
+ let mut opts = OpenOptions::new();
+ opts.read(readable);
+ opts.write(!readable);
+ let path = unsafe {
+ CStr::from_ptr("/dev/null\0".as_ptr() as *const _)
+ };
+ let fd = File::open_c(&path, &opts)?;
+ Ok((ChildStdio::Owned(fd.into_fd()), None))
+ }
+ }
+ }
+}
+
+impl From<AnonPipe> for Stdio {
+ fn from(pipe: AnonPipe) -> Stdio {
+ Stdio::Fd(pipe.into_fd())
+ }
+}
+
+impl From<File> for Stdio {
+ fn from(file: File) -> Stdio {
+ Stdio::Fd(file.into_fd())
+ }
+}
+
+impl ChildStdio {
+ pub fn fd(&self) -> Option<c_int> {
+ match *self {
+ ChildStdio::Inherit => None,
+ ChildStdio::Explicit(fd) => Some(fd),
+ ChildStdio::Owned(ref fd) => Some(fd.raw()),
+ }
+ }
+}
+
+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(())
+ }
+}
+
+/// Unix exit statuses
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatus(c_int);
+
+impl ExitStatus {
+ pub fn new(status: c_int) -> ExitStatus {
+ ExitStatus(status)
+ }
+
+ fn exited(&self) -> bool {
+ unsafe { libc::WIFEXITED(self.0) }
+ }
+
+ pub fn success(&self) -> bool {
+ self.code() == Some(0)
+ }
+
+ pub fn code(&self) -> Option<i32> {
+ if self.exited() {
+ Some(unsafe { libc::WEXITSTATUS(self.0) })
+ } else {
+ None
+ }
+ }
+
+ pub fn signal(&self) -> Option<i32> {
+ if !self.exited() {
+ Some(unsafe { libc::WTERMSIG(self.0) })
+ } else {
+ None
+ }
+ }
+}
+
+impl From<c_int> for ExitStatus {
+ fn from(a: c_int) -> ExitStatus {
+ ExitStatus(a)
+ }
+}
+
+impl fmt::Display for ExitStatus {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if let Some(code) = self.code() {
+ write!(f, "exit code: {}", code)
+ } else {
+ let signal = self.signal().unwrap();
+ write!(f, "signal: {}", signal)
+ }
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitCode(u8);
+
+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
+ }
+}
+
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests {
+ use super::*;
+
+ use ffi::OsStr;
+ use mem;
+ use ptr;
+ use libc;
+ use sys::cvt;
+
+ macro_rules! t {
+ ($e:expr) => {
+ match $e {
+ Ok(t) => t,
+ Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
+ }
+ }
+ }
+
+ // Android with api less than 21 define sig* functions inline, so it is not
+ // available for dynamic link. Implementing sigemptyset and sigaddset allow us
+ // to support older Android version (independent of libc version).
+ // The following implementations are based on https://git.io/vSkNf
+
+ #[cfg(not(target_os = "android"))]
+ extern {
+ #[cfg_attr(target_os = "netbsd", link_name = "__sigemptyset14")]
+ fn sigemptyset(set: *mut libc::sigset_t) -> libc::c_int;
+
+ #[cfg_attr(target_os = "netbsd", link_name = "__sigaddset14")]
+ fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int;
+ }
+
+ #[cfg(target_os = "android")]
+ unsafe fn sigemptyset(set: *mut libc::sigset_t) -> libc::c_int {
+ libc::memset(set as *mut _, 0, mem::size_of::<libc::sigset_t>());
+ return 0;
+ }
+
+ #[cfg(target_os = "android")]
+ unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int {
+ use slice;
+
+ let raw = slice::from_raw_parts_mut(set as *mut u8, mem::size_of::<libc::sigset_t>());
+ let bit = (signum - 1) as usize;
+ raw[bit / 8] |= 1 << (bit % 8);
+ return 0;
+ }
+
+ // See #14232 for more information, but it appears that signal delivery to a
+ // newly spawned process may just be raced in the macOS, so to prevent this
+ // test from being flaky we ignore it on macOS.
+ #[test]
+ #[cfg_attr(target_os = "macos", ignore)]
+ // When run under our current QEMU emulation test suite this test fails,
+ // although the reason isn't very clear as to why. For now this test is
+ // ignored there.
+ #[cfg_attr(target_arch = "arm", ignore)]
+ #[cfg_attr(target_arch = "aarch64", ignore)]
+ fn test_process_mask() {
+ unsafe {
+ // Test to make sure that a signal mask does not get inherited.
+ let mut cmd = Command::new(OsStr::new("cat"));
+
+ let mut set: libc::sigset_t = mem::uninitialized();
+ let mut old_set: libc::sigset_t = mem::uninitialized();
+ t!(cvt(sigemptyset(&mut set)));
+ t!(cvt(sigaddset(&mut set, libc::SIGINT)));
+ t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, &mut old_set)));
+
+ cmd.stdin(Stdio::MakePipe);
+ cmd.stdout(Stdio::MakePipe);
+
+ let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true));
+ let stdin_write = pipes.stdin.take().unwrap();
+ let stdout_read = pipes.stdout.take().unwrap();
+
+ t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &old_set,
+ ptr::null_mut())));
+
+ t!(cvt(libc::kill(cat.id() as libc::pid_t, libc::SIGINT)));
+ // We need to wait until SIGINT is definitely delivered. The
+ // easiest way is to write something to cat, and try to read it
+ // back: if SIGINT is unmasked, it'll get delivered when cat is
+ // next scheduled.
+ let _ = stdin_write.write(b"Hello");
+ drop(stdin_write);
+
+ // Either EOF or failure (EPIPE) is okay.
+ let mut buf = [0; 5];
+ if let Ok(ret) = stdout_read.read(&mut buf) {
+ assert_eq!(ret, 0);
+ }
+
+ t!(cat.wait());
+ }
+ }
+}
diff --git a/ctr-std/src/sys/unix/process/process_fuchsia.rs b/ctr-std/src/sys/unix/process/process_fuchsia.rs
new file mode 100644
index 0000000..fa48001
--- /dev/null
+++ b/ctr-std/src/sys/unix/process/process_fuchsia.rs
@@ -0,0 +1,181 @@
+// 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 io;
+use libc::{self, size_t};
+use mem;
+use ptr;
+
+use sys::process::zircon::{Handle, zx_handle_t};
+use sys::process::process_common::*;
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+impl Command {
+ pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
+ -> io::Result<(Process, StdioPipes)> {
+ let envp = self.capture_env();
+
+ if self.saw_nul() {
+ return Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "nul byte found in provided data"));
+ }
+
+ let (ours, theirs) = self.setup_io(default, needs_stdin)?;
+
+ let process_handle = unsafe { self.do_exec(theirs, envp.as_ref())? };
+
+ Ok((Process { handle: Handle::new(process_handle) }, ours))
+ }
+
+ pub fn exec(&mut self, default: Stdio) -> io::Error {
+ if self.saw_nul() {
+ return io::Error::new(io::ErrorKind::InvalidInput,
+ "nul byte found in provided data")
+ }
+
+ match self.setup_io(default, true) {
+ Ok((_, _)) => {
+ // FIXME: This is tough because we don't support the exec syscalls
+ unimplemented!();
+ },
+ Err(e) => e,
+ }
+ }
+
+ unsafe fn do_exec(&mut self, stdio: ChildPipes, maybe_envp: Option<&CStringArray>)
+ -> io::Result<zx_handle_t> {
+ use sys::process::zircon::*;
+
+ let envp = match maybe_envp {
+ Some(envp) => envp.as_ptr(),
+ None => ptr::null(),
+ };
+
+ let transfer_or_clone = |opt_fd, target_fd| if let Some(local_fd) = opt_fd {
+ fdio_spawn_action_t {
+ action: FDIO_SPAWN_ACTION_TRANSFER_FD,
+ local_fd,
+ target_fd,
+ ..Default::default()
+ }
+ } else {
+ fdio_spawn_action_t {
+ action: FDIO_SPAWN_ACTION_CLONE_FD,
+ local_fd: target_fd,
+ target_fd,
+ ..Default::default()
+ }
+ };
+
+ // Clone stdin, stdout, and stderr
+ let action1 = transfer_or_clone(stdio.stdin.fd(), 0);
+ let action2 = transfer_or_clone(stdio.stdout.fd(), 1);
+ let action3 = transfer_or_clone(stdio.stderr.fd(), 2);
+ let actions = [action1, action2, action3];
+
+ // We don't want FileDesc::drop to be called on any stdio. fdio_spawn_etc
+ // always consumes transferred file descriptors.
+ mem::forget(stdio);
+
+ for callback in self.get_closures().iter_mut() {
+ callback()?;
+ }
+
+ let mut process_handle: zx_handle_t = 0;
+ zx_cvt(fdio_spawn_etc(
+ 0,
+ FDIO_SPAWN_CLONE_JOB | FDIO_SPAWN_CLONE_LDSVC | FDIO_SPAWN_CLONE_NAMESPACE,
+ self.get_argv()[0], self.get_argv().as_ptr(), envp, 3, actions.as_ptr(),
+ &mut process_handle,
+ ptr::null_mut(),
+ ))?;
+ // FIXME: See if we want to do something with that err_msg
+
+ Ok(process_handle)
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Processes
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct Process {
+ handle: Handle,
+}
+
+impl Process {
+ pub fn id(&self) -> u32 {
+ self.handle.raw() as u32
+ }
+
+ pub fn kill(&mut self) -> io::Result<()> {
+ use sys::process::zircon::*;
+
+ unsafe { zx_cvt(zx_task_kill(self.handle.raw()))?; }
+
+ Ok(())
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ use default::Default;
+ use sys::process::zircon::*;
+
+ let mut proc_info: zx_info_process_t = Default::default();
+ let mut actual: size_t = 0;
+ let mut avail: size_t = 0;
+
+ unsafe {
+ zx_cvt(zx_object_wait_one(self.handle.raw(), ZX_TASK_TERMINATED,
+ ZX_TIME_INFINITE, ptr::null_mut()))?;
+ zx_cvt(zx_object_get_info(self.handle.raw(), ZX_INFO_PROCESS,
+ &mut proc_info as *mut _ as *mut libc::c_void,
+ mem::size_of::<zx_info_process_t>(), &mut actual,
+ &mut avail))?;
+ }
+ if actual != 1 {
+ return Err(io::Error::new(io::ErrorKind::InvalidData,
+ "Failed to get exit status of process"));
+ }
+ Ok(ExitStatus::new(proc_info.rec.return_code))
+ }
+
+ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+ use default::Default;
+ use sys::process::zircon::*;
+
+ let mut proc_info: zx_info_process_t = Default::default();
+ let mut actual: size_t = 0;
+ let mut avail: size_t = 0;
+
+ unsafe {
+ let status = zx_object_wait_one(self.handle.raw(), ZX_TASK_TERMINATED,
+ 0, ptr::null_mut());
+ match status {
+ 0 => { }, // Success
+ x if x == ERR_TIMED_OUT => {
+ return Ok(None);
+ },
+ _ => { panic!("Failed to wait on process handle: {}", status); },
+ }
+ zx_cvt(zx_object_get_info(self.handle.raw(), ZX_INFO_PROCESS,
+ &mut proc_info as *mut _ as *mut libc::c_void,
+ mem::size_of::<zx_info_process_t>(), &mut actual,
+ &mut avail))?;
+ }
+ if actual != 1 {
+ return Err(io::Error::new(io::ErrorKind::InvalidData,
+ "Failed to get exit status of process"));
+ }
+ Ok(Some(ExitStatus::new(proc_info.rec.return_code)))
+ }
+}
diff --git a/ctr-std/src/sys/unix/process/process_unix.rs b/ctr-std/src/sys/unix/process/process_unix.rs
new file mode 100644
index 0000000..9d6d607
--- /dev/null
+++ b/ctr-std/src/sys/unix/process/process_unix.rs
@@ -0,0 +1,405 @@
+// 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.
+
+use io::{self, Error, ErrorKind};
+use libc::{self, c_int, gid_t, pid_t, uid_t};
+use ptr;
+
+use sys::cvt;
+use sys::process::process_common::*;
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+impl Command {
+ pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
+ -> io::Result<(Process, StdioPipes)> {
+ use sys;
+
+ const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
+
+ let envp = self.capture_env();
+
+ if self.saw_nul() {
+ return Err(io::Error::new(ErrorKind::InvalidInput,
+ "nul byte found in provided data"));
+ }
+
+ let (ours, theirs) = self.setup_io(default, needs_stdin)?;
+
+ if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
+ return Ok((ret, ours))
+ }
+
+ let (input, output) = sys::pipe::anon_pipe()?;
+
+ let pid = unsafe {
+ match cvt(libc::fork())? {
+ 0 => {
+ drop(input);
+ let err = self.do_exec(theirs, envp.as_ref());
+ let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
+ let bytes = [
+ (errno >> 24) as u8,
+ (errno >> 16) as u8,
+ (errno >> 8) as u8,
+ (errno >> 0) as u8,
+ CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1],
+ CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3]
+ ];
+ // pipe I/O up to PIPE_BUF bytes should be atomic, and then
+ // we want to be sure we *don't* run at_exit destructors as
+ // we're being torn down regardless
+ assert!(output.write(&bytes).is_ok());
+ libc::_exit(1)
+ }
+ n => n,
+ }
+ };
+
+ let mut p = Process { pid: pid, status: None };
+ drop(output);
+ let mut bytes = [0; 8];
+
+ // loop to handle EINTR
+ loop {
+ match input.read(&mut bytes) {
+ Ok(0) => return Ok((p, ours)),
+ Ok(8) => {
+ assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]),
+ "Validation on the CLOEXEC pipe failed: {:?}", bytes);
+ let errno = combine(&bytes[0.. 4]);
+ assert!(p.wait().is_ok(),
+ "wait() should either return Ok or panic");
+ return Err(Error::from_raw_os_error(errno))
+ }
+ Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
+ Err(e) => {
+ assert!(p.wait().is_ok(),
+ "wait() should either return Ok or panic");
+ panic!("the CLOEXEC pipe failed: {:?}", e)
+ },
+ Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic
+ assert!(p.wait().is_ok(),
+ "wait() should either return Ok or panic");
+ panic!("short read on the CLOEXEC pipe")
+ }
+ }
+ }
+
+ fn combine(arr: &[u8]) -> i32 {
+ let a = arr[0] as u32;
+ let b = arr[1] as u32;
+ let c = arr[2] as u32;
+ let d = arr[3] as u32;
+
+ ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32
+ }
+ }
+
+ pub fn exec(&mut self, default: Stdio) -> io::Error {
+ let envp = self.capture_env();
+
+ if self.saw_nul() {
+ return io::Error::new(ErrorKind::InvalidInput,
+ "nul byte found in provided data")
+ }
+
+ match self.setup_io(default, true) {
+ Ok((_, theirs)) => unsafe { self.do_exec(theirs, envp.as_ref()) },
+ Err(e) => e,
+ }
+ }
+
+ // And at this point we've reached a special time in the life of the
+ // child. The child must now be considered hamstrung and unable to
+ // do anything other than syscalls really. Consider the following
+ // scenario:
+ //
+ // 1. Thread A of process 1 grabs the malloc() mutex
+ // 2. Thread B of process 1 forks(), creating thread C
+ // 3. Thread C of process 2 then attempts to malloc()
+ // 4. The memory of process 2 is the same as the memory of
+ // process 1, so the mutex is locked.
+ //
+ // This situation looks a lot like deadlock, right? It turns out
+ // that this is what pthread_atfork() takes care of, which is
+ // presumably implemented across platforms. The first thing that
+ // threads to *before* forking is to do things like grab the malloc
+ // mutex, and then after the fork they unlock it.
+ //
+ // Despite this information, libnative's spawn has been witnessed to
+ // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but
+ // all collected backtraces point at malloc/free traffic in the
+ // child spawned process.
+ //
+ // For this reason, the block of code below should contain 0
+ // invocations of either malloc of free (or their related friends).
+ //
+ // As an example of not having malloc/free traffic, we don't close
+ // this file descriptor by dropping the FileDesc (which contains an
+ // allocation). Instead we just close it manually. This will never
+ // have the drop glue anyway because this code never returns (the
+ // child will either exec() or invoke libc::exit)
+ unsafe fn do_exec(
+ &mut self,
+ stdio: ChildPipes,
+ maybe_envp: Option<&CStringArray>
+ ) -> io::Error {
+ use sys::{self, cvt_r};
+
+ macro_rules! t {
+ ($e:expr) => (match $e {
+ Ok(e) => e,
+ Err(e) => return e,
+ })
+ }
+
+ if let Some(fd) = stdio.stdin.fd() {
+ t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
+ }
+ if let Some(fd) = stdio.stdout.fd() {
+ t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
+ }
+ if let Some(fd) = stdio.stderr.fd() {
+ t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
+ }
+
+ if cfg!(not(any(target_os = "l4re"))) {
+ if let Some(u) = self.get_gid() {
+ t!(cvt(libc::setgid(u as gid_t)));
+ }
+ if let Some(u) = self.get_uid() {
+ // When dropping privileges from root, the `setgroups` call
+ // will remove any extraneous groups. If we don't call this,
+ // then even though our uid has dropped, we may still have
+ // groups that enable us to do super-user things. This will
+ // fail if we aren't root, so don't bother checking the
+ // return value, this is just done as an optimistic
+ // privilege dropping function.
+ let _ = libc::setgroups(0, ptr::null());
+
+ t!(cvt(libc::setuid(u as uid_t)));
+ }
+ }
+ if let Some(ref cwd) = *self.get_cwd() {
+ t!(cvt(libc::chdir(cwd.as_ptr())));
+ }
+ if let Some(envp) = maybe_envp {
+ *sys::os::environ() = envp.as_ptr();
+ }
+
+ // emscripten has no signal support.
+ #[cfg(not(any(target_os = "emscripten")))]
+ {
+ use mem;
+ // Reset signal handling so the child process starts in a
+ // standardized state. libstd ignores SIGPIPE, and signal-handling
+ // libraries often set a mask. Child processes inherit ignored
+ // signals and the signal mask from their parent, but most
+ // UNIX programs do not reset these things on their own, so we
+ // need to clean things up now to avoid confusing the program
+ // we're about to run.
+ let mut set: libc::sigset_t = mem::uninitialized();
+ if cfg!(target_os = "android") {
+ // Implementing sigemptyset allow us to support older Android
+ // versions. See the comment about Android and sig* functions in
+ // process_common.rs
+ libc::memset(&mut set as *mut _ as *mut _,
+ 0,
+ mem::size_of::<libc::sigset_t>());
+ } else {
+ t!(cvt(libc::sigemptyset(&mut set)));
+ }
+ t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set,
+ ptr::null_mut())));
+ let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
+ if ret == libc::SIG_ERR {
+ return io::Error::last_os_error()
+ }
+ }
+
+ for callback in self.get_closures().iter_mut() {
+ t!(callback());
+ }
+
+ libc::execvp(self.get_argv()[0], self.get_argv().as_ptr());
+ io::Error::last_os_error()
+ }
+
+ #[cfg(not(any(target_os = "macos", target_os = "freebsd",
+ all(target_os = "linux", target_env = "gnu"))))]
+ fn posix_spawn(&mut self, _: &ChildPipes, _: Option<&CStringArray>)
+ -> io::Result<Option<Process>>
+ {
+ Ok(None)
+ }
+
+ // Only support platforms for which posix_spawn() can return ENOENT
+ // directly.
+ #[cfg(any(target_os = "macos", target_os = "freebsd",
+ all(target_os = "linux", target_env = "gnu")))]
+ fn posix_spawn(&mut self, stdio: &ChildPipes, envp: Option<&CStringArray>)
+ -> io::Result<Option<Process>>
+ {
+ use mem;
+ use sys;
+
+ if self.get_cwd().is_some() ||
+ self.get_gid().is_some() ||
+ self.get_uid().is_some() ||
+ self.env_saw_path() ||
+ self.get_closures().len() != 0 {
+ return Ok(None)
+ }
+
+ // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly.
+ #[cfg(all(target_os = "linux", target_env = "gnu"))]
+ {
+ if let Some(version) = sys::os::glibc_version() {
+ if version < (2, 24) {
+ return Ok(None)
+ }
+ } else {
+ return Ok(None)
+ }
+ }
+
+ let mut p = Process { pid: 0, status: None };
+
+ struct PosixSpawnFileActions(libc::posix_spawn_file_actions_t);
+
+ impl Drop for PosixSpawnFileActions {
+ fn drop(&mut self) {
+ unsafe {
+ libc::posix_spawn_file_actions_destroy(&mut self.0);
+ }
+ }
+ }
+
+ struct PosixSpawnattr(libc::posix_spawnattr_t);
+
+ impl Drop for PosixSpawnattr {
+ fn drop(&mut self) {
+ unsafe {
+ libc::posix_spawnattr_destroy(&mut self.0);
+ }
+ }
+ }
+
+ unsafe {
+ let mut file_actions = PosixSpawnFileActions(mem::uninitialized());
+ let mut attrs = PosixSpawnattr(mem::uninitialized());
+
+ libc::posix_spawnattr_init(&mut attrs.0);
+ libc::posix_spawn_file_actions_init(&mut file_actions.0);
+
+ if let Some(fd) = stdio.stdin.fd() {
+ cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
+ fd,
+ libc::STDIN_FILENO))?;
+ }
+ if let Some(fd) = stdio.stdout.fd() {
+ cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
+ fd,
+ libc::STDOUT_FILENO))?;
+ }
+ if let Some(fd) = stdio.stderr.fd() {
+ cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
+ fd,
+ libc::STDERR_FILENO))?;
+ }
+
+ let mut set: libc::sigset_t = mem::uninitialized();
+ cvt(libc::sigemptyset(&mut set))?;
+ cvt(libc::posix_spawnattr_setsigmask(&mut attrs.0,
+ &set))?;
+ cvt(libc::sigaddset(&mut set, libc::SIGPIPE))?;
+ cvt(libc::posix_spawnattr_setsigdefault(&mut attrs.0,
+ &set))?;
+
+ let flags = libc::POSIX_SPAWN_SETSIGDEF |
+ libc::POSIX_SPAWN_SETSIGMASK;
+ cvt(libc::posix_spawnattr_setflags(&mut attrs.0, flags as _))?;
+
+ let envp = envp.map(|c| c.as_ptr())
+ .unwrap_or(*sys::os::environ() as *const _);
+ let ret = libc::posix_spawnp(
+ &mut p.pid,
+ self.get_argv()[0],
+ &file_actions.0,
+ &attrs.0,
+ self.get_argv().as_ptr() as *const _,
+ envp as *const _,
+ );
+ if ret == 0 {
+ Ok(Some(p))
+ } else {
+ Err(io::Error::from_raw_os_error(ret))
+ }
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Processes
+////////////////////////////////////////////////////////////////////////////////
+
+/// The unique id of the process (this should never be negative).
+pub struct Process {
+ pid: pid_t,
+ status: Option<ExitStatus>,
+}
+
+impl Process {
+ pub fn id(&self) -> u32 {
+ self.pid as u32
+ }
+
+ pub fn kill(&mut self) -> io::Result<()> {
+ // If we've already waited on this process then the pid can be recycled
+ // and used for another process, and we probably shouldn't be killing
+ // random processes, so just return an error.
+ if self.status.is_some() {
+ Err(Error::new(ErrorKind::InvalidInput,
+ "invalid argument: can't kill an exited process"))
+ } else {
+ cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ())
+ }
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ use sys::cvt_r;
+ if let Some(status) = self.status {
+ return Ok(status)
+ }
+ let mut status = 0 as c_int;
+ cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
+ self.status = Some(ExitStatus::new(status));
+ Ok(ExitStatus::new(status))
+ }
+
+ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+ if let Some(status) = self.status {
+ return Ok(Some(status))
+ }
+ let mut status = 0 as c_int;
+ let pid = cvt(unsafe {
+ libc::waitpid(self.pid, &mut status, libc::WNOHANG)
+ })?;
+ if pid == 0 {
+ Ok(None)
+ } else {
+ self.status = Some(ExitStatus::new(status));
+ Ok(Some(ExitStatus::new(status)))
+ }
+ }
+}
diff --git a/ctr-std/src/sys/unix/process/zircon.rs b/ctr-std/src/sys/unix/process/zircon.rs
new file mode 100644
index 0000000..a06c73e
--- /dev/null
+++ b/ctr-std/src/sys/unix/process/zircon.rs
@@ -0,0 +1,279 @@
+// 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.
+
+#![allow(non_camel_case_types, unused)]
+
+use convert::TryInto;
+use io;
+use os::raw::c_char;
+use u64;
+
+use libc::{c_int, c_void, size_t};
+
+pub type zx_handle_t = u32;
+pub type zx_vaddr_t = usize;
+pub type zx_rights_t = u32;
+pub type zx_status_t = i32;
+
+pub const ZX_HANDLE_INVALID: zx_handle_t = 0;
+
+pub type zx_time_t = u64;
+pub const ZX_TIME_INFINITE : zx_time_t = u64::MAX;
+
+pub type zx_signals_t = u32;
+
+pub const ZX_OBJECT_SIGNAL_3 : zx_signals_t = 1 << 3;
+
+pub const ZX_TASK_TERMINATED : zx_signals_t = ZX_OBJECT_SIGNAL_3;
+
+pub const ZX_RIGHT_SAME_RIGHTS : zx_rights_t = 1 << 31;
+
+pub type zx_object_info_topic_t = u32;
+
+pub const ZX_INFO_PROCESS : zx_object_info_topic_t = 3;
+
+pub fn zx_cvt<T>(t: T) -> io::Result<T> where T: TryInto<zx_status_t>+Copy {
+ if let Ok(status) = TryInto::try_into(t) {
+ if status < 0 {
+ Err(io::Error::from_raw_os_error(status))
+ } else {
+ Ok(t)
+ }
+ } else {
+ Err(io::Error::last_os_error())
+ }
+}
+
+// Safe wrapper around zx_handle_t
+pub struct Handle {
+ raw: zx_handle_t,
+}
+
+impl Handle {
+ pub fn new(raw: zx_handle_t) -> Handle {
+ Handle {
+ raw,
+ }
+ }
+
+ pub fn raw(&self) -> zx_handle_t {
+ self.raw
+ }
+}
+
+impl Drop for Handle {
+ fn drop(&mut self) {
+ unsafe { zx_cvt(zx_handle_close(self.raw)).expect("Failed to close zx_handle_t"); }
+ }
+}
+
+// Common ZX_INFO header
+#[derive(Default)]
+#[repr(C)]
+pub struct zx_info_header_t {
+ pub topic: u32, // identifies the info struct
+ pub avail_topic_size: u16, // “native” size of the struct
+ pub topic_size: u16, // size of the returned struct (<=topic_size)
+ pub avail_count: u32, // number of records the kernel has
+ pub count: u32, // number of records returned (limited by buffer size)
+}
+
+#[derive(Default)]
+#[repr(C)]
+pub struct zx_record_process_t {
+ pub return_code: c_int,
+}
+
+// Returned for topic ZX_INFO_PROCESS
+#[derive(Default)]
+#[repr(C)]
+pub struct zx_info_process_t {
+ pub hdr: zx_info_header_t,
+ pub rec: zx_record_process_t,
+}
+
+extern {
+ pub fn zx_job_default() -> zx_handle_t;
+
+ pub fn zx_task_kill(handle: zx_handle_t) -> zx_status_t;
+
+ pub fn zx_handle_close(handle: zx_handle_t) -> zx_status_t;
+
+ pub fn zx_handle_duplicate(handle: zx_handle_t, rights: zx_rights_t,
+ out: *const zx_handle_t) -> zx_handle_t;
+
+ pub fn zx_object_wait_one(handle: zx_handle_t, signals: zx_signals_t, timeout: zx_time_t,
+ pending: *mut zx_signals_t) -> zx_status_t;
+
+ pub fn zx_object_get_info(handle: zx_handle_t, topic: u32, buffer: *mut c_void,
+ buffer_size: size_t, actual_size: *mut size_t,
+ avail: *mut size_t) -> zx_status_t;
+}
+
+#[derive(Default)]
+#[repr(C)]
+pub struct fdio_spawn_action_t {
+ pub action: u32,
+ pub reserved0: u32,
+ pub local_fd: i32,
+ pub target_fd: i32,
+ pub reserved1: u64,
+}
+
+extern {
+ pub fn fdio_spawn_etc(job: zx_handle_t, flags: u32, path: *const c_char,
+ argv: *const *const c_char, envp: *const *const c_char,
+ action_count: u64, actions: *const fdio_spawn_action_t,
+ process: *mut zx_handle_t, err_msg: *mut c_char) -> zx_status_t;
+}
+
+// fdio_spawn_etc flags
+
+pub const FDIO_SPAWN_CLONE_JOB: u32 = 0x0001;
+pub const FDIO_SPAWN_CLONE_LDSVC: u32 = 0x0002;
+pub const FDIO_SPAWN_CLONE_NAMESPACE: u32 = 0x0004;
+pub const FDIO_SPAWN_CLONE_STDIO: u32 = 0x0008;
+pub const FDIO_SPAWN_CLONE_ENVIRON: u32 = 0x0010;
+pub const FDIO_SPAWN_CLONE_ALL: u32 = 0xFFFF;
+
+// fdio_spawn_etc actions
+
+pub const FDIO_SPAWN_ACTION_CLONE_FD: u32 = 0x0001;
+pub const FDIO_SPAWN_ACTION_TRANSFER_FD: u32 = 0x0002;
+
+// Errors
+
+#[allow(unused)] pub const ERR_INTERNAL: zx_status_t = -1;
+
+// ERR_NOT_SUPPORTED: The operation is not implemented, supported,
+// or enabled.
+#[allow(unused)] pub const ERR_NOT_SUPPORTED: zx_status_t = -2;
+
+// ERR_NO_RESOURCES: The system was not able to allocate some resource
+// needed for the operation.
+#[allow(unused)] pub const ERR_NO_RESOURCES: zx_status_t = -3;
+
+// ERR_NO_MEMORY: The system was not able to allocate memory needed
+// for the operation.
+#[allow(unused)] pub const ERR_NO_MEMORY: zx_status_t = -4;
+
+// ERR_CALL_FAILED: The second phase of zx_channel_call(; did not complete
+// successfully.
+#[allow(unused)] pub const ERR_CALL_FAILED: zx_status_t = -5;
+
+// ERR_INTERRUPTED_RETRY: The system call was interrupted, but should be
+// retried. This should not be seen outside of the VDSO.
+#[allow(unused)] pub const ERR_INTERRUPTED_RETRY: zx_status_t = -6;
+
+// ======= Parameter errors =======
+// ERR_INVALID_ARGS: an argument is invalid, ex. null pointer
+#[allow(unused)] pub const ERR_INVALID_ARGS: zx_status_t = -10;
+
+// ERR_BAD_HANDLE: A specified handle value does not refer to a handle.
+#[allow(unused)] pub const ERR_BAD_HANDLE: zx_status_t = -11;
+
+// ERR_WRONG_TYPE: The subject of the operation is the wrong type to
+// perform the operation.
+// Example: Attempting a message_read on a thread handle.
+#[allow(unused)] pub const ERR_WRONG_TYPE: zx_status_t = -12;
+
+// ERR_BAD_SYSCALL: The specified syscall number is invalid.
+#[allow(unused)] pub const ERR_BAD_SYSCALL: zx_status_t = -13;
+
+// ERR_OUT_OF_RANGE: An argument is outside the valid range for this
+// operation.
+#[allow(unused)] pub const ERR_OUT_OF_RANGE: zx_status_t = -14;
+
+// ERR_BUFFER_TOO_SMALL: A caller provided buffer is too small for
+// this operation.
+#[allow(unused)] pub const ERR_BUFFER_TOO_SMALL: zx_status_t = -15;
+
+// ======= Precondition or state errors =======
+// ERR_BAD_STATE: operation failed because the current state of the
+// object does not allow it, or a precondition of the operation is
+// not satisfied
+#[allow(unused)] pub const ERR_BAD_STATE: zx_status_t = -20;
+
+// ERR_TIMED_OUT: The time limit for the operation elapsed before
+// the operation completed.
+#[allow(unused)] pub const ERR_TIMED_OUT: zx_status_t = -21;
+
+// ERR_SHOULD_WAIT: The operation cannot be performed currently but
+// potentially could succeed if the caller waits for a prerequisite
+// to be satisfied, for example waiting for a handle to be readable
+// or writable.
+// Example: Attempting to read from a message pipe that has no
+// messages waiting but has an open remote will return ERR_SHOULD_WAIT.
+// Attempting to read from a message pipe that has no messages waiting
+// and has a closed remote end will return ERR_REMOTE_CLOSED.
+#[allow(unused)] pub const ERR_SHOULD_WAIT: zx_status_t = -22;
+
+// ERR_CANCELED: The in-progress operation (e.g. a wait) has been
+// // canceled.
+#[allow(unused)] pub const ERR_CANCELED: zx_status_t = -23;
+
+// ERR_PEER_CLOSED: The operation failed because the remote end
+// of the subject of the operation was closed.
+#[allow(unused)] pub const ERR_PEER_CLOSED: zx_status_t = -24;
+
+// ERR_NOT_FOUND: The requested entity is not found.
+#[allow(unused)] pub const ERR_NOT_FOUND: zx_status_t = -25;
+
+// ERR_ALREADY_EXISTS: An object with the specified identifier
+// already exists.
+// Example: Attempting to create a file when a file already exists
+// with that name.
+#[allow(unused)] pub const ERR_ALREADY_EXISTS: zx_status_t = -26;
+
+// ERR_ALREADY_BOUND: The operation failed because the named entity
+// is already owned or controlled by another entity. The operation
+// could succeed later if the current owner releases the entity.
+#[allow(unused)] pub const ERR_ALREADY_BOUND: zx_status_t = -27;
+
+// ERR_UNAVAILABLE: The subject of the operation is currently unable
+// to perform the operation.
+// Note: This is used when there's no direct way for the caller to
+// observe when the subject will be able to perform the operation
+// and should thus retry.
+#[allow(unused)] pub const ERR_UNAVAILABLE: zx_status_t = -28;
+
+// ======= Permission check errors =======
+// ERR_ACCESS_DENIED: The caller did not have permission to perform
+// the specified operation.
+#[allow(unused)] pub const ERR_ACCESS_DENIED: zx_status_t = -30;
+
+// ======= Input-output errors =======
+// ERR_IO: Otherwise unspecified error occurred during I/O.
+#[allow(unused)] pub const ERR_IO: zx_status_t = -40;
+
+// ERR_REFUSED: The entity the I/O operation is being performed on
+// rejected the operation.
+// Example: an I2C device NAK'ing a transaction or a disk controller
+// rejecting an invalid command.
+#[allow(unused)] pub const ERR_IO_REFUSED: zx_status_t = -41;
+
+// ERR_IO_DATA_INTEGRITY: The data in the operation failed an integrity
+// check and is possibly corrupted.
+// Example: CRC or Parity error.
+#[allow(unused)] pub const ERR_IO_DATA_INTEGRITY: zx_status_t = -42;
+
+// ERR_IO_DATA_LOSS: The data in the operation is currently unavailable
+// and may be permanently lost.
+// Example: A disk block is irrecoverably damaged.
+#[allow(unused)] pub const ERR_IO_DATA_LOSS: zx_status_t = -43;
+
+// Filesystem specific errors
+#[allow(unused)] pub const ERR_BAD_PATH: zx_status_t = -50;
+#[allow(unused)] pub const ERR_NOT_DIR: zx_status_t = -51;
+#[allow(unused)] pub const ERR_NOT_FILE: zx_status_t = -52;
+// ERR_FILE_BIG: A file exceeds a filesystem-specific size limit.
+#[allow(unused)] pub const ERR_FILE_BIG: zx_status_t = -53;
+// ERR_NO_SPACE: Filesystem or device space is exhausted.
+#[allow(unused)] pub const ERR_NO_SPACE: zx_status_t = -54;
diff --git a/ctr-std/src/sys/unix/rand.rs b/ctr-std/src/sys/unix/rand.rs
index 4500c9d..caa1894 100644
--- a/ctr-std/src/sys/unix/rand.rs
+++ b/ctr-std/src/sys/unix/rand.rs
@@ -21,23 +21,197 @@ pub fn hashmap_random_keys() -> (u64, u64) {
return v
}
+#[cfg(all(unix,
+ not(target_os = "ios"),
+ not(target_os = "openbsd"),
+ not(target_os = "freebsd"),
+ not(target_os = "fuchsia")))]
mod imp {
- use libctru;
+ use fs::File;
+ use io::Read;
+ use libc;
+ use sys::os::errno;
+
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ fn getrandom(buf: &mut [u8]) -> libc::c_long {
+ unsafe {
+ libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), libc::GRND_NONBLOCK)
+ }
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }
+
+ fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
+ let mut read = 0;
+ while read < v.len() {
+ let result = getrandom(&mut v[read..]);
+ if result == -1 {
+ let err = errno() as libc::c_int;
+ if err == libc::EINTR {
+ continue;
+ } else if err == libc::EAGAIN {
+ return false
+ } else {
+ panic!("unexpected getrandom error: {}", err);
+ }
+ } else {
+ read += result as usize;
+ }
+ }
+
+ return true
+ }
+
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ fn is_getrandom_available() -> bool {
+ use io;
+ use sync::atomic::{AtomicBool, Ordering};
+ use sync::Once;
+
+ static CHECKER: Once = Once::new();
+ static AVAILABLE: AtomicBool = AtomicBool::new(false);
+
+ CHECKER.call_once(|| {
+ let mut buf: [u8; 0] = [];
+ let result = getrandom(&mut buf);
+ let available = if result == -1 {
+ let err = io::Error::last_os_error().raw_os_error();
+ err != Some(libc::ENOSYS)
+ } else {
+ true
+ };
+ AVAILABLE.store(available, Ordering::Relaxed);
+ });
+
+ AVAILABLE.load(Ordering::Relaxed)
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ fn is_getrandom_available() -> bool { false }
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ // getrandom_fill_bytes here can fail if getrandom() returns EAGAIN,
+ // meaning it would have blocked because the non-blocking pool (urandom)
+ // has not initialized in the kernel yet due to a lack of entropy the
+ // fallback we do here is to avoid blocking applications which could
+ // depend on this call without ever knowing they do and don't have a
+ // work around. The PRNG of /dev/urandom will still be used but not
+ // over a completely full entropy pool
+ if is_getrandom_available() && getrandom_fill_bytes(v) {
+ return
+ }
+
+ let mut file = File::open("/dev/urandom")
+ .expect("failed to open /dev/urandom");
+ file.read_exact(v).expect("failed to read /dev/urandom");
+ }
+}
+
+#[cfg(target_os = "openbsd")]
+mod imp {
+ use libc;
+ use sys::os::errno;
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ // getentropy(2) permits a maximum buffer size of 256 bytes
+ for s in v.chunks_mut(256) {
+ let ret = unsafe {
+ libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len())
+ };
+ if ret == -1 {
+ panic!("unexpected getentropy error: {}", errno());
+ }
+ }
+ }
+}
+
+#[cfg(target_os = "ios")]
+mod imp {
+ use io;
+ use libc::{c_int, size_t};
+ use ptr;
+
+ enum SecRandom {}
+
+ #[allow(non_upper_case_globals)]
+ const kSecRandomDefault: *const SecRandom = ptr::null();
+
+ extern {
+ fn SecRandomCopyBytes(rnd: *const SecRandom,
+ count: size_t,
+ bytes: *mut u8) -> c_int;
+ }
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ let ret = unsafe {
+ SecRandomCopyBytes(kSecRandomDefault,
+ v.len(),
+ v.as_mut_ptr())
+ };
+ if ret == -1 {
+ panic!("couldn't generate random bytes: {}",
+ io::Error::last_os_error());
+ }
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+mod imp {
+ use libc;
+ use ptr;
pub fn fill_bytes(v: &mut [u8]) {
+ let mib = [libc::CTL_KERN, libc::KERN_ARND];
+ // kern.arandom permits a maximum buffer size of 256 bytes
+ for s in v.chunks_mut(256) {
+ let mut s_len = s.len();
+ let ret = unsafe {
+ libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint,
+ s.as_mut_ptr() as *mut _, &mut s_len,
+ ptr::null(), 0)
+ };
+ if ret == -1 || s_len != s.len() {
+ panic!("kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})",
+ ret, s.len(), s_len);
+ }
+ }
+ }
+}
+
+#[cfg(target_os = "fuchsia")]
+mod imp {
+ #[link(name = "zircon")]
+ extern {
+ fn zx_cprng_draw(buffer: *mut u8, len: usize, actual: *mut usize) -> i32;
+ }
+
+ fn getrandom(buf: &mut [u8]) -> Result<usize, i32> {
unsafe {
- // Initializing and de-initializing the sslC subsystem every time
- // we initialize a hashmap is pretty dumb, but I can't think of a
- // better method at the moment.
- //
- // lazy_static won't work because
- // destructors (for closing the subsystem on exit) won't run.
- //
- // Perhaps overriding __appInit() and __appExit() will work,
- // but that's an experiment for another time.
- libctru::sslcInit(0);
- libctru::sslcGenerateRandomData(v.as_ptr() as _, v.len() as u32);
- libctru::sslcExit();
+ let mut actual = 0;
+ let status = zx_cprng_draw(buf.as_mut_ptr(), buf.len(), &mut actual);
+ if status == 0 {
+ Ok(actual)
+ } else {
+ Err(status)
+ }
+ }
+ }
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ let mut buf = v;
+ while !buf.is_empty() {
+ let ret = getrandom(buf);
+ match ret {
+ Err(err) => {
+ panic!("kernel zx_cprng_draw call failed! (returned {}, buf.len() {})",
+ err, buf.len())
+ }
+ Ok(actual) => {
+ let move_buf = buf;
+ buf = &mut move_buf[(actual as usize)..];
+ }
+ }
}
}
}
diff --git a/ctr-std/src/sys/unix/rwlock.rs b/ctr-std/src/sys/unix/rwlock.rs
index 4a802e9..c754d5b 100644
--- a/ctr-std/src/sys/unix/rwlock.rs
+++ b/ctr-std/src/sys/unix/rwlock.rs
@@ -1,4 +1,4 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -8,16 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+use libc;
use cell::UnsafeCell;
-use super::mutex::Mutex;
-use super::condvar::Condvar;
+use sync::atomic::{AtomicUsize, Ordering};
-// A simple read-preferring RWLock implementation that I found on wikipedia <.<
pub struct RWLock {
- mutex: Mutex,
- cvar: Condvar,
- reader_count: UnsafeCell<u32>,
- writer_active: UnsafeCell<bool>,
+ inner: UnsafeCell<libc::pthread_rwlock_t>,
+ write_locked: UnsafeCell<bool>,
+ num_readers: AtomicUsize,
}
unsafe impl Send for RWLock {}
@@ -26,102 +24,119 @@ unsafe impl Sync for RWLock {}
impl RWLock {
pub const fn new() -> RWLock {
RWLock {
- mutex: Mutex::new(),
- cvar: Condvar::new(),
- reader_count: UnsafeCell::new(0),
- writer_active: UnsafeCell::new(false),
+ inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER),
+ write_locked: UnsafeCell::new(false),
+ num_readers: AtomicUsize::new(0),
}
}
-
#[inline]
pub unsafe fn read(&self) {
- self.mutex.lock();
-
- while *self.writer_active.get() {
- self.cvar.wait(&self.mutex);
+ let r = libc::pthread_rwlock_rdlock(self.inner.get());
+
+ // According to the pthread_rwlock_rdlock spec, this function **may**
+ // fail with EDEADLK if a deadlock is detected. On the other hand
+ // pthread mutexes will *never* return EDEADLK if they are initialized
+ // as the "fast" kind (which ours always are). As a result, a deadlock
+ // situation may actually return from the call to pthread_rwlock_rdlock
+ // instead of blocking forever (as mutexes and Windows rwlocks do). Note
+ // that not all unix implementations, however, will return EDEADLK for
+ // their rwlocks.
+ //
+ // We roughly maintain the deadlocking behavior by panicking to ensure
+ // that this lock acquisition does not succeed.
+ //
+ // We also check whether this lock is already write locked. This
+ // is only possible if it was write locked by the current thread and
+ // the implementation allows recursive locking. The POSIX standard
+ // doesn't require recursively locking a rwlock to deadlock, but we can't
+ // allow that because it could lead to aliasing issues.
+ if r == libc::EAGAIN {
+ panic!("rwlock maximum reader count exceeded");
+ } else if r == libc::EDEADLK || *self.write_locked.get() {
+ if r == 0 {
+ self.raw_unlock();
+ }
+ panic!("rwlock read lock would result in deadlock");
+ } else {
+ debug_assert_eq!(r, 0);
+ self.num_readers.fetch_add(1, Ordering::Relaxed);
}
-
- assert!(*self.reader_count.get() != u32::max_value());
- *self.reader_count.get() += 1;
-
- self.mutex.unlock();
}
-
#[inline]
pub unsafe fn try_read(&self) -> bool {
- if !self.mutex.try_lock() {
- return false
- }
-
- while *self.writer_active.get() {
- self.cvar.wait(&self.mutex);
+ let r = libc::pthread_rwlock_tryrdlock(self.inner.get());
+ if r == 0 {
+ if *self.write_locked.get() {
+ self.raw_unlock();
+ false
+ } else {
+ self.num_readers.fetch_add(1, Ordering::Relaxed);
+ true
+ }
+ } else {
+ false
}
-
- assert!(*self.reader_count.get() != u32::max_value());
- *self.reader_count.get() += 1;
-
- self.mutex.unlock();
- true
}
-
#[inline]
pub unsafe fn write(&self) {
- self.mutex.lock();
-
- while *self.writer_active.get() || *self.reader_count.get() > 0 {
- self.cvar.wait(&self.mutex);
+ let r = libc::pthread_rwlock_wrlock(self.inner.get());
+ // See comments above for why we check for EDEADLK and write_locked. We
+ // also need to check that num_readers is 0.
+ if r == libc::EDEADLK || *self.write_locked.get() ||
+ self.num_readers.load(Ordering::Relaxed) != 0 {
+ if r == 0 {
+ self.raw_unlock();
+ }
+ panic!("rwlock write lock would result in deadlock");
+ } else {
+ debug_assert_eq!(r, 0);
}
-
- *self.writer_active.get() = true;
-
- self.mutex.unlock();
+ *self.write_locked.get() = true;
}
-
#[inline]
pub unsafe fn try_write(&self) -> bool {
- if !self.mutex.try_lock() {
- return false;
- }
-
- while *self.writer_active.get() || *self.reader_count.get() > 0 {
- self.cvar.wait(&self.mutex);
+ let r = libc::pthread_rwlock_trywrlock(self.inner.get());
+ if r == 0 {
+ if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 {
+ self.raw_unlock();
+ false
+ } else {
+ *self.write_locked.get() = true;
+ true
+ }
+ } else {
+ false
}
-
- *self.writer_active.get() = true;
-
- self.mutex.unlock();
- true
}
-
+ #[inline]
+ unsafe fn raw_unlock(&self) {
+ let r = libc::pthread_rwlock_unlock(self.inner.get());
+ debug_assert_eq!(r, 0);
+ }
#[inline]
pub unsafe fn read_unlock(&self) {
- self.mutex.lock();
-
- *self.reader_count.get() -= 1;
-
- if *self.reader_count.get() == 0 {
- self.cvar.notify_one()
- }
-
- self.mutex.unlock();
+ debug_assert!(!*self.write_locked.get());
+ self.num_readers.fetch_sub(1, Ordering::Relaxed);
+ self.raw_unlock();
}
-
#[inline]
pub unsafe fn write_unlock(&self) {
- self.mutex.lock();
-
- *self.writer_active.get() = false;
-
- self.cvar.notify_all();
-
- self.mutex.unlock();
+ debug_assert_eq!(self.num_readers.load(Ordering::Relaxed), 0);
+ debug_assert!(*self.write_locked.get());
+ *self.write_locked.get() = false;
+ self.raw_unlock();
}
-
#[inline]
pub unsafe fn destroy(&self) {
- self.mutex.destroy();
- self.cvar.destroy();
- *self.reader_count.get() = 0;
- *self.writer_active.get() = false;
+ let r = libc::pthread_rwlock_destroy(self.inner.get());
+ // On DragonFly pthread_rwlock_destroy() returns EINVAL if called on a
+ // rwlock that was just initialized with
+ // libc::PTHREAD_RWLOCK_INITIALIZER. Once it is used (locked/unlocked)
+ // or pthread_rwlock_init() is called, this behaviour no longer occurs.
+ if cfg!(target_os = "dragonfly") {
+ debug_assert!(r == 0 || r == libc::EINVAL);
+ } else {
+ debug_assert_eq!(r, 0);
+ }
}
}
diff --git a/ctr-std/src/sys/unix/stack_overflow.rs b/ctr-std/src/sys/unix/stack_overflow.rs
index bed2741..40453f9 100644
--- a/ctr-std/src/sys/unix/stack_overflow.rs
+++ b/ctr-std/src/sys/unix/stack_overflow.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -8,16 +8,213 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-pub struct Handler;
+#![cfg_attr(test, allow(dead_code))]
+
+use libc;
+use self::imp::{make_handler, drop_handler};
+
+pub use self::imp::cleanup;
+pub use self::imp::init;
+
+pub struct Handler {
+ _data: *mut libc::c_void
+}
impl Handler {
pub unsafe fn new() -> Handler {
- Handler
+ make_handler()
}
}
-pub unsafe fn init() {
+impl Drop for Handler {
+ fn drop(&mut self) {
+ unsafe {
+ drop_handler(self);
+ }
+ }
}
-pub unsafe fn cleanup() {
+#[cfg(any(target_os = "linux",
+ target_os = "macos",
+ target_os = "bitrig",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "solaris",
+ all(target_os = "netbsd", not(target_vendor = "rumprun")),
+ target_os = "openbsd"))]
+mod imp {
+ use super::Handler;
+ use mem;
+ use ptr;
+ use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE};
+ use libc::{sigaction, SIGBUS, SIG_DFL,
+ SA_SIGINFO, SA_ONSTACK, sighandler_t};
+ use libc;
+ use libc::{mmap, munmap};
+ use libc::{SIGSEGV, PROT_READ, PROT_WRITE, MAP_PRIVATE, MAP_ANON};
+ use libc::MAP_FAILED;
+
+ use sys_common::thread_info;
+
+
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
+ #[repr(C)]
+ struct siginfo_t {
+ a: [libc::c_int; 3], // si_signo, si_errno, si_code
+ si_addr: *mut libc::c_void,
+ }
+
+ (*(info as *const siginfo_t)).si_addr as usize
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
+ (*info).si_addr as usize
+ }
+
+ // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
+ // (unmapped pages) at the end of every thread's stack, so if a thread ends
+ // up running into the guard page it'll trigger this handler. We want to
+ // detect these cases and print out a helpful error saying that the stack
+ // has overflowed. All other signals, however, should go back to what they
+ // were originally supposed to do.
+ //
+ // This handler currently exists purely to print an informative message
+ // whenever a thread overflows its stack. We then abort to exit and
+ // indicate a crash, but to avoid a misleading SIGSEGV that might lead
+ // users to believe that unsafe code has accessed an invalid pointer; the
+ // SIGSEGV encountered when overflowing the stack is expected and
+ // well-defined.
+ //
+ // If this is not a stack overflow, the handler un-registers itself and
+ // then returns (to allow the original signal to be delivered again).
+ // Returning from this kind of signal handler is technically not defined
+ // to work when reading the POSIX spec strictly, but in practice it turns
+ // out many large systems and all implementations allow returning from a
+ // signal handler to work. For a more detailed explanation see the
+ // comments on #26458.
+ unsafe extern fn signal_handler(signum: libc::c_int,
+ info: *mut libc::siginfo_t,
+ _data: *mut libc::c_void) {
+ use sys_common::util::report_overflow;
+
+ let guard = thread_info::stack_guard().unwrap_or(0..0);
+ let addr = siginfo_si_addr(info);
+
+ // If the faulting address is within the guard page, then we print a
+ // message saying so and abort.
+ if guard.start <= addr && addr < guard.end {
+ report_overflow();
+ rtabort!("stack overflow");
+ } else {
+ // Unregister ourselves by reverting back to the default behavior.
+ let mut action: sigaction = mem::zeroed();
+ action.sa_sigaction = SIG_DFL;
+ sigaction(signum, &action, ptr::null_mut());
+
+ // See comment above for why this function returns.
+ }
+ }
+
+ static mut MAIN_ALTSTACK: *mut libc::c_void = ptr::null_mut();
+
+ pub unsafe fn init() {
+ let mut action: sigaction = mem::zeroed();
+ action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+ action.sa_sigaction = signal_handler as sighandler_t;
+ sigaction(SIGSEGV, &action, ptr::null_mut());
+ sigaction(SIGBUS, &action, ptr::null_mut());
+
+ let handler = make_handler();
+ MAIN_ALTSTACK = handler._data;
+ mem::forget(handler);
+ }
+
+ pub unsafe fn cleanup() {
+ Handler { _data: MAIN_ALTSTACK };
+ }
+
+ unsafe fn get_stackp() -> *mut libc::c_void {
+ let stackp = mmap(ptr::null_mut(),
+ SIGSTKSZ,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON,
+ -1,
+ 0);
+ if stackp == MAP_FAILED {
+ panic!("failed to allocate an alternative stack");
+ }
+ stackp
+ }
+
+ #[cfg(any(target_os = "linux",
+ target_os = "macos",
+ target_os = "bitrig",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ unsafe fn get_stack() -> libc::stack_t {
+ libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ }
+ }
+
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly"))]
+ unsafe fn get_stack() -> libc::stack_t {
+ libc::stack_t { ss_sp: get_stackp() as *mut i8, ss_flags: 0, ss_size: SIGSTKSZ }
+ }
+
+ pub unsafe fn make_handler() -> Handler {
+ let mut stack = mem::zeroed();
+ sigaltstack(ptr::null(), &mut stack);
+ // Configure alternate signal stack, if one is not already set.
+ if stack.ss_flags & SS_DISABLE != 0 {
+ stack = get_stack();
+ sigaltstack(&stack, ptr::null_mut());
+ Handler { _data: stack.ss_sp as *mut libc::c_void }
+ } else {
+ Handler { _data: ptr::null_mut() }
+ }
+ }
+
+ pub unsafe fn drop_handler(handler: &mut Handler) {
+ if !handler._data.is_null() {
+ let stack = libc::stack_t {
+ ss_sp: ptr::null_mut(),
+ ss_flags: SS_DISABLE,
+ // Workaround for bug in macOS implementation of sigaltstack
+ // UNIX2003 which returns ENOMEM when disabling a stack while
+ // passing ss_size smaller than MINSIGSTKSZ. According to POSIX
+ // both ss_sp and ss_size should be ignored in this case.
+ ss_size: SIGSTKSZ,
+ };
+ sigaltstack(&stack, ptr::null_mut());
+ munmap(handler._data, SIGSTKSZ);
+ }
+ }
+}
+
+#[cfg(not(any(target_os = "linux",
+ target_os = "macos",
+ target_os = "bitrig",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "solaris",
+ all(target_os = "netbsd", not(target_vendor = "rumprun")),
+ target_os = "openbsd")))]
+mod imp {
+ use ptr;
+
+ pub unsafe fn init() {
+ }
+
+ pub unsafe fn cleanup() {
+ }
+
+ pub unsafe fn make_handler() -> super::Handler {
+ super::Handler { _data: ptr::null_mut() }
+ }
+
+ pub unsafe fn drop_handler(_handler: &mut super::Handler) {
+ }
}
diff --git a/ctr-std/src/sys/unix/thread.rs b/ctr-std/src/sys/unix/thread.rs
index 694f85a..7fdecc9 100644
--- a/ctr-std/src/sys/unix/thread.rs
+++ b/ctr-std/src/sys/unix/thread.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -8,99 +8,450 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use alloc_crate::boxed::FnBox;
-use libc;
+use boxed::FnBox;
use cmp;
use ffi::CStr;
use io;
+use libc;
use mem;
use ptr;
-use sys_common::thread::start_thread;
+use sys::os;
use time::Duration;
-use libctru::Thread as ThreadHandle;
+use sys_common::thread::*;
+
+#[cfg(not(target_os = "l4re"))]
+pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
+#[cfg(target_os = "l4re")]
+pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
pub struct Thread {
- handle: ThreadHandle,
+ id: libc::pthread_t,
}
+// Some platforms may have pthread_t as a pointer in which case we still want
+// a thread to be Send/Sync
unsafe impl Send for Thread {}
unsafe impl Sync for Thread {}
-pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
+// The pthread_attr_setstacksize symbol doesn't exist in the emscripten libc,
+// so we have to not link to it to satisfy emcc's ERROR_ON_UNDEFINED_SYMBOLS.
+#[cfg(not(target_os = "emscripten"))]
+unsafe fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
+ stack_size: libc::size_t) -> libc::c_int {
+ libc::pthread_attr_setstacksize(attr, stack_size)
+}
+#[cfg(target_os = "emscripten")]
+unsafe fn pthread_attr_setstacksize(_attr: *mut libc::pthread_attr_t,
+ _stack_size: libc::size_t) -> libc::c_int {
+ panic!()
+}
impl Thread {
- pub unsafe fn new<'a>(stack: usize, p: Box<FnBox() + 'a>) -> io::Result<Thread> {
+ pub unsafe fn new<'a>(stack: usize, p: Box<FnBox() + 'a>)
+ -> io::Result<Thread> {
let p = box p;
- let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE);
+ let mut native: libc::pthread_t = mem::zeroed();
+ let mut attr: libc::pthread_attr_t = mem::zeroed();
+ assert_eq!(libc::pthread_attr_init(&mut attr), 0);
- let mut priority = 0;
- ::libctru::svcGetThreadPriority(&mut priority, 0xFFFF8000);
+ let stack_size = cmp::max(stack, min_stack_size(&attr));
- let handle = ::libctru::threadCreate(Some(thread_func), &*p as *const _ as *mut _,
- stack_size, priority, -2, false);
+ match pthread_attr_setstacksize(&mut attr,
+ stack_size) {
+ 0 => {}
+ n => {
+ assert_eq!(n, libc::EINVAL);
+ // EINVAL means |stack_size| is either too small or not a
+ // multiple of the system page size. Because it's definitely
+ // >= PTHREAD_STACK_MIN, it must be an alignment issue.
+ // Round up to the nearest page and try again.
+ let page_size = os::page_size();
+ let stack_size = (stack_size + page_size - 1) &
+ (-(page_size as isize - 1) as usize - 1);
+ assert_eq!(libc::pthread_attr_setstacksize(&mut attr,
+ stack_size), 0);
+ }
+ };
+
+ let ret = libc::pthread_create(&mut native, &attr, thread_start,
+ &*p as *const _ as *mut _);
+ assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
- return if handle == ptr::null_mut() {
- Err(io::Error::from_raw_os_error(libc::EAGAIN))
+ return if ret != 0 {
+ Err(io::Error::from_raw_os_error(ret))
} else {
- mem::forget(p); // ownership passed to the new thread
- Ok(Thread { handle: handle })
+ mem::forget(p); // ownership passed to pthread_create
+ Ok(Thread { id: native })
};
- extern "C" fn thread_func(start: *mut libc::c_void) {
- unsafe { start_thread(start as *mut u8) }
+ extern fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
+ unsafe { start_thread(main as *mut u8); }
+ ptr::null_mut()
}
}
-
+
pub fn yield_now() {
+ let ret = unsafe { libc::sched_yield() };
+ debug_assert_eq!(ret, 0);
+ }
+
+ #[cfg(any(target_os = "linux",
+ target_os = "android"))]
+ pub fn set_name(name: &CStr) {
+ const PR_SET_NAME: libc::c_int = 15;
+ // pthread wrapper only appeared in glibc 2.12, so we use syscall
+ // directly.
unsafe {
- ::libctru::svcSleepThread(0)
+ libc::prctl(PR_SET_NAME, name.as_ptr() as libc::c_ulong, 0, 0, 0);
}
}
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "bitrig",
+ target_os = "openbsd"))]
+ pub fn set_name(name: &CStr) {
+ unsafe {
+ libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr());
+ }
+ }
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ pub fn set_name(name: &CStr) {
+ unsafe {
+ libc::pthread_setname_np(name.as_ptr());
+ }
+ }
+
+ #[cfg(target_os = "netbsd")]
+ pub fn set_name(name: &CStr) {
+ use ffi::CString;
+ let cname = CString::new(&b"%s"[..]).unwrap();
+ unsafe {
+ libc::pthread_setname_np(libc::pthread_self(), cname.as_ptr(),
+ name.as_ptr() as *mut libc::c_void);
+ }
+ }
+ #[cfg(any(target_env = "newlib",
+ target_os = "solaris",
+ target_os = "haiku",
+ target_os = "l4re",
+ target_os = "emscripten"))]
pub fn set_name(_name: &CStr) {
- // threads aren't named in libctru
+ // Newlib, Illumos, Haiku, and Emscripten have no way to set a thread name.
+ }
+ #[cfg(target_os = "fuchsia")]
+ pub fn set_name(_name: &CStr) {
+ // FIXME: determine whether Fuchsia has a way to set a thread name.
}
pub fn sleep(dur: Duration) {
+ let mut secs = dur.as_secs();
+ let mut nsecs = dur.subsec_nanos() as _;
+
+ // If we're awoken with a signal then the return value will be -1 and
+ // nanosleep will fill in `ts` with the remaining time.
unsafe {
- let nanos = dur.as_secs()
- .saturating_mul(1_000_000_000)
- .saturating_add(dur.subsec_nanos() as u64);
- ::libctru::svcSleepThread(nanos as i64)
+ while secs > 0 || nsecs > 0 {
+ let mut ts = libc::timespec {
+ tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t,
+ tv_nsec: nsecs,
+ };
+ secs -= ts.tv_sec as u64;
+ if libc::nanosleep(&ts, &mut ts) == -1 {
+ assert_eq!(os::errno(), libc::EINTR);
+ secs += ts.tv_sec as u64;
+ nsecs = ts.tv_nsec;
+ } else {
+ nsecs = 0;
+ }
+ }
}
}
pub fn join(self) {
unsafe {
- let ret = ::libctru::threadJoin(self.handle, u64::max_value());
- ::libctru::threadFree(self.handle);
+ let ret = libc::pthread_join(self.id, ptr::null_mut());
mem::forget(self);
- debug_assert_eq!(ret, 0);
+ assert!(ret == 0,
+ "failed to join thread: {}", io::Error::from_raw_os_error(ret));
}
}
- #[allow(dead_code)]
- pub fn id(&self) -> ThreadHandle {
- self.handle
- }
+ pub fn id(&self) -> libc::pthread_t { self.id }
- #[allow(dead_code)]
- pub fn into_id(self) -> ThreadHandle {
- let handle = self.handle;
+ pub fn into_id(self) -> libc::pthread_t {
+ let id = self.id;
mem::forget(self);
- handle
+ id
}
}
impl Drop for Thread {
fn drop(&mut self) {
- unsafe { ::libctru::threadDetach(self.handle) }
+ let ret = unsafe { libc::pthread_detach(self.id) };
+ debug_assert_eq!(ret, 0);
}
}
+#[cfg(all(not(all(target_os = "linux", not(target_env = "musl"))),
+ not(target_os = "freebsd"),
+ not(target_os = "macos"),
+ not(target_os = "bitrig"),
+ not(all(target_os = "netbsd", not(target_vendor = "rumprun"))),
+ not(target_os = "openbsd"),
+ not(target_os = "solaris")))]
+#[cfg_attr(test, allow(dead_code))]
+pub mod guard {
+ use ops::Range;
+ pub type Guard = Range<usize>;
+ pub unsafe fn current() -> Option<Guard> { None }
+ pub unsafe fn init() -> Option<Guard> { None }
+ pub unsafe fn deinit() {}
+}
+
+
+#[cfg(any(all(target_os = "linux", not(target_env = "musl")),
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "bitrig",
+ all(target_os = "netbsd", not(target_vendor = "rumprun")),
+ target_os = "openbsd",
+ target_os = "solaris"))]
+#[cfg_attr(test, allow(dead_code))]
pub mod guard {
- pub unsafe fn current() -> Option<usize> { None }
- pub unsafe fn init() -> Option<usize> { None }
+ use libc;
+ use libc::{mmap, mprotect};
+ use libc::{PROT_NONE, PROT_READ, PROT_WRITE, MAP_PRIVATE, MAP_ANON, MAP_FAILED, MAP_FIXED};
+ use ops::Range;
+ use sys::os;
+
+ // This is initialized in init() and only read from after
+ static mut PAGE_SIZE: usize = 0;
+
+ pub type Guard = Range<usize>;
+
+ #[cfg(target_os = "solaris")]
+ unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
+ let mut current_stack: libc::stack_t = ::mem::zeroed();
+ assert_eq!(libc::stack_getbounds(&mut current_stack), 0);
+ Some(current_stack.ss_sp)
+ }
+
+ #[cfg(target_os = "macos")]
+ unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
+ let stackaddr = libc::pthread_get_stackaddr_np(libc::pthread_self()) as usize -
+ libc::pthread_get_stacksize_np(libc::pthread_self());
+ Some(stackaddr as *mut libc::c_void)
+ }
+
+ #[cfg(any(target_os = "openbsd", target_os = "bitrig"))]
+ unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
+ let mut current_stack: libc::stack_t = ::mem::zeroed();
+ assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(),
+ &mut current_stack), 0);
+
+ let extra = if cfg!(target_os = "bitrig") {3} else {1} * PAGE_SIZE;
+ let stackaddr = if libc::pthread_main_np() == 1 {
+ // main thread
+ current_stack.ss_sp as usize - current_stack.ss_size + extra
+ } else {
+ // new thread
+ current_stack.ss_sp as usize - current_stack.ss_size
+ };
+ Some(stackaddr as *mut libc::c_void)
+ }
+
+ #[cfg(any(target_os = "android", target_os = "freebsd",
+ target_os = "linux", target_os = "netbsd", target_os = "l4re"))]
+ unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
+ let mut ret = None;
+ let mut attr: libc::pthread_attr_t = ::mem::zeroed();
+ assert_eq!(libc::pthread_attr_init(&mut attr), 0);
+ #[cfg(target_os = "freebsd")]
+ let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
+ #[cfg(not(target_os = "freebsd"))]
+ let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr);
+ if e == 0 {
+ let mut stackaddr = ::ptr::null_mut();
+ let mut stacksize = 0;
+ assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr,
+ &mut stacksize), 0);
+ ret = Some(stackaddr);
+ }
+ assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+ ret
+ }
+
+ // Precondition: PAGE_SIZE is initialized.
+ unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> {
+ assert!(PAGE_SIZE != 0);
+ let stackaddr = get_stack_start()?;
+
+ // Ensure stackaddr is page aligned! A parent process might
+ // have reset RLIMIT_STACK to be non-page aligned. The
+ // pthread_attr_getstack() reports the usable stack area
+ // stackaddr < stackaddr + stacksize, so if stackaddr is not
+ // page-aligned, calculate the fix such that stackaddr <
+ // new_page_aligned_stackaddr < stackaddr + stacksize
+ let remainder = (stackaddr as usize) % PAGE_SIZE;
+ Some(if remainder == 0 {
+ stackaddr
+ } else {
+ ((stackaddr as usize) + PAGE_SIZE - remainder) as *mut libc::c_void
+ })
+ }
+
+ pub unsafe fn init() -> Option<Guard> {
+ PAGE_SIZE = os::page_size();
+
+ let stackaddr = get_stack_start_aligned()?;
+
+ if cfg!(target_os = "linux") {
+ // Linux doesn't allocate the whole stack right away, and
+ // the kernel has its own stack-guard mechanism to fault
+ // when growing too close to an existing mapping. If we map
+ // our own guard, then the kernel starts enforcing a rather
+ // large gap above that, rendering much of the possible
+ // stack space useless. See #43052.
+ //
+ // Instead, we'll just note where we expect rlimit to start
+ // faulting, so our handler can report "stack overflow", and
+ // trust that the kernel's own stack guard will work.
+ let stackaddr = stackaddr as usize;
+ Some(stackaddr - PAGE_SIZE..stackaddr)
+ } else {
+ // Reallocate the last page of the stack.
+ // This ensures SIGBUS will be raised on
+ // stack overflow.
+ // Systems which enforce strict PAX MPROTECT do not allow
+ // to mprotect() a mapping with less restrictive permissions
+ // than the initial mmap() used, so we mmap() here with
+ // read/write permissions and only then mprotect() it to
+ // no permissions at all. See issue #50313.
+ let result = mmap(stackaddr, PAGE_SIZE, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+ if result != stackaddr || result == MAP_FAILED {
+ panic!("failed to allocate a guard page");
+ }
+
+ let result = mprotect(stackaddr, PAGE_SIZE, PROT_NONE);
+ if result != 0 {
+ panic!("failed to protect the guard page");
+ }
+
+ let guardaddr = stackaddr as usize;
+ let offset = if cfg!(target_os = "freebsd") {
+ 2
+ } else {
+ 1
+ };
+
+ Some(guardaddr..guardaddr + offset * PAGE_SIZE)
+ }
+ }
+
+ pub unsafe fn deinit() {
+ if !cfg!(target_os = "linux") {
+ if let Some(stackaddr) = get_stack_start_aligned() {
+ // Remove the protection on the guard page.
+ // FIXME: we cannot unmap the page, because when we mmap()
+ // above it may be already mapped by the OS, which we can't
+ // detect from mmap()'s return value. If we unmap this page,
+ // it will lead to failure growing stack size on platforms like
+ // macOS. Instead, just restore the page to a writable state.
+ // This ain't Linux, so we probably don't need to care about
+ // execstack.
+ let result = mprotect(stackaddr, PAGE_SIZE, PROT_READ | PROT_WRITE);
+
+ if result != 0 {
+ panic!("unable to reset the guard page");
+ }
+ }
+ }
+ }
+
+ #[cfg(any(target_os = "macos",
+ target_os = "bitrig",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ pub unsafe fn current() -> Option<Guard> {
+ let stackaddr = get_stack_start()? as usize;
+ Some(stackaddr - PAGE_SIZE..stackaddr)
+ }
+
+ #[cfg(any(target_os = "android", target_os = "freebsd",
+ target_os = "linux", target_os = "netbsd", target_os = "l4re"))]
+ pub unsafe fn current() -> Option<Guard> {
+ let mut ret = None;
+ let mut attr: libc::pthread_attr_t = ::mem::zeroed();
+ assert_eq!(libc::pthread_attr_init(&mut attr), 0);
+ #[cfg(target_os = "freebsd")]
+ let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
+ #[cfg(not(target_os = "freebsd"))]
+ let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr);
+ if e == 0 {
+ let mut guardsize = 0;
+ assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0);
+ if guardsize == 0 {
+ panic!("there is no guard page");
+ }
+ let mut stackaddr = ::ptr::null_mut();
+ let mut size = 0;
+ assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr,
+ &mut size), 0);
+
+ let stackaddr = stackaddr as usize;
+ ret = if cfg!(target_os = "freebsd") {
+ // FIXME does freebsd really fault *below* the guard addr?
+ let guardaddr = stackaddr - guardsize;
+ Some(guardaddr - PAGE_SIZE..guardaddr)
+ } else if cfg!(target_os = "netbsd") {
+ Some(stackaddr - guardsize..stackaddr)
+ } else if cfg!(all(target_os = "linux", target_env = "gnu")) {
+ // glibc used to include the guard area within the stack, as noted in the BUGS
+ // section of `man pthread_attr_getguardsize`. This has been corrected starting
+ // with glibc 2.27, and in some distro backports, so the guard is now placed at the
+ // end (below) the stack. There's no easy way for us to know which we have at
+ // runtime, so we'll just match any fault in the range right above or below the
+ // stack base to call that fault a stack overflow.
+ Some(stackaddr - guardsize..stackaddr + guardsize)
+ } else {
+ Some(stackaddr..stackaddr + guardsize)
+ };
+ }
+ assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+ ret
+ }
+}
+
+// glibc >= 2.15 has a __pthread_get_minstack() function that returns
+// PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
+// storage. We need that information to avoid blowing up when a small stack
+// is created in an application with big thread-local storage requirements.
+// See #6233 for rationale and details.
+#[cfg(target_os = "linux")]
+#[allow(deprecated)]
+fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize {
+ weak!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t);
+
+ match __pthread_get_minstack.get() {
+ None => libc::PTHREAD_STACK_MIN,
+ Some(f) => unsafe { f(attr) },
+ }
+}
+
+// No point in looking up __pthread_get_minstack() on non-glibc
+// platforms.
+#[cfg(all(not(target_os = "linux"),
+ not(target_os = "netbsd")))]
+fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
+ libc::PTHREAD_STACK_MIN
+}
+
+#[cfg(target_os = "netbsd")]
+fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
+ 2048 // just a guess
}
diff --git a/ctr-std/src/sys/unix/thread_local.rs b/ctr-std/src/sys/unix/thread_local.rs
index 3cb3523..2487f6b 100644
--- a/ctr-std/src/sys/unix/thread_local.rs
+++ b/ctr-std/src/sys/unix/thread_local.rs
@@ -1,4 +1,4 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -8,63 +8,35 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-// Borrowed from /sys/redox/thread_local.rs
-
#![allow(dead_code)] // not used on all platforms
-use collections::BTreeMap;
-use ptr;
-use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
-
-pub type Key = usize;
-
-type Dtor = unsafe extern fn(*mut u8);
-
-static NEXT_KEY: AtomicUsize = ATOMIC_USIZE_INIT;
+use mem;
+use libc;
-static mut KEYS: *mut BTreeMap<Key, Option<Dtor>> = ptr::null_mut();
-
-#[thread_local]
-static mut LOCALS: *mut BTreeMap<Key, *mut u8> = ptr::null_mut();
-
-unsafe fn keys() -> &'static mut BTreeMap<Key, Option<Dtor>> {
- if KEYS == ptr::null_mut() {
- KEYS = Box::into_raw(Box::new(BTreeMap::new()));
- }
- &mut *KEYS
-}
-
-unsafe fn locals() -> &'static mut BTreeMap<Key, *mut u8> {
- if LOCALS == ptr::null_mut() {
- LOCALS = Box::into_raw(Box::new(BTreeMap::new()));
- }
- &mut *LOCALS
-}
+pub type Key = libc::pthread_key_t;
#[inline]
-pub unsafe fn create(dtor: Option<Dtor>) -> Key {
- let key = NEXT_KEY.fetch_add(1, Ordering::SeqCst);
- keys().insert(key, dtor);
+pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
+ let mut key = 0;
+ assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0);
key
}
#[inline]
-pub unsafe fn get(key: Key) -> *mut u8 {
- if let Some(&entry) = locals().get(&key) {
- entry
- } else {
- ptr::null_mut()
- }
+pub unsafe fn set(key: Key, value: *mut u8) {
+ let r = libc::pthread_setspecific(key, value as *mut _);
+ debug_assert_eq!(r, 0);
}
#[inline]
-pub unsafe fn set(key: Key, value: *mut u8) {
- locals().insert(key, value);
+pub unsafe fn get(key: Key) -> *mut u8 {
+ libc::pthread_getspecific(key) as *mut u8
}
#[inline]
pub unsafe fn destroy(key: Key) {
- keys().remove(&key);
+ let r = libc::pthread_key_delete(key);
+ debug_assert_eq!(r, 0);
}
#[inline]
diff --git a/ctr-std/src/sys/unix/time.rs b/ctr-std/src/sys/unix/time.rs
index d5dcbc4..89786eb 100644
--- a/ctr-std/src/sys/unix/time.rs
+++ b/ctr-std/src/sys/unix/time.rs
@@ -14,6 +14,7 @@ use time::Duration;
use core::hash::{Hash, Hasher};
pub use self::inner::{Instant, SystemTime, UNIX_EPOCH};
+use convert::TryInto;
const NSEC_PER_SEC: u64 = 1_000_000_000;
@@ -42,8 +43,12 @@ impl Timespec {
}
fn add_duration(&self, other: &Duration) -> Timespec {
- let secs = (self.t.tv_sec as i64).checked_add(other.as_secs() as i64);
- let mut secs = secs.expect("overflow when adding duration to time");
+ let mut secs = other
+ .as_secs()
+ .try_into() // <- target type would be `libc::time_t`
+ .ok()
+ .and_then(|secs| self.t.tv_sec.checked_add(secs))
+ .expect("overflow when adding duration to time");
// Nano calculations can't overflow because nanos are <1B which fit
// in a u32.
@@ -55,16 +60,19 @@ impl Timespec {
}
Timespec {
t: libc::timespec {
- tv_sec: secs as libc::time_t,
- tv_nsec: nsec as libc::c_long,
+ tv_sec: secs,
+ tv_nsec: nsec as _,
},
}
}
fn sub_duration(&self, other: &Duration) -> Timespec {
- let secs = (self.t.tv_sec as i64).checked_sub(other.as_secs() as i64);
- let mut secs = secs.expect("overflow when subtracting duration \
- from time");
+ let mut secs = other
+ .as_secs()
+ .try_into() // <- target type would be `libc::time_t`
+ .ok()
+ .and_then(|secs| self.t.tv_sec.checked_sub(secs))
+ .expect("overflow when subtracting duration from time");
// Similar to above, nanos can't overflow.
let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;
@@ -75,8 +83,8 @@ impl Timespec {
}
Timespec {
t: libc::timespec {
- tv_sec: secs as libc::time_t,
- tv_nsec: nsec as libc::c_long,
+ tv_sec: secs,
+ tv_nsec: nsec as _,
},
}
}
@@ -111,6 +119,7 @@ impl Hash for Timespec {
}
}
+#[cfg(any(target_os = "macos", target_os = "ios"))]
mod inner {
use fmt;
use libc;
@@ -122,8 +131,6 @@ mod inner {
use super::NSEC_PER_SEC;
use super::Timespec;
- use libctru;
-
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Instant {
t: u64
@@ -145,7 +152,7 @@ mod inner {
impl Instant {
pub fn now() -> Instant {
- Instant { t: ctr_absolute_time() }
+ Instant { t: unsafe { libc::mach_absolute_time() } }
}
pub fn sub_instant(&self, other: &Instant) -> Duration {
@@ -166,71 +173,11 @@ mod inner {
pub fn sub_duration(&self, other: &Duration) -> Instant {
Instant {
t: self.t.checked_sub(dur2intervals(other))
- .expect("overflow when adding duration to instant"),
+ .expect("overflow when subtracting duration from instant"),
}
}
}
- // The initial system tick after which all Instants occur
- static mut TICK: u64 = 0;
-
- // A source of monotonic time based on ticks of the 3DS CPU. Returns the
- // number of system ticks elapsed since an arbitrary point in the past
- fn ctr_absolute_time() -> u64 {
- let first_tick = get_first_tick();
- let current_tick = get_system_tick();
- current_tick - first_tick
- }
-
- // The first time this function is called, it generates and returns the
- // initial system tick used to create Instants
- //
- // subsequent calls to this function return the previously generated
- // tick value
- fn get_first_tick() -> u64 {
- static ONCE: Once = Once::new();
- unsafe {
- ONCE.call_once(|| {
- TICK = get_system_tick();
- });
- TICK
- }
- }
-
- // Gets the current system tick
- #[inline]
- fn get_system_tick() -> u64 {
- unsafe { libctru::svcGetSystemTick() }
- }
-
- // A struct representing the clock speed of the 3DS
- struct CtrClockInfo {
- numer: u32,
- denom: u32,
- }
-
- // Initializes the CtrClockInfo struct
- //
- // Note that svcGetSystemTick always runs at 268MHz (268,111,856Hz), even
- // on a New 3DS running in 804MHz mode
- //
- // See https://www.3dbrew.org/wiki/Hardware#Common_hardware
- fn info() -> &'static CtrClockInfo {
- static INFO: CtrClockInfo = CtrClockInfo {
- numer: 1_000_000_000,
- denom: 268_111_856,
- };
- &INFO
- }
-
- fn dur2intervals(dur: &Duration) -> u64 {
- let info = info();
- let nanos = dur.as_secs().checked_mul(NSEC_PER_SEC).and_then(|nanos| {
- nanos.checked_add(dur.subsec_nanos() as u64)
- }).expect("overflow converting duration to nanoseconds");
- mul_div_u64(nanos, info.denom as u64, info.numer as u64)
- }
-
impl SystemTime {
pub fn now() -> SystemTime {
use ptr;
@@ -282,4 +229,137 @@ mod inner {
.finish()
}
}
+
+ fn dur2intervals(dur: &Duration) -> u64 {
+ let info = info();
+ let nanos = dur.as_secs().checked_mul(NSEC_PER_SEC).and_then(|nanos| {
+ nanos.checked_add(dur.subsec_nanos() as u64)
+ }).expect("overflow converting duration to nanoseconds");
+ mul_div_u64(nanos, info.denom as u64, info.numer as u64)
+ }
+
+ fn info() -> &'static libc::mach_timebase_info {
+ static mut INFO: libc::mach_timebase_info = libc::mach_timebase_info {
+ numer: 0,
+ denom: 0,
+ };
+ static ONCE: Once = Once::new();
+
+ unsafe {
+ ONCE.call_once(|| {
+ libc::mach_timebase_info(&mut INFO);
+ });
+ &INFO
+ }
+ }
+}
+
+#[cfg(not(any(target_os = "macos", target_os = "ios")))]
+mod inner {
+ use fmt;
+ use libc;
+ use sys::cvt;
+ use time::Duration;
+
+ use super::Timespec;
+
+ #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+ pub struct Instant {
+ t: Timespec,
+ }
+
+ #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+ pub struct SystemTime {
+ t: Timespec,
+ }
+
+ pub const UNIX_EPOCH: SystemTime = SystemTime {
+ t: Timespec {
+ t: libc::timespec {
+ tv_sec: 0,
+ tv_nsec: 0,
+ },
+ },
+ };
+
+ impl Instant {
+ pub fn now() -> Instant {
+ Instant { t: now(libc::CLOCK_MONOTONIC) }
+ }
+
+ pub fn sub_instant(&self, other: &Instant) -> Duration {
+ self.t.sub_timespec(&other.t).unwrap_or_else(|_| {
+ panic!("specified instant was later than self")
+ })
+ }
+
+ pub fn add_duration(&self, other: &Duration) -> Instant {
+ Instant { t: self.t.add_duration(other) }
+ }
+
+ pub fn sub_duration(&self, other: &Duration) -> Instant {
+ Instant { t: self.t.sub_duration(other) }
+ }
+ }
+
+ impl fmt::Debug for Instant {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Instant")
+ .field("tv_sec", &self.t.t.tv_sec)
+ .field("tv_nsec", &self.t.t.tv_nsec)
+ .finish()
+ }
+ }
+
+ impl SystemTime {
+ pub fn now() -> SystemTime {
+ SystemTime { t: now(libc::CLOCK_REALTIME) }
+ }
+
+ pub fn sub_time(&self, other: &SystemTime)
+ -> Result<Duration, Duration> {
+ self.t.sub_timespec(&other.t)
+ }
+
+ pub fn add_duration(&self, other: &Duration) -> SystemTime {
+ SystemTime { t: self.t.add_duration(other) }
+ }
+
+ pub fn sub_duration(&self, other: &Duration) -> SystemTime {
+ SystemTime { t: self.t.sub_duration(other) }
+ }
+ }
+
+ impl From<libc::timespec> for SystemTime {
+ fn from(t: libc::timespec) -> SystemTime {
+ SystemTime { t: Timespec { t: t } }
+ }
+ }
+
+ impl fmt::Debug for SystemTime {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("SystemTime")
+ .field("tv_sec", &self.t.t.tv_sec)
+ .field("tv_nsec", &self.t.t.tv_nsec)
+ .finish()
+ }
+ }
+
+ #[cfg(not(target_os = "dragonfly"))]
+ pub type clock_t = libc::c_int;
+ #[cfg(target_os = "dragonfly")]
+ pub type clock_t = libc::c_ulong;
+
+ fn now(clock: clock_t) -> Timespec {
+ let mut t = Timespec {
+ t: libc::timespec {
+ tv_sec: 0,
+ tv_nsec: 0,
+ }
+ };
+ cvt(unsafe {
+ libc::clock_gettime(clock, &mut t.t)
+ }).unwrap();
+ t
+ }
}
diff --git a/ctr-std/src/sys/unix/weak.rs b/ctr-std/src/sys/unix/weak.rs
new file mode 100644
index 0000000..18944be
--- /dev/null
+++ b/ctr-std/src/sys/unix/weak.rs
@@ -0,0 +1,79 @@
+// 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.
+
+//! Support for "weak linkage" to symbols on Unix
+//!
+//! Some I/O operations we do in libstd require newer versions of OSes but we
+//! need to maintain binary compatibility with older releases for now. In order
+//! to use the new functionality when available we use this module for
+//! detection.
+//!
+//! One option to use here is weak linkage, but that is unfortunately only
+//! really workable on Linux. Hence, use dlsym to get the symbol value at
+//! runtime. This is also done for compatibility with older versions of glibc,
+//! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that
+//! we've been dynamically linked to the library the symbol comes from, but that
+//! is currently always the case for things like libpthread/libc.
+//!
+//! A long time ago this used weak linkage for the __pthread_get_minstack
+//! symbol, but that caused Debian to detect an unnecessarily strict versioned
+//! dependency on libc6 (#23628).
+
+use libc;
+
+use ffi::CString;
+use marker;
+use mem;
+use sync::atomic::{AtomicUsize, Ordering};
+
+macro_rules! weak {
+ (fn $name:ident($($t:ty),*) -> $ret:ty) => (
+ static $name: ::sys::weak::Weak<unsafe extern fn($($t),*) -> $ret> =
+ ::sys::weak::Weak::new(stringify!($name));
+ )
+}
+
+pub struct Weak<F> {
+ name: &'static str,
+ addr: AtomicUsize,
+ _marker: marker::PhantomData<F>,
+}
+
+impl<F> Weak<F> {
+ pub const fn new(name: &'static str) -> Weak<F> {
+ Weak {
+ name,
+ addr: AtomicUsize::new(1),
+ _marker: marker::PhantomData,
+ }
+ }
+
+ pub fn get(&self) -> Option<&F> {
+ assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
+ unsafe {
+ if self.addr.load(Ordering::SeqCst) == 1 {
+ self.addr.store(fetch(self.name), Ordering::SeqCst);
+ }
+ if self.addr.load(Ordering::SeqCst) == 0 {
+ None
+ } else {
+ mem::transmute::<&AtomicUsize, Option<&F>>(&self.addr)
+ }
+ }
+ }
+}
+
+unsafe fn fetch(name: &str) -> usize {
+ let name = match CString::new(name) {
+ Ok(cstr) => cstr,
+ Err(..) => return 0,
+ };
+ libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize
+}
diff --git a/ctr-std/src/sys/wasm/args.rs b/ctr-std/src/sys/wasm/args.rs
new file mode 100644
index 0000000..b3c6b67
--- /dev/null
+++ b/ctr-std/src/sys/wasm/args.rs
@@ -0,0 +1,62 @@
+// 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.
+
+use ffi::OsString;
+use marker::PhantomData;
+use vec;
+use sys::ArgsSysCall;
+
+pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
+ // On wasm these should always be null, so there's nothing for us to do here
+}
+
+pub unsafe fn cleanup() {
+}
+
+pub fn args() -> Args {
+ let v = ArgsSysCall::perform();
+ Args {
+ iter: v.into_iter(),
+ _dont_send_or_sync_me: PhantomData,
+ }
+}
+
+pub struct Args {
+ iter: vec::IntoIter<OsString>,
+ _dont_send_or_sync_me: PhantomData<*mut ()>,
+}
+
+impl Args {
+ pub fn inner_debug(&self) -> &[OsString] {
+ self.iter.as_slice()
+ }
+}
+
+impl Iterator for Args {
+ type Item = OsString;
+ fn next(&mut self) -> Option<OsString> {
+ self.iter.next()
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+impl ExactSizeIterator for Args {
+ fn len(&self) -> usize {
+ self.iter.len()
+ }
+}
+
+impl DoubleEndedIterator for Args {
+ fn next_back(&mut self) -> Option<OsString> {
+ self.iter.next_back()
+ }
+}
diff --git a/ctr-std/src/sys/unix/backtrace.rs b/ctr-std/src/sys/wasm/backtrace.rs
index 9a8c48f..9a8c48f 100644
--- a/ctr-std/src/sys/unix/backtrace.rs
+++ b/ctr-std/src/sys/wasm/backtrace.rs
diff --git a/ctr-std/src/sys/wasm/cmath.rs b/ctr-std/src/sys/wasm/cmath.rs
new file mode 100644
index 0000000..87ac209
--- /dev/null
+++ b/ctr-std/src/sys/wasm/cmath.rs
@@ -0,0 +1,119 @@
+// 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.
+
+#[inline]
+pub unsafe fn cbrtf(n: f32) -> f32 {
+ f64::cbrt(n as f64) as f32
+}
+
+#[inline]
+pub unsafe fn expm1f(n: f32) -> f32 {
+ f64::exp_m1(n as f64) as f32
+}
+
+#[inline]
+#[allow(deprecated)]
+pub unsafe fn fdimf(a: f32, b: f32) -> f32 {
+ f64::abs_sub(a as f64, b as f64) as f32
+}
+
+#[inline]
+pub unsafe fn log1pf(n: f32) -> f32 {
+ f64::ln_1p(n as f64) as f32
+}
+
+#[inline]
+pub unsafe fn hypotf(x: f32, y: f32) -> f32 {
+ f64::hypot(x as f64, y as f64) as f32
+}
+
+#[inline]
+pub unsafe fn acosf(n: f32) -> f32 {
+ f64::acos(n as f64) as f32
+}
+
+#[inline]
+pub unsafe fn asinf(n: f32) -> f32 {
+ f64::asin(n as f64) as f32
+}
+
+#[inline]
+pub unsafe fn atan2f(n: f32, b: f32) -> f32 {
+ f64::atan2(n as f64, b as f64) as f32
+}
+
+#[inline]
+pub unsafe fn atanf(n: f32) -> f32 {
+ f64::atan(n as f64) as f32
+}
+
+#[inline]
+pub unsafe fn coshf(n: f32) -> f32 {
+ f64::cosh(n as f64) as f32
+}
+
+#[inline]
+pub unsafe fn sinhf(n: f32) -> f32 {
+ f64::sinh(n as f64) as f32
+}
+
+#[inline]
+pub unsafe fn tanf(n: f32) -> f32 {
+ f64::tan(n as f64) as f32
+}
+
+#[inline]
+pub unsafe fn tanhf(n: f32) -> f32 {
+ f64::tanh(n as f64) as f32
+}
+
+// Right now all these functions, the f64 version of the functions above, all
+// shell out to random names. These names aren't actually defined anywhere, per
+// se, but we need this to compile somehow.
+//
+// The idea with this is that when you're using wasm then, for now, we have no
+// way of providing an implementation of these which delegates to a "correct"
+// implementation. For example most wasm applications probably just want to
+// delegate to the javascript `Math` object and its related functions, but wasm
+// doesn't currently have the ability to seamlessly do that (when you
+// instantiate a module you have to set that up).
+//
+// As a result these are just defined here with "hopefully helpful" names. The
+// symbols won't ever be needed or show up unless these functions are called,
+// and hopefully when they're called the errors are self-explanatory enough to
+// figure out what's going on.
+
+extern {
+ #[link_name = "Math_acos"]
+ pub fn acos(n: f64) -> f64;
+ #[link_name = "Math_asin"]
+ pub fn asin(n: f64) -> f64;
+ #[link_name = "Math_atan"]
+ pub fn atan(n: f64) -> f64;
+ #[link_name = "Math_atan2"]
+ pub fn atan2(a: f64, b: f64) -> f64;
+ #[link_name = "Math_cbrt"]
+ pub fn cbrt(n: f64) -> f64;
+ #[link_name = "Math_cosh"]
+ pub fn cosh(n: f64) -> f64;
+ #[link_name = "Math_expm1"]
+ pub fn expm1(n: f64) -> f64;
+ pub fn fdim(a: f64, b: f64) -> f64;
+ #[link_name = "Math_log1p"]
+ pub fn log1p(n: f64) -> f64;
+ #[link_name = "Math_sinh"]
+ pub fn sinh(n: f64) -> f64;
+ #[link_name = "Math_tan"]
+ pub fn tan(n: f64) -> f64;
+ #[link_name = "Math_tanh"]
+ pub fn tanh(n: f64) -> f64;
+ #[link_name = "Math_hypot"]
+ pub fn hypot(x: f64, y: f64) -> f64;
+}
diff --git a/ctr-std/src/sys/wasm/condvar.rs b/ctr-std/src/sys/wasm/condvar.rs
new file mode 100644
index 0000000..afa7afe
--- /dev/null
+++ b/ctr-std/src/sys/wasm/condvar.rs
@@ -0,0 +1,43 @@
+// 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.
+
+use sys::mutex::Mutex;
+use time::Duration;
+
+pub struct Condvar { }
+
+impl Condvar {
+ pub const fn new() -> Condvar {
+ Condvar { }
+ }
+
+ #[inline]
+ pub unsafe fn init(&mut self) {}
+
+ #[inline]
+ pub unsafe fn notify_one(&self) {
+ }
+
+ #[inline]
+ pub unsafe fn notify_all(&self) {
+ }
+
+ pub unsafe fn wait(&self, _mutex: &Mutex) {
+ panic!("can't block with web assembly")
+ }
+
+ pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool {
+ panic!("can't block with web assembly");
+ }
+
+ #[inline]
+ pub unsafe fn destroy(&self) {
+ }
+}
diff --git a/ctr-std/src/sys/wasm/env.rs b/ctr-std/src/sys/wasm/env.rs
new file mode 100644
index 0000000..1422042
--- /dev/null
+++ b/ctr-std/src/sys/wasm/env.rs
@@ -0,0 +1,19 @@
+// 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.
+
+pub mod os {
+ pub const FAMILY: &'static str = "";
+ pub const OS: &'static str = "";
+ pub const DLL_PREFIX: &'static str = "";
+ pub const DLL_SUFFIX: &'static str = ".wasm";
+ pub const DLL_EXTENSION: &'static str = "wasm";
+ pub const EXE_SUFFIX: &'static str = ".wasm";
+ pub const EXE_EXTENSION: &'static str = "wasm";
+}
diff --git a/ctr-std/src/sys/wasm/fs.rs b/ctr-std/src/sys/wasm/fs.rs
new file mode 100644
index 0000000..b3c70a6
--- /dev/null
+++ b/ctr-std/src/sys/wasm/fs.rs
@@ -0,0 +1,304 @@
+// 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.
+
+use ffi::OsString;
+use fmt;
+use hash::{Hash, Hasher};
+use io::{self, SeekFrom};
+use path::{Path, PathBuf};
+use sys::time::SystemTime;
+use sys::{unsupported, Void};
+
+pub struct File(Void);
+
+pub struct FileAttr(Void);
+
+pub struct ReadDir(Void);
+
+pub struct DirEntry(Void);
+
+#[derive(Clone, Debug)]
+pub struct OpenOptions { }
+
+pub struct FilePermissions(Void);
+
+pub struct FileType(Void);
+
+#[derive(Debug)]
+pub struct DirBuilder { }
+
+impl FileAttr {
+ pub fn size(&self) -> u64 {
+ match self.0 {}
+ }
+
+ pub fn perm(&self) -> FilePermissions {
+ match self.0 {}
+ }
+
+ pub fn file_type(&self) -> FileType {
+ match self.0 {}
+ }
+
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ match self.0 {}
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ match self.0 {}
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ match self.0 {}
+ }
+}
+
+impl Clone for FileAttr {
+ fn clone(&self) -> FileAttr {
+ match self.0 {}
+ }
+}
+
+impl FilePermissions {
+ pub fn readonly(&self) -> bool {
+ match self.0 {}
+ }
+
+ pub fn set_readonly(&mut self, _readonly: bool) {
+ match self.0 {}
+ }
+}
+
+impl Clone for FilePermissions {
+ fn clone(&self) -> FilePermissions {
+ match self.0 {}
+ }
+}
+
+impl PartialEq for FilePermissions {
+ fn eq(&self, _other: &FilePermissions) -> bool {
+ match self.0 {}
+ }
+}
+
+impl Eq for FilePermissions {
+}
+
+impl fmt::Debug for FilePermissions {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+impl FileType {
+ pub fn is_dir(&self) -> bool {
+ match self.0 {}
+ }
+
+ pub fn is_file(&self) -> bool {
+ match self.0 {}
+ }
+
+ pub fn is_symlink(&self) -> bool {
+ match self.0 {}
+ }
+}
+
+impl Clone for FileType {
+ fn clone(&self) -> FileType {
+ match self.0 {}
+ }
+}
+
+impl Copy for FileType {}
+
+impl PartialEq for FileType {
+ fn eq(&self, _other: &FileType) -> bool {
+ match self.0 {}
+ }
+}
+
+impl Eq for FileType {
+}
+
+impl Hash for FileType {
+ fn hash<H: Hasher>(&self, _h: &mut H) {
+ match self.0 {}
+ }
+}
+
+impl fmt::Debug for FileType {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+impl fmt::Debug for ReadDir {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+impl Iterator for ReadDir {
+ type Item = io::Result<DirEntry>;
+
+ fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ match self.0 {}
+ }
+}
+
+impl DirEntry {
+ pub fn path(&self) -> PathBuf {
+ match self.0 {}
+ }
+
+ pub fn file_name(&self) -> OsString {
+ match self.0 {}
+ }
+
+ pub fn metadata(&self) -> io::Result<FileAttr> {
+ match self.0 {}
+ }
+
+ pub fn file_type(&self) -> io::Result<FileType> {
+ match self.0 {}
+ }
+}
+
+impl OpenOptions {
+ pub fn new() -> OpenOptions {
+ OpenOptions { }
+ }
+
+ pub fn read(&mut self, _read: bool) { }
+ pub fn write(&mut self, _write: bool) { }
+ pub fn append(&mut self, _append: bool) { }
+ pub fn truncate(&mut self, _truncate: bool) { }
+ pub fn create(&mut self, _create: bool) { }
+ pub fn create_new(&mut self, _create_new: bool) { }
+}
+
+impl File {
+ pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result<File> {
+ unsupported()
+ }
+
+ pub fn file_attr(&self) -> io::Result<FileAttr> {
+ match self.0 {}
+ }
+
+ pub fn fsync(&self) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn datasync(&self) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn truncate(&self, _size: u64) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
+ match self.0 {}
+ }
+
+ pub fn duplicate(&self) -> io::Result<File> {
+ match self.0 {}
+ }
+
+ pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn diverge(&self) -> ! {
+ match self.0 {}
+ }
+}
+
+impl DirBuilder {
+ pub fn new() -> DirBuilder {
+ DirBuilder { }
+ }
+
+ pub fn mkdir(&self, _p: &Path) -> io::Result<()> {
+ unsupported()
+ }
+}
+
+impl fmt::Debug for File {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
+ unsupported()
+}
+
+pub fn unlink(_p: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
+ match perm.0 {}
+}
+
+pub fn rmdir(_p: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn symlink(_src: &Path, _dst: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn stat(_p: &Path) -> io::Result<FileAttr> {
+ unsupported()
+}
+
+pub fn lstat(_p: &Path) -> io::Result<FileAttr> {
+ unsupported()
+}
+
+pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn copy(_from: &Path, _to: &Path) -> io::Result<u64> {
+ unsupported()
+}
diff --git a/ctr-std/src/sys/wasm/memchr.rs b/ctr-std/src/sys/wasm/memchr.rs
new file mode 100644
index 0000000..964e359
--- /dev/null
+++ b/ctr-std/src/sys/wasm/memchr.rs
@@ -0,0 +1,11 @@
+// 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.
+
+pub use core::slice::memchr::{memchr, memrchr};
diff --git a/ctr-std/src/sys/wasm/mod.rs b/ctr-std/src/sys/wasm/mod.rs
new file mode 100644
index 0000000..c02e5e8
--- /dev/null
+++ b/ctr-std/src/sys/wasm/mod.rs
@@ -0,0 +1,314 @@
+// 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.
+
+//! System bindings for the wasm/web platform
+//!
+//! This module contains the facade (aka platform-specific) implementations of
+//! OS level functionality for wasm. Note that this wasm is *not* the emscripten
+//! wasm, so we have no runtime here.
+//!
+//! This is all super highly experimental and not actually intended for
+//! wide/production use yet, it's still all in the experimental category. This
+//! will likely change over time.
+//!
+//! Currently all functions here are basically stubs that immediately return
+//! errors. The hope is that with a portability lint we can turn actually just
+//! remove all this and just omit parts of the standard library if we're
+//! compiling for wasm. That way it's a compile time error for something that's
+//! guaranteed to be a runtime error!
+
+use io;
+use os::raw::c_char;
+use ptr;
+use sys::os_str::Buf;
+use sys_common::{AsInner, FromInner};
+use ffi::{OsString, OsStr};
+use time::Duration;
+
+pub mod args;
+#[cfg(feature = "backtrace")]
+pub mod backtrace;
+pub mod cmath;
+pub mod condvar;
+pub mod env;
+pub mod fs;
+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 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 unsupported<T>() -> io::Result<T> {
+ Err(unsupported_err())
+}
+
+pub fn unsupported_err() -> io::Error {
+ io::Error::new(io::ErrorKind::Other,
+ "operation not supported on wasm yet")
+}
+
+pub fn decode_error_kind(_code: i32) -> io::ErrorKind {
+ io::ErrorKind::Other
+}
+
+// This enum is used as the storage for a bunch of types which can't actually
+// exist.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub enum Void {}
+
+pub unsafe fn strlen(mut s: *const c_char) -> usize {
+ let mut n = 0;
+ while *s != 0 {
+ n += 1;
+ s = s.offset(1);
+ }
+ return n
+}
+
+pub unsafe fn abort_internal() -> ! {
+ ExitSysCall::perform(1)
+}
+
+// We don't have randomness yet, but I totally used a random number generator to
+// generate these numbers.
+//
+// More seriously though this is just for DOS protection in hash maps. It's ok
+// if we don't do that on wasm just yet.
+pub fn hashmap_random_keys() -> (u64, u64) {
+ (1, 2)
+}
+
+// Implement a minimal set of system calls to enable basic IO
+pub enum SysCallIndex {
+ Read = 0,
+ Write = 1,
+ Exit = 2,
+ Args = 3,
+ GetEnv = 4,
+ SetEnv = 5,
+ Time = 6,
+}
+
+#[repr(C)]
+pub struct ReadSysCall {
+ fd: usize,
+ ptr: *mut u8,
+ len: usize,
+ result: usize,
+}
+
+impl ReadSysCall {
+ pub fn perform(fd: usize, buffer: &mut [u8]) -> usize {
+ let mut call_record = ReadSysCall {
+ fd,
+ len: buffer.len(),
+ ptr: buffer.as_mut_ptr(),
+ result: 0
+ };
+ if unsafe { syscall(SysCallIndex::Read, &mut call_record) } {
+ call_record.result
+ } else {
+ 0
+ }
+ }
+}
+
+#[repr(C)]
+pub struct WriteSysCall {
+ fd: usize,
+ ptr: *const u8,
+ len: usize,
+}
+
+impl WriteSysCall {
+ pub fn perform(fd: usize, buffer: &[u8]) {
+ let mut call_record = WriteSysCall {
+ fd,
+ len: buffer.len(),
+ ptr: buffer.as_ptr()
+ };
+ unsafe { syscall(SysCallIndex::Write, &mut call_record); }
+ }
+}
+
+#[repr(C)]
+pub struct ExitSysCall {
+ code: usize,
+}
+
+impl ExitSysCall {
+ pub fn perform(code: usize) -> ! {
+ let mut call_record = ExitSysCall {
+ code
+ };
+ unsafe {
+ syscall(SysCallIndex::Exit, &mut call_record);
+ ::intrinsics::abort();
+ }
+ }
+}
+
+fn receive_buffer<E, F: FnMut(&mut [u8]) -> Result<usize, E>>(estimate: usize, mut f: F)
+ -> Result<Vec<u8>, E>
+{
+ let mut buffer = vec![0; estimate];
+ loop {
+ let result = f(&mut buffer)?;
+ if result <= buffer.len() {
+ buffer.truncate(result);
+ break;
+ }
+ buffer.resize(result, 0);
+ }
+ Ok(buffer)
+}
+
+#[repr(C)]
+pub struct ArgsSysCall {
+ ptr: *mut u8,
+ len: usize,
+ result: usize
+}
+
+impl ArgsSysCall {
+ pub fn perform() -> Vec<OsString> {
+ receive_buffer(1024, |buffer| -> Result<usize, !> {
+ let mut call_record = ArgsSysCall {
+ len: buffer.len(),
+ ptr: buffer.as_mut_ptr(),
+ result: 0
+ };
+ if unsafe { syscall(SysCallIndex::Args, &mut call_record) } {
+ Ok(call_record.result)
+ } else {
+ Ok(0)
+ }
+ })
+ .unwrap()
+ .split(|b| *b == 0)
+ .map(|s| FromInner::from_inner(Buf { inner: s.to_owned() }))
+ .collect()
+ }
+}
+
+#[repr(C)]
+pub struct GetEnvSysCall {
+ key_ptr: *const u8,
+ key_len: usize,
+ value_ptr: *mut u8,
+ value_len: usize,
+ result: usize
+}
+
+impl GetEnvSysCall {
+ pub fn perform(key: &OsStr) -> Option<OsString> {
+ let key_buf = &AsInner::as_inner(key).inner;
+ receive_buffer(64, |buffer| {
+ let mut call_record = GetEnvSysCall {
+ key_len: key_buf.len(),
+ key_ptr: key_buf.as_ptr(),
+ value_len: buffer.len(),
+ value_ptr: buffer.as_mut_ptr(),
+ result: !0usize
+ };
+ if unsafe { syscall(SysCallIndex::GetEnv, &mut call_record) } {
+ if call_record.result == !0usize {
+ Err(())
+ } else {
+ Ok(call_record.result)
+ }
+ } else {
+ Err(())
+ }
+ }).ok().map(|s| {
+ FromInner::from_inner(Buf { inner: s })
+ })
+ }
+}
+
+#[repr(C)]
+pub struct SetEnvSysCall {
+ key_ptr: *const u8,
+ key_len: usize,
+ value_ptr: *const u8,
+ value_len: usize
+}
+
+impl SetEnvSysCall {
+ pub fn perform(key: &OsStr, value: Option<&OsStr>) {
+ let key_buf = &AsInner::as_inner(key).inner;
+ let value_buf = value.map(|v| &AsInner::as_inner(v).inner);
+ let mut call_record = SetEnvSysCall {
+ key_len: key_buf.len(),
+ key_ptr: key_buf.as_ptr(),
+ value_len: value_buf.map(|v| v.len()).unwrap_or(!0usize),
+ value_ptr: value_buf.map(|v| v.as_ptr()).unwrap_or(ptr::null())
+ };
+ unsafe { syscall(SysCallIndex::SetEnv, &mut call_record); }
+ }
+}
+
+pub enum TimeClock {
+ Monotonic = 0,
+ System = 1,
+}
+
+#[repr(C)]
+pub struct TimeSysCall {
+ clock: usize,
+ secs_hi: usize,
+ secs_lo: usize,
+ nanos: usize
+}
+
+impl TimeSysCall {
+ pub fn perform(clock: TimeClock) -> Duration {
+ let mut call_record = TimeSysCall {
+ clock: clock as usize,
+ secs_hi: 0,
+ secs_lo: 0,
+ nanos: 0
+ };
+ if unsafe { syscall(SysCallIndex::Time, &mut call_record) } {
+ Duration::new(
+ ((call_record.secs_hi as u64) << 32) | (call_record.secs_lo as u64),
+ call_record.nanos as u32
+ )
+ } else {
+ panic!("Time system call is not implemented by WebAssembly host");
+ }
+ }
+}
+
+unsafe fn syscall<T>(index: SysCallIndex, data: &mut T) -> bool {
+ #[cfg(feature = "wasm_syscall")]
+ extern {
+ #[no_mangle]
+ fn rust_wasm_syscall(index: usize, data: *mut Void) -> usize;
+ }
+
+ #[cfg(not(feature = "wasm_syscall"))]
+ unsafe fn rust_wasm_syscall(_index: usize, _data: *mut Void) -> usize { 0 }
+
+ rust_wasm_syscall(index as usize, data as *mut T as *mut Void) != 0
+}
diff --git a/ctr-std/src/sys/wasm/mutex.rs b/ctr-std/src/sys/wasm/mutex.rs
new file mode 100644
index 0000000..4197bdc
--- /dev/null
+++ b/ctr-std/src/sys/wasm/mutex.rs
@@ -0,0 +1,79 @@
+// 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.
+
+use cell::UnsafeCell;
+
+pub struct Mutex {
+ locked: UnsafeCell<bool>,
+}
+
+unsafe impl Send for Mutex {}
+unsafe impl Sync for Mutex {} // no threads on wasm
+
+impl Mutex {
+ pub const fn new() -> Mutex {
+ Mutex { locked: UnsafeCell::new(false) }
+ }
+
+ #[inline]
+ pub unsafe fn init(&mut self) {
+ }
+
+ #[inline]
+ pub unsafe fn lock(&self) {
+ let locked = self.locked.get();
+ assert!(!*locked, "cannot recursively acquire mutex");
+ *locked = true;
+ }
+
+ #[inline]
+ pub unsafe fn unlock(&self) {
+ *self.locked.get() = false;
+ }
+
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ let locked = self.locked.get();
+ if *locked {
+ false
+ } else {
+ *locked = true;
+ true
+ }
+ }
+
+ #[inline]
+ pub unsafe fn destroy(&self) {
+ }
+}
+
+// All empty stubs because wasm has no threads yet, so lock acquisition always
+// succeeds.
+pub struct ReentrantMutex {
+}
+
+impl ReentrantMutex {
+ pub unsafe fn uninitialized() -> ReentrantMutex {
+ ReentrantMutex { }
+ }
+
+ pub unsafe fn init(&mut self) {}
+
+ pub unsafe fn lock(&self) {}
+
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ true
+ }
+
+ pub unsafe fn unlock(&self) {}
+
+ pub unsafe fn destroy(&self) {}
+}
diff --git a/ctr-std/src/sys/wasm/net.rs b/ctr-std/src/sys/wasm/net.rs
new file mode 100644
index 0000000..e7476ab
--- /dev/null
+++ b/ctr-std/src/sys/wasm/net.rs
@@ -0,0 +1,337 @@
+// 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.
+
+use fmt;
+use io;
+use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr};
+use time::Duration;
+use sys::{unsupported, Void};
+
+pub struct TcpStream(Void);
+
+impl TcpStream {
+ pub fn connect(_: &SocketAddr) -> io::Result<TcpStream> {
+ unsupported()
+ }
+
+ pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
+ unsupported()
+ }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ match self.0 {}
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ match self.0 {}
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn write(&self, _: &[u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ match self.0 {}
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ match self.0 {}
+ }
+
+ pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpStream> {
+ match self.0 {}
+ }
+
+ pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ match self.0 {}
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ match self.0 {}
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ match self.0 {}
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+}
+
+impl fmt::Debug for TcpStream {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+pub struct TcpListener(Void);
+
+impl TcpListener {
+ pub fn bind(_: &SocketAddr) -> io::Result<TcpListener> {
+ unsupported()
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ match self.0 {}
+ }
+
+ pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+ match self.0 {}
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpListener> {
+ match self.0 {}
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ match self.0 {}
+ }
+
+ pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn only_v6(&self) -> io::Result<bool> {
+ match self.0 {}
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ match self.0 {}
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+}
+
+impl fmt::Debug for TcpListener {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+pub struct UdpSocket(Void);
+
+impl UdpSocket {
+ pub fn bind(_: &SocketAddr) -> io::Result<UdpSocket> {
+ unsupported()
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ match self.0 {}
+ }
+
+ pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ match self.0 {}
+ }
+
+ pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ match self.0 {}
+ }
+
+ pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn duplicate(&self) -> io::Result<UdpSocket> {
+ match self.0 {}
+ }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ match self.0 {}
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ match self.0 {}
+ }
+
+ pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn broadcast(&self) -> io::Result<bool> {
+ match self.0 {}
+ }
+
+ pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn multicast_loop_v4(&self) -> io::Result<bool> {
+ match self.0 {}
+ }
+
+ pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
+ match self.0 {}
+ }
+
+ pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn multicast_loop_v6(&self) -> io::Result<bool> {
+ match self.0 {}
+ }
+
+ pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr)
+ -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32)
+ -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr)
+ -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32)
+ -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ match self.0 {}
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ match self.0 {}
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ match self.0 {}
+ }
+
+ pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn send(&self, _: &[u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn connect(&self, _: &SocketAddr) -> io::Result<()> {
+ match self.0 {}
+ }
+}
+
+impl fmt::Debug for UdpSocket {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {}
+ }
+}
+
+pub struct LookupHost(Void);
+
+impl Iterator for LookupHost {
+ type Item = SocketAddr;
+ fn next(&mut self) -> Option<SocketAddr> {
+ match self.0 {}
+ }
+}
+
+pub fn lookup_host(_: &str) -> io::Result<LookupHost> {
+ unsupported()
+}
+
+#[allow(bad_style)]
+pub mod netc {
+ pub const AF_INET: u8 = 0;
+ pub const AF_INET6: u8 = 1;
+ pub type sa_family_t = u8;
+
+ #[derive(Copy, Clone)]
+ pub struct in_addr {
+ pub s_addr: u32,
+ }
+
+ #[derive(Copy, Clone)]
+ pub struct sockaddr_in {
+ pub sin_family: sa_family_t,
+ pub sin_port: u16,
+ pub sin_addr: in_addr,
+ }
+
+ #[derive(Copy, Clone)]
+ pub struct in6_addr {
+ pub s6_addr: [u8; 16],
+ }
+
+ #[derive(Copy, Clone)]
+ pub struct sockaddr_in6 {
+ pub sin6_family: sa_family_t,
+ pub sin6_port: u16,
+ pub sin6_addr: in6_addr,
+ pub sin6_flowinfo: u32,
+ pub sin6_scope_id: u32,
+ }
+
+ #[derive(Copy, Clone)]
+ pub struct sockaddr {
+ }
+
+ pub type socklen_t = usize;
+}
diff --git a/ctr-std/src/sys/wasm/os.rs b/ctr-std/src/sys/wasm/os.rs
new file mode 100644
index 0000000..23ca175
--- /dev/null
+++ b/ctr-std/src/sys/wasm/os.rs
@@ -0,0 +1,112 @@
+// 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.
+
+use error::Error as StdError;
+use ffi::{OsString, OsStr};
+use fmt;
+use io;
+use path::{self, PathBuf};
+use str;
+use sys::{unsupported, Void, ExitSysCall, GetEnvSysCall, SetEnvSysCall};
+
+pub fn errno() -> i32 {
+ 0
+}
+
+pub fn error_string(_errno: i32) -> String {
+ format!("operation successful")
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn chdir(_: &path::Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub struct SplitPaths<'a>(&'a Void);
+
+pub fn split_paths(_unparsed: &OsStr) -> SplitPaths {
+ panic!("unsupported")
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+ type Item = PathBuf;
+ fn next(&mut self) -> Option<PathBuf> {
+ match *self.0 {}
+ }
+}
+
+#[derive(Debug)]
+pub struct JoinPathsError;
+
+pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
+ where I: Iterator<Item=T>, T: AsRef<OsStr>
+{
+ Err(JoinPathsError)
+}
+
+impl fmt::Display for JoinPathsError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ "not supported on wasm yet".fmt(f)
+ }
+}
+
+impl StdError for JoinPathsError {
+ fn description(&self) -> &str {
+ "not supported on wasm yet"
+ }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub struct Env(Void);
+
+impl Iterator for Env {
+ type Item = (OsString, OsString);
+ fn next(&mut self) -> Option<(OsString, OsString)> {
+ match self.0 {}
+ }
+}
+
+pub fn env() -> Env {
+ panic!("not supported on web assembly")
+}
+
+pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
+ Ok(GetEnvSysCall::perform(k))
+}
+
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+ Ok(SetEnvSysCall::perform(k, Some(v)))
+}
+
+pub fn unsetenv(k: &OsStr) -> io::Result<()> {
+ Ok(SetEnvSysCall::perform(k, None))
+}
+
+pub fn temp_dir() -> PathBuf {
+ panic!("no filesystem on wasm")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+ None
+}
+
+pub fn exit(_code: i32) -> ! {
+ ExitSysCall::perform(_code as isize as usize)
+}
+
+pub fn getpid() -> u32 {
+ panic!("no pids on wasm")
+}
diff --git a/ctr-std/src/sys/wasm/os_str.rs b/ctr-std/src/sys/wasm/os_str.rs
new file mode 100644
index 0000000..e0da5bd
--- /dev/null
+++ b/ctr-std/src/sys/wasm/os_str.rs
@@ -0,0 +1,189 @@
+// 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.
+
+/// The underlying OsString/OsStr implementation on Unix systems: just
+/// a `Vec<u8>`/`[u8]`.
+
+use borrow::Cow;
+use fmt;
+use str;
+use mem;
+use rc::Rc;
+use sync::Arc;
+use sys_common::{AsInner, IntoInner};
+use sys_common::bytestring::debug_fmt_bytestring;
+use core::str::lossy::Utf8Lossy;
+
+#[derive(Clone, Hash)]
+pub struct Buf {
+ pub inner: Vec<u8>
+}
+
+pub struct Slice {
+ pub inner: [u8]
+}
+
+impl fmt::Debug for Slice {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ debug_fmt_bytestring(&self.inner, formatter)
+ }
+}
+
+impl fmt::Display for Slice {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter)
+ }
+}
+
+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)
+ }
+}
+
+impl IntoInner<Vec<u8>> for Buf {
+ fn into_inner(self) -> Vec<u8> {
+ self.inner
+ }
+}
+
+impl AsInner<[u8]> for Buf {
+ fn as_inner(&self) -> &[u8] {
+ &self.inner
+ }
+}
+
+
+impl Buf {
+ pub fn from_string(s: String) -> Buf {
+ Buf { inner: s.into_bytes() }
+ }
+
+ #[inline]
+ pub fn with_capacity(capacity: usize) -> Buf {
+ Buf {
+ inner: Vec::with_capacity(capacity)
+ }
+ }
+
+ #[inline]
+ pub fn clear(&mut self) {
+ self.inner.clear()
+ }
+
+ #[inline]
+ pub fn capacity(&self) -> usize {
+ self.inner.capacity()
+ }
+
+ #[inline]
+ pub fn reserve(&mut self, additional: usize) {
+ self.inner.reserve(additional)
+ }
+
+ #[inline]
+ pub fn reserve_exact(&mut self, additional: usize) {
+ self.inner.reserve_exact(additional)
+ }
+
+ #[inline]
+ 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)
+ }
+
+ pub fn as_slice(&self) -> &Slice {
+ unsafe { mem::transmute(&*self.inner) }
+ }
+
+ pub fn into_string(self) -> Result<String, Buf> {
+ String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } )
+ }
+
+ pub fn push_slice(&mut self, s: &Slice) {
+ self.inner.extend_from_slice(&s.inner)
+ }
+
+ #[inline]
+ pub fn into_box(self) -> Box<Slice> {
+ unsafe { mem::transmute(self.inner.into_boxed_slice()) }
+ }
+
+ #[inline]
+ pub fn from_box(boxed: Box<Slice>) -> Buf {
+ let inner: Box<[u8]> = unsafe { mem::transmute(boxed) };
+ Buf { inner: inner.into_vec() }
+ }
+
+ #[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 {
+ fn from_u8_slice(s: &[u8]) -> &Slice {
+ unsafe { mem::transmute(s) }
+ }
+
+ pub fn from_str(s: &str) -> &Slice {
+ Slice::from_u8_slice(s.as_bytes())
+ }
+
+ pub fn to_str(&self) -> Option<&str> {
+ str::from_utf8(&self.inner).ok()
+ }
+
+ pub fn to_string_lossy(&self) -> Cow<str> {
+ String::from_utf8_lossy(&self.inner)
+ }
+
+ pub fn to_owned(&self) -> Buf {
+ Buf { inner: self.inner.to_vec() }
+ }
+
+ #[inline]
+ pub fn into_box(&self) -> Box<Slice> {
+ let boxed: Box<[u8]> = self.inner.into();
+ unsafe { mem::transmute(boxed) }
+ }
+
+ pub fn empty_box() -> Box<Slice> {
+ let boxed: Box<[u8]> = Default::default();
+ unsafe { mem::transmute(boxed) }
+ }
+
+ #[inline]
+ pub fn into_arc(&self) -> Arc<Slice> {
+ let arc: Arc<[u8]> = Arc::from(&self.inner);
+ unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) }
+ }
+
+ #[inline]
+ pub fn into_rc(&self) -> Rc<Slice> {
+ let rc: Rc<[u8]> = Rc::from(&self.inner);
+ unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) }
+ }
+}
diff --git a/ctr-std/src/sys/wasm/path.rs b/ctr-std/src/sys/wasm/path.rs
new file mode 100644
index 0000000..395b8c1
--- /dev/null
+++ b/ctr-std/src/sys/wasm/path.rs
@@ -0,0 +1,29 @@
+// 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.
+
+use path::Prefix;
+use ffi::OsStr;
+
+#[inline]
+pub fn is_sep_byte(b: u8) -> bool {
+ b == b'/'
+}
+
+#[inline]
+pub fn is_verbatim_sep(b: u8) -> bool {
+ b == b'/'
+}
+
+pub fn parse_prefix(_: &OsStr) -> Option<Prefix> {
+ None
+}
+
+pub const MAIN_SEP_STR: &'static str = "/";
+pub const MAIN_SEP: char = '/';
diff --git a/ctr-std/src/sys/wasm/pipe.rs b/ctr-std/src/sys/wasm/pipe.rs
new file mode 100644
index 0000000..992e1ac
--- /dev/null
+++ b/ctr-std/src/sys/wasm/pipe.rs
@@ -0,0 +1,35 @@
+// 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.
+
+use io;
+use sys::Void;
+
+pub struct AnonPipe(Void);
+
+impl AnonPipe {
+ pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
+ match self.0 {}
+ }
+
+ pub fn diverge(&self) -> ! {
+ match self.0 {}
+ }
+}
+
+pub fn read2(p1: AnonPipe,
+ _v1: &mut Vec<u8>,
+ _p2: AnonPipe,
+ _v2: &mut Vec<u8>) -> io::Result<()> {
+ match p1.0 {}
+}
diff --git a/ctr-std/src/sys/unix/process.rs b/ctr-std/src/sys/wasm/process.rs
index 06dafc6..433e9ce 100644
--- a/ctr-std/src/sys/unix/process.rs
+++ b/ctr-std/src/sys/wasm/process.rs
@@ -16,11 +16,6 @@ use sys::pipe::AnonPipe;
use sys::{unsupported, Void};
use sys_common::process::{CommandEnv, DefaultEnvKey};
-use libc::c_int;
-
-const EXIT_SUCCESS: c_int = 0;
-const EXIT_FAILURE: c_int = 1;
-
////////////////////////////////////////////////////////////////////////////////
// Command
////////////////////////////////////////////////////////////////////////////////
@@ -82,9 +77,8 @@ impl From<AnonPipe> for Stdio {
}
impl From<File> for Stdio {
- fn from(_file: File) -> Stdio {
- //file.diverge()
- unimplemented!()
+ fn from(file: File) -> Stdio {
+ file.diverge()
}
}
@@ -135,6 +129,18 @@ impl fmt::Display for ExitStatus {
}
}
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitCode(bool);
+
+impl ExitCode {
+ pub const SUCCESS: ExitCode = ExitCode(false);
+ pub const FAILURE: ExitCode = ExitCode(true);
+
+ pub fn as_i32(&self) -> i32 {
+ self.0 as i32
+ }
+}
+
pub struct Process(Void);
impl Process {
@@ -154,16 +160,3 @@ impl Process {
match self.0 {}
}
}
-
-#[derive(PartialEq, Eq, Clone, Copy, Debug)]
-pub struct ExitCode(u8);
-
-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
- }
-}
diff --git a/ctr-std/src/sys/wasm/rwlock.rs b/ctr-std/src/sys/wasm/rwlock.rs
new file mode 100644
index 0000000..6516010
--- /dev/null
+++ b/ctr-std/src/sys/wasm/rwlock.rs
@@ -0,0 +1,82 @@
+// 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.
+
+use cell::UnsafeCell;
+
+pub struct RWLock {
+ mode: UnsafeCell<isize>,
+}
+
+unsafe impl Send for RWLock {}
+unsafe impl Sync for RWLock {} // no threads on wasm
+
+impl RWLock {
+ pub const fn new() -> RWLock {
+ RWLock {
+ mode: UnsafeCell::new(0),
+ }
+ }
+
+ #[inline]
+ pub unsafe fn read(&self) {
+ let mode = self.mode.get();
+ if *mode >= 0 {
+ *mode += 1;
+ } else {
+ rtabort!("rwlock locked for writing");
+ }
+ }
+
+ #[inline]
+ pub unsafe fn try_read(&self) -> bool {
+ let mode = self.mode.get();
+ if *mode >= 0 {
+ *mode += 1;
+ true
+ } else {
+ false
+ }
+ }
+
+ #[inline]
+ pub unsafe fn write(&self) {
+ let mode = self.mode.get();
+ if *mode == 0 {
+ *mode = -1;
+ } else {
+ rtabort!("rwlock locked for reading")
+ }
+ }
+
+ #[inline]
+ pub unsafe fn try_write(&self) -> bool {
+ let mode = self.mode.get();
+ if *mode == 0 {
+ *mode = -1;
+ true
+ } else {
+ false
+ }
+ }
+
+ #[inline]
+ pub unsafe fn read_unlock(&self) {
+ *self.mode.get() -= 1;
+ }
+
+ #[inline]
+ pub unsafe fn write_unlock(&self) {
+ *self.mode.get() += 1;
+ }
+
+ #[inline]
+ pub unsafe fn destroy(&self) {
+ }
+}
diff --git a/ctr-std/src/sys/wasm/stack_overflow.rs b/ctr-std/src/sys/wasm/stack_overflow.rs
new file mode 100644
index 0000000..bed2741
--- /dev/null
+++ b/ctr-std/src/sys/wasm/stack_overflow.rs
@@ -0,0 +1,23 @@
+// 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.
+
+pub struct Handler;
+
+impl Handler {
+ pub unsafe fn new() -> Handler {
+ Handler
+ }
+}
+
+pub unsafe fn init() {
+}
+
+pub unsafe fn cleanup() {
+}
diff --git a/ctr-std/src/sys/wasm/stdio.rs b/ctr-std/src/sys/wasm/stdio.rs
new file mode 100644
index 0000000..023f295
--- /dev/null
+++ b/ctr-std/src/sys/wasm/stdio.rs
@@ -0,0 +1,75 @@
+// 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.
+
+use io;
+use sys::{ReadSysCall, WriteSysCall};
+
+pub struct Stdin;
+pub struct Stdout;
+pub struct Stderr;
+
+impl Stdin {
+ pub fn new() -> io::Result<Stdin> {
+ Ok(Stdin)
+ }
+
+ pub fn read(&self, data: &mut [u8]) -> io::Result<usize> {
+ Ok(ReadSysCall::perform(0, data))
+ }
+}
+
+impl Stdout {
+ pub fn new() -> io::Result<Stdout> {
+ Ok(Stdout)
+ }
+
+ pub fn write(&self, data: &[u8]) -> io::Result<usize> {
+ WriteSysCall::perform(1, data);
+ Ok(data.len())
+ }
+
+ 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> {
+ WriteSysCall::perform(2, data);
+ Ok(data.len())
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl io::Write for Stderr {
+ fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+ (&*self).write(data)
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ (&*self).flush()
+ }
+}
+
+pub const STDIN_BUF_SIZE: usize = 0;
+
+pub fn is_ebadf(_err: &io::Error) -> bool {
+ true
+}
+
+pub fn stderr_prints_nothing() -> bool {
+ !cfg!(feature = "wasm_syscall")
+}
diff --git a/ctr-std/src/sys/wasm/thread.rs b/ctr-std/src/sys/wasm/thread.rs
new file mode 100644
index 0000000..728e678
--- /dev/null
+++ b/ctr-std/src/sys/wasm/thread.rs
@@ -0,0 +1,50 @@
+// 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.
+
+use boxed::FnBox;
+use ffi::CStr;
+use io;
+use sys::{unsupported, Void};
+use time::Duration;
+
+pub struct Thread(Void);
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
+
+impl Thread {
+ pub unsafe fn new<'a>(_stack: usize, _p: Box<FnBox() + 'a>)
+ -> io::Result<Thread>
+ {
+ unsupported()
+ }
+
+ pub fn yield_now() {
+ // do nothing
+ }
+
+ pub fn set_name(_name: &CStr) {
+ // nope
+ }
+
+ pub fn sleep(_dur: Duration) {
+ panic!("can't sleep");
+ }
+
+ pub fn join(self) {
+ match self.0 {}
+ }
+}
+
+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/wasm/thread_local.rs b/ctr-std/src/sys/wasm/thread_local.rs
new file mode 100644
index 0000000..442dd33
--- /dev/null
+++ b/ctr-std/src/sys/wasm/thread_local.rs
@@ -0,0 +1,50 @@
+// 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.
+
+use boxed::Box;
+use ptr;
+
+pub type Key = usize;
+
+struct Allocated {
+ value: *mut u8,
+ dtor: Option<unsafe extern fn(*mut u8)>,
+}
+
+#[inline]
+pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
+ Box::into_raw(Box::new(Allocated {
+ value: ptr::null_mut(),
+ dtor,
+ })) as usize
+}
+
+#[inline]
+pub unsafe fn set(key: Key, value: *mut u8) {
+ (*(key as *mut Allocated)).value = value;
+}
+
+#[inline]
+pub unsafe fn get(key: Key) -> *mut u8 {
+ (*(key as *mut Allocated)).value
+}
+
+#[inline]
+pub unsafe fn destroy(key: Key) {
+ let key = Box::from_raw(key as *mut Allocated);
+ if let Some(f) = key.dtor {
+ f(key.value);
+ }
+}
+
+#[inline]
+pub fn requires_synchronized_create() -> bool {
+ false
+}
diff --git a/ctr-std/src/sys/wasm/time.rs b/ctr-std/src/sys/wasm/time.rs
new file mode 100644
index 0000000..e52435e
--- /dev/null
+++ b/ctr-std/src/sys/wasm/time.rs
@@ -0,0 +1,57 @@
+// 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.
+
+use time::Duration;
+use sys::{TimeSysCall, TimeClock};
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct Instant(Duration);
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct SystemTime(Duration);
+
+pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
+
+impl Instant {
+ pub fn now() -> Instant {
+ Instant(TimeSysCall::perform(TimeClock::Monotonic))
+ }
+
+ pub fn sub_instant(&self, other: &Instant) -> Duration {
+ self.0 - other.0
+ }
+
+ pub fn add_duration(&self, other: &Duration) -> Instant {
+ Instant(self.0 + *other)
+ }
+
+ pub fn sub_duration(&self, other: &Duration) -> Instant {
+ Instant(self.0 - *other)
+ }
+}
+
+impl SystemTime {
+ pub fn now() -> SystemTime {
+ SystemTime(TimeSysCall::perform(TimeClock::System))
+ }
+
+ pub fn sub_time(&self, other: &SystemTime)
+ -> Result<Duration, Duration> {
+ self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0)
+ }
+
+ pub fn add_duration(&self, other: &Duration) -> SystemTime {
+ SystemTime(self.0 + *other)
+ }
+
+ pub fn sub_duration(&self, other: &Duration) -> SystemTime {
+ SystemTime(self.0 - *other)
+ }
+}
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
+ }
+}
diff --git a/ctr-std/src/sys_common/at_exit_imp.rs b/ctr-std/src/sys_common/at_exit_imp.rs
index f511981..26da51c 100644
--- a/ctr-std/src/sys_common/at_exit_imp.rs
+++ b/ctr-std/src/sys_common/at_exit_imp.rs
@@ -12,7 +12,7 @@
//!
//! Documentation can be found on the `rt::at_exit` function.
-use alloc_crate::boxed::FnBox;
+use boxed::FnBox;
use ptr;
use sys_common::mutex::Mutex;
diff --git a/ctr-std/src/sys_common/backtrace.rs b/ctr-std/src/sys_common/backtrace.rs
index 36cbce2..20109d2 100644
--- a/ctr-std/src/sys_common/backtrace.rs
+++ b/ctr-std/src/sys_common/backtrace.rs
@@ -41,6 +41,8 @@ pub struct Frame {
pub exact_position: *const u8,
/// Address of the enclosing function.
pub symbol_addr: *const u8,
+ /// Which inlined function is this frame referring to
+ pub inline_context: u32,
}
/// Max number of frames to print.
@@ -64,6 +66,7 @@ fn _print(w: &mut Write, format: PrintFormat) -> io::Result<()> {
let mut frames = [Frame {
exact_position: ptr::null(),
symbol_addr: ptr::null(),
+ inline_context: 0,
}; MAX_NB_FRAMES];
let (nb_frames, context) = unwind_backtrace(&mut frames)?;
let (skipped_before, skipped_after) =
@@ -133,13 +136,13 @@ pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
f()
}
-/// Controls how the backtrace should be formated.
+/// Controls how the backtrace should be formatted.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PrintFormat {
- /// Show all the frames with absolute path for files.
- Full = 2,
/// Show only relevant data from the backtrace.
- Short = 3,
+ Short = 2,
+ /// Show all the frames with absolute path for files.
+ Full = 3,
}
// For now logging is turned off by default, and this function checks to see
@@ -147,11 +150,10 @@ pub enum PrintFormat {
pub fn log_enabled() -> Option<PrintFormat> {
static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
match ENABLED.load(Ordering::SeqCst) {
- 0 => {},
+ 0 => {}
1 => return None,
- 2 => return Some(PrintFormat::Full),
- 3 => return Some(PrintFormat::Short),
- _ => unreachable!(),
+ 2 => return Some(PrintFormat::Short),
+ _ => return Some(PrintFormat::Full),
}
let val = match env::var_os("RUST_BACKTRACE") {
diff --git a/ctr-std/src/sys_common/mod.rs b/ctr-std/src/sys_common/mod.rs
index 27504d3..d0c4d6a 100644
--- a/ctr-std/src/sys_common/mod.rs
+++ b/ctr-std/src/sys_common/mod.rs
@@ -28,6 +28,16 @@
use sync::Once;
use sys;
+macro_rules! rtabort {
+ ($($t:tt)*) => (::sys_common::util::abort(format_args!($($t)*)))
+}
+
+macro_rules! rtassert {
+ ($e:expr) => (if !$e {
+ rtabort!(concat!("assertion failed: ", stringify!($e)));
+ })
+}
+
pub mod at_exit_imp;
#[cfg(feature = "backtrace")]
pub mod backtrace;
@@ -101,10 +111,6 @@ pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> {
if at_exit_imp::push(Box::new(f)) {Ok(())} else {Err(())}
}
-macro_rules! rtabort {
- ($($t:tt)*) => (::sys_common::util::abort(format_args!($($t)*)))
-}
-
/// One-time runtime cleanup.
pub fn cleanup() {
static CLEANUP: Once = Once::new();
diff --git a/ctr-std/src/sys_common/net.rs b/ctr-std/src/sys_common/net.rs
index 19e201a..a0dbda0 100644
--- a/ctr-std/src/sys_common/net.rs
+++ b/ctr-std/src/sys_common/net.rs
@@ -150,7 +150,7 @@ pub fn lookup_host(host: &str) -> io::Result<LookupHost> {
hints.ai_socktype = c::SOCK_STREAM;
let mut res = ptr::null_mut();
unsafe {
- match cvt_gai(c::getaddrinfo(c_host.as_ptr() as *const _, ptr::null(), &hints, &mut res)) {
+ match cvt_gai(c::getaddrinfo(c_host.as_ptr() as *const u8, ptr::null(), &hints, &mut res)) {
Ok(_) => {
Ok(LookupHost { original: res, cur: res })
},
diff --git a/ctr-std/src/sys_common/poison.rs b/ctr-std/src/sys_common/poison.rs
index 934ac3e..e74c40a 100644
--- a/ctr-std/src/sys_common/poison.rs
+++ b/ctr-std/src/sys_common/poison.rs
@@ -98,7 +98,7 @@ pub struct PoisonError<T> {
}
/// An enumeration of possible errors associated with a [`TryLockResult`] which
-/// can occur while trying to aquire a lock, from the [`try_lock`] method on a
+/// can occur while trying to acquire a lock, from the [`try_lock`] method on a
/// [`Mutex`] or the [`try_read`] and [`try_write`] methods on an [`RwLock`].
///
/// [`Mutex`]: struct.Mutex.html
diff --git a/ctr-std/src/sys_common/process.rs b/ctr-std/src/sys_common/process.rs
index 0a64d78..ddf0ebe 100644
--- a/ctr-std/src/sys_common/process.rs
+++ b/ctr-std/src/sys_common/process.rs
@@ -14,7 +14,7 @@
use ffi::{OsStr, OsString};
use env;
use collections::BTreeMap;
-use alloc_crate::borrow::Borrow;
+use borrow::Borrow;
pub trait EnvKey:
From<OsString> + Into<OsString> +
@@ -47,6 +47,7 @@ impl EnvKey for DefaultEnvKey {}
#[derive(Clone, Debug)]
pub struct CommandEnv<K> {
clear: bool,
+ saw_path: bool,
vars: BTreeMap<K, Option<OsString>>
}
@@ -54,6 +55,7 @@ impl<K: EnvKey> Default for CommandEnv<K> {
fn default() -> Self {
CommandEnv {
clear: false,
+ saw_path: false,
vars: Default::default()
}
}
@@ -108,9 +110,11 @@ impl<K: EnvKey> CommandEnv<K> {
// The following functions build up changes
pub fn set(&mut self, key: &OsStr, value: &OsStr) {
+ self.maybe_saw_path(&key);
self.vars.insert(key.to_owned().into(), Some(value.to_owned()));
}
pub fn remove(&mut self, key: &OsStr) {
+ self.maybe_saw_path(&key);
if self.clear {
self.vars.remove(key);
} else {
@@ -121,4 +125,12 @@ impl<K: EnvKey> CommandEnv<K> {
self.clear = true;
self.vars.clear();
}
+ pub fn have_changed_path(&self) -> bool {
+ self.saw_path || self.clear
+ }
+ fn maybe_saw_path(&mut self, key: &OsStr) {
+ if !self.saw_path && key == "PATH" {
+ self.saw_path = true;
+ }
+ }
}
diff --git a/ctr-std/src/sys_common/remutex.rs b/ctr-std/src/sys_common/remutex.rs
index ce43ec6..022056f 100644
--- a/ctr-std/src/sys_common/remutex.rs
+++ b/ctr-std/src/sys_common/remutex.rs
@@ -41,7 +41,7 @@ unsafe impl<T: Send> Sync for ReentrantMutex<T> {}
/// because implementation of the trait would violate Rust’s reference aliasing
/// rules. Use interior mutability (usually `RefCell`) in order to mutate the
/// guarded data.
-#[must_use]
+#[must_use = "if unused the ReentrantMutex will immediately unlock"]
pub struct ReentrantMutexGuard<'a, T: 'a> {
// funny underscores due to how Deref currently works (it disregards field
// privacy).
diff --git a/ctr-std/src/sys_common/thread.rs b/ctr-std/src/sys_common/thread.rs
index cafa353..da6f58e 100644
--- a/ctr-std/src/sys_common/thread.rs
+++ b/ctr-std/src/sys_common/thread.rs
@@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use alloc_crate::boxed::FnBox;
+use boxed::FnBox;
use env;
use sync::atomic::{self, Ordering};
use sys::stack_overflow;
diff --git a/ctr-std/src/sys_common/thread_local.rs b/ctr-std/src/sys_common/thread_local.rs
index a4aa3d9..d0d6224 100644
--- a/ctr-std/src/sys_common/thread_local.rs
+++ b/ctr-std/src/sys_common/thread_local.rs
@@ -169,7 +169,7 @@ impl StaticKey {
self.key.store(key, Ordering::SeqCst);
}
INIT_LOCK.unlock();
- assert!(key != 0);
+ rtassert!(key != 0);
return key
}
@@ -190,7 +190,7 @@ impl StaticKey {
imp::destroy(key1);
key2
};
- assert!(key != 0);
+ rtassert!(key != 0);
match self.key.compare_and_swap(0, key as usize, Ordering::SeqCst) {
// The CAS succeeded, so we've created the actual key
0 => key as usize,
diff --git a/ctr-std/src/sys_common/util.rs b/ctr-std/src/sys_common/util.rs
index a391c7c..a373e98 100644
--- a/ctr-std/src/sys_common/util.rs
+++ b/ctr-std/src/sys_common/util.rs
@@ -10,10 +10,13 @@
use fmt;
use io::prelude::*;
-use sys::stdio::Stderr;
+use sys::stdio::{Stderr, stderr_prints_nothing};
use thread;
pub fn dumb_print(args: fmt::Arguments) {
+ if stderr_prints_nothing() {
+ return
+ }
let _ = Stderr::new().map(|mut stderr| stderr.write_fmt(args));
}
diff --git a/ctr-std/src/sys_common/wtf8.rs b/ctr-std/src/sys_common/wtf8.rs
index dda4e1b..14a2555 100644
--- a/ctr-std/src/sys_common/wtf8.rs
+++ b/ctr-std/src/sys_common/wtf8.rs
@@ -56,7 +56,7 @@ pub struct CodePoint {
/// Example: `U+1F4A9`
impl fmt::Debug for CodePoint {
#[inline]
- fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "U+{:04X}", self.value)
}
}
@@ -144,7 +144,7 @@ impl ops::DerefMut for Wtf8Buf {
/// Example: `"a\u{D800}"` for a string with code points [U+0061, U+D800]
impl fmt::Debug for Wtf8Buf {
#[inline]
- fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&**self, formatter)
}
}
@@ -876,21 +876,7 @@ impl Hash for Wtf8 {
}
impl Wtf8 {
- pub fn is_ascii(&self) -> bool {
- self.bytes.is_ascii()
- }
- pub fn to_ascii_uppercase(&self) -> Wtf8Buf {
- Wtf8Buf { bytes: self.bytes.to_ascii_uppercase() }
- }
- pub fn to_ascii_lowercase(&self) -> Wtf8Buf {
- Wtf8Buf { bytes: self.bytes.to_ascii_lowercase() }
- }
- pub fn eq_ignore_ascii_case(&self, other: &Wtf8) -> bool {
- self.bytes.eq_ignore_ascii_case(&other.bytes)
- }
-
pub fn make_ascii_uppercase(&mut self) { self.bytes.make_ascii_uppercase() }
- pub fn make_ascii_lowercase(&mut self) { self.bytes.make_ascii_lowercase() }
}
#[cfg(test)]
diff --git a/ctr-std/src/time.rs b/ctr-std/src/time.rs
index 1215302..90ab349 100644
--- a/ctr-std/src/time.rs
+++ b/ctr-std/src/time.rs
@@ -271,7 +271,6 @@ impl SystemTime {
/// # Examples
///
/// ```no_run
- /// #![feature(assoc_unix_epoch)]
/// use std::time::SystemTime;
///
/// match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
@@ -279,7 +278,7 @@ impl SystemTime {
/// Err(_) => panic!("SystemTime before UNIX EPOCH!"),
/// }
/// ```
- #[unstable(feature = "assoc_unix_epoch", issue = "49502")]
+ #[stable(feature = "assoc_unix_epoch", since = "1.28.0")]
pub const UNIX_EPOCH: SystemTime = UNIX_EPOCH;
/// Returns the system time corresponding to "now".